diff --git a/DEPS b/DEPS
index 7a367ba26..89a624bd 100644
--- a/DEPS
+++ b/DEPS
@@ -297,19 +297,19 @@
   # 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': 'a77024ee45e98c59af5bcd63c09e18997491b556',
+  'skia_revision': '0450b8d5a2a40aea9b31e1b467ee6c39f0c6f06d',
   # 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': '9b4769535998b6e3864b7154363159dc50541066',
+  'v8_revision': '562b2178254da2933bbe84c932ddbe861179b722',
   # 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': '0f5770deabf20bfa7622cc70d4f7eed5a9e02af1',
+  'angle_revision': '6ee6fac97f2a1c651439299cc1d76a2a70c961fe',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '708ca9579181a8096193f68ad6c412a5fd302d5b',
+  'swiftshader_revision': 'c61fc9aec16265d4d7363a0bdba0f24f7a59cbe0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'cgreUs4CTQV8qLilUQr2vPdpCfeQdtnh-xtYStQz9gwC',
+          'version': 'eXuajLr_13p-4rXI2VJ3D_5D4cVoe1lUm5W2jzjyykUC',
         },
       ],
       'dep_type': 'cipd',
@@ -873,7 +873,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'HWGL8sk_b9SwVwwSR5Gltmnr6sG5WxzuzT7AyJpRkNkC',
+          'version': '7BoYCZFP7DHtgBj2xVDbUP1Di5jxm-RHxJcVkEStmhEC',
         },
       ],
       'dep_type': 'cipd',
@@ -884,7 +884,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'EdjH-IqvBY46yCmx-PVDk3g_UvVAWHNjF-3ta5f6xfIC',
+          'version': 'kvnnJX6BYmOihN9rxvdVqbm9h_-LIoGi4sXa1EW04dMC',
         },
       ],
       'dep_type': 'cipd',
@@ -1018,7 +1018,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': '0WkAedh1tJB8lzisWJRT80UjpacKLltuV7NqP-0tx9gC',
+               'version': 'CvokX4c6dx7DwQ8VVMQ70CROzyJWg13oOq3feeuTzg8C',
           },
       ],
       'condition': 'checkout_android',
@@ -1729,10 +1729,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '44e4c8770158c505b03ee7feafa4859d083b0912',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '8a9afc652513fae1a913f3db1648e5adc54239b1',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'dd01507ff53bf61b357623209d52a65831ed7d4d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f4f22872d0d919ad716ae6bd4cd731b79c70936e',
+    Var('webrtc_git') + '/src.git' + '@' + '0cf140d720e15f5b9064190511ef51746a6efe84',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@02a7f1d33d15c99c96c370036e4c6dd77b787cb9',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6882dd54f1b6bd7da135a2d8912871ef51b56676',
     'condition': 'checkout_src_internal',
   },
 
@@ -1835,7 +1835,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'idmOLokpR8pqEKF3BZHQrq0Lya7OyyLJr0sbu0nSfKgC',
+        'version': 'xyX-aFWvcF7WKFzTFRsyzI7sSc8QFNJZN_gy5TwPiUUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1846,7 +1846,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'lnTOZ6SVH7iYQ0TTTKhtLtEZ_y_nvEF2yzE7qqVmTy8C',
+        'version': 'h7OE9fCrkNVorXo4tNlLAGOTvZor7e8nExX6Eoa-M2MC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 32461a5c..c60fcf2 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -266,6 +266,23 @@
 interface Event
     getter path                        # crbug.com/1277431
 
+# The Pop-Up API (crbug.com/1307772) is enabled by the fieldtrial_testing_config,
+# but only for desktop and Android platforms. It is not shipped to stable yet,
+# on any platform.
+interface Element : Node
+	  getter popUp
+	  method hidePopUp
+	  getter popUpHideTarget
+	  setter popUpShowTarget
+	  getter popUpShowTarget
+	  setter defaultOpen
+	  setter popUpToggleTarget
+	  getter popUpToggleTarget
+	  method showPopUp
+	  setter popUp
+	  getter defaultOpen
+	  setter popUpHideTarget
+
 # FedCM API is not implemented on WebView. crbug.com/1340252
 interface IdentityCredential : Credential
     getter token
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b6671e35f..c758099 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -523,6 +523,10 @@
     "frame_throttler/frame_throttling_controller.cc",
     "frame_throttler/frame_throttling_controller.h",
     "frame_throttler/frame_throttling_observer.h",
+    "glanceables/glanceables_controller.cc",
+    "glanceables/glanceables_controller.h",
+    "glanceables/glanceables_view.cc",
+    "glanceables/glanceables_view.h",
     "high_contrast/high_contrast_controller.cc",
     "high_contrast/high_contrast_controller.h",
     "highlighter/highlighter_controller.cc",
@@ -2635,6 +2639,8 @@
     "frame/caption_buttons/frame_size_button_unittest.cc",
     "frame/default_frame_header_unittest.cc",
     "frame/non_client_frame_view_ash_unittest.cc",
+    "glanceables/glanceables_controller_unittest.cc",
+    "glanceables/glanceables_view_unittest.cc",
     "highlighter/highlighter_controller_unittest.cc",
     "highlighter/highlighter_gesture_util_unittest.cc",
     "host/ash_window_tree_host_platform_unittest.cc",
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index efe4bab7..434b3a25 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -29,11 +29,16 @@
   ~AppListViewPixelRTLTest() override = default;
 
   void ShowAppListAndHideCursor() {
-    GetAppListTestHelper()->ShowAppList();
+    AppListTestHelper* test_helper = GetAppListTestHelper();
+    test_helper->ShowAppList();
 
-    // Hide the search box cursor to avoid the flakiness due to the blinking.
-    views::TextfieldTestApi(
-        GetAppListTestHelper()->GetBubbleSearchBoxView()->search_box())
+    // Use a fixed placeholder text instead of the one picked randomly to
+    // avoid the test flakiness.
+    test_helper->GetSearchBoxView()->UseFixedPlaceholderTextForTest();
+
+    // Hide the search box cursor to avoid the flakiness due to the
+    // blinking.
+    views::TextfieldTestApi(test_helper->GetBubbleSearchBoxView()->search_box())
         .SetCursorLayerOpacity(0.f);
   }
 
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index fccf3a0..1675fa1 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -109,13 +109,6 @@
   return trimmed_query.empty();
 }
 
-SearchBoxView::PlaceholderTextType SelectPlaceholderText() {
-  if (chromeos::features::IsCloudGamingDeviceEnabled()) {
-    return kGamingPlaceholders[rand() % std::size(kGamingPlaceholders)];
-  }
-  return kDefaultPlaceholders[rand() % std::size(kDefaultPlaceholders)];
-}
-
 std::u16string GetCategoryName(SearchResult* search_result) {
   switch (search_result->category()) {
     case ash::AppListSearchResultCategory::kApps:
@@ -1023,6 +1016,17 @@
     MaybeSetAutocompleteGhostText(std::u16string(), std::u16string());
 }
 
+SearchBoxView::PlaceholderTextType SearchBoxView::SelectPlaceholderText()
+    const {
+  if (use_fixed_placeholder_text_for_test_)
+    return kDefaultPlaceholders[0];
+
+  if (chromeos::features::IsCloudGamingDeviceEnabled())
+    return kGamingPlaceholders[rand() % std::size(kGamingPlaceholders)];
+
+  return kDefaultPlaceholders[rand() % std::size(kDefaultPlaceholders)];
+}
+
 void SearchBoxView::UpdateQuery(const std::u16string& new_query) {
   search_box()->SetText(new_query);
   ContentsChanged(search_box(), new_query);
@@ -1046,6 +1050,14 @@
       ax::mojom::Event::kActiveDescendantChanged, true);
 }
 
+void SearchBoxView::UseFixedPlaceholderTextForTest() {
+  if (use_fixed_placeholder_text_for_test_)
+    return;
+
+  use_fixed_placeholder_text_for_test_ = true;
+  UpdatePlaceholderTextAndAccessibleName();
+}
+
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
   DCHECK(result_selection_controller_);
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 836b270a..18f9ea7 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -153,6 +153,10 @@
   void SetA11yActiveDescendant(
       const absl::optional<int32_t>& active_descendant);
 
+  // Refreshes the placeholder text with a fixed one rather than the one picked
+  // up randomly
+  void UseFixedPlaceholderTextForTest();
+
   void set_contents_view(ContentsView* contents_view) {
     contents_view_ = contents_view;
   }
@@ -210,6 +214,9 @@
   // text to the autocomplete text and sets the text highlight.
   void SetAutocompleteText(const std::u16string& autocomplete_text);
 
+  // Returns the text shown in the text field when there is no text inputs.
+  SearchBoxView::PlaceholderTextType SelectPlaceholderText() const;
+
   // Overridden from views::TextfieldController:
   void OnBeforeUserAction(views::Textfield* sender) override;
   bool HandleKeyEvent(views::Textfield* sender,
@@ -286,6 +293,10 @@
   // that occur after a search has been initiated.
   base::TimeTicks user_initiated_model_update_time_;
 
+  // If true, `SelectPlaceholderText()` always returns a fixed placeholder text
+  // instead of the one picked randomly.
+  bool use_fixed_placeholder_text_for_test_ = false;
+
   base::ScopedObservation<SearchBoxModel, SearchBoxModelObserver>
       search_box_model_observer_{this};
 
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
index bf4dc137..d19a3e4 100644
--- a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
@@ -36,6 +36,7 @@
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/color/color_provider_manager.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/image_view.h"
@@ -521,8 +522,7 @@
   EXPECT_EQ(intro_label_color, text_primary_color);
 }
 
-// TODO(crbug.com/1352671): Flaky.
-TEST_F(AssistantOnboardingViewTest, DISABLED_DarkAndLightModeFlagOff) {
+TEST_F(AssistantOnboardingViewTest, DarkAndLightModeFlagOff) {
   // ProductivityLauncher uses DarkLightMode colors.
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
@@ -530,6 +530,9 @@
           chromeos::features::kDarkLightMode, features::kNotificationsRefresh,
           features::kProductivityLauncher});
 
+  // Reset ColorProvider's cache to reflect the flag value changes above.
+  ui::ColorProviderManager::Get().ResetColorProviderCache();
+
   ShowAssistantUi();
 
   EXPECT_EQ(greeting_label()->GetEnabledColor(), kTextColorPrimary);
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 32afb8f..95e4d21 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -791,6 +791,10 @@
 const base::Feature kFuseBoxDebug{"FuseBoxDebug",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable glanceables on login.
+const base::Feature kGlanceables{"Glanceables",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable GuestOS integration with the files app.
 const base::Feature kGuestOsFiles{"GuestOsFiles",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
@@ -2012,6 +2016,10 @@
   return base::FeatureList::IsEnabled(kGaiaReauthEndpoint);
 }
 
+bool AreGlanceablesEnabled() {
+  return base::FeatureList::IsEnabled(kGlanceables);
+}
+
 bool IsGuestOsFilesEnabled() {
   return base::FeatureList::IsEnabled(kGuestOsFiles);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 48fcd1d0..2604739 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -330,6 +330,7 @@
 extern const base::Feature kFullscreenAlertBubble;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFuseBox;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFuseBoxDebug;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kGlanceables;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kGuestOsFiles;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kGaiaReauthEndpoint;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kGamepadVibration;
@@ -757,6 +758,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFullscreenAfterUnlockAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFullscreenAlertBubbleEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGaiaReauthEndpointEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool AreGlanceablesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHibernateEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideArcMediaNotificationsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
diff --git a/ash/glanceables/glanceables_controller.cc b/ash/glanceables/glanceables_controller.cc
new file mode 100644
index 0000000..2fb8fc1
--- /dev/null
+++ b/ash/glanceables/glanceables_controller.cc
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/glanceables/glanceables_controller.h"
+
+#include <memory>
+
+#include "ash/glanceables/glanceables_view.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
+#include "base/logging.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace ash {
+
+GlanceablesController::GlanceablesController() = default;
+
+GlanceablesController::~GlanceablesController() = default;
+
+void GlanceablesController::CreateUi() {
+  widget_ = std::make_unique<views::Widget>();
+  views::Widget::InitParams params;
+  params.delegate = new views::WidgetDelegate;  // Takes ownership.
+  params.delegate->SetOwnedByWidget(true);
+  // Allow maximize so the glanceable container's FillLayoutManager can fill the
+  // screen with the widget. This is required even for fullscreen widgets.
+  params.delegate->SetCanMaximize(true);
+  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
+  params.name = "GlanceablesWidget";
+  params.show_state = ui::SHOW_STATE_FULLSCREEN;
+  // Create the glanceables widget on the primary display.
+  params.parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+                                      kShellWindowId_GlanceablesContainer);
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
+  widget_->Init(std::move(params));
+
+  view_ = widget_->SetContentsView(std::make_unique<GlanceablesView>());
+
+  widget_->Show();
+}
+
+void GlanceablesController::DestroyUi() {
+  widget_.reset();
+  view_ = nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/glanceables/glanceables_controller.h b/ash/glanceables/glanceables_controller.h
new file mode 100644
index 0000000..04a8e6f
--- /dev/null
+++ b/ash/glanceables/glanceables_controller.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_GLANCEABLES_GLANCEABLES_CONTROLLER_H_
+#define ASH_GLANCEABLES_GLANCEABLES_CONTROLLER_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+
+namespace views {
+class Widget;
+}  // namespace views
+
+namespace ash {
+
+class GlanceablesView;
+
+// Controls the "welcome back" glanceables screen shown on login.
+class ASH_EXPORT GlanceablesController {
+ public:
+  GlanceablesController();
+  GlanceablesController(const GlanceablesController&) = delete;
+  GlanceablesController& operator=(const GlanceablesController&) = delete;
+  ~GlanceablesController();
+
+  // Creates the glanceables widget and view.
+  void CreateUi();
+
+  // Destroys the glanceables widget and view.
+  void DestroyUi();
+
+  views::Widget* widget_for_test() { return widget_.get(); }
+  GlanceablesView* view_for_test() { return view_; }
+
+ private:
+  std::unique_ptr<views::Widget> widget_;
+  GlanceablesView* view_ = nullptr;
+};
+
+}  // namespace ash
+
+#endif  // ASH_GLANCEABLES_GLANCEABLES_CONTROLLER_H_
diff --git a/ash/glanceables/glanceables_controller_unittest.cc b/ash/glanceables/glanceables_controller_unittest.cc
new file mode 100644
index 0000000..fd415c9
--- /dev/null
+++ b/ash/glanceables/glanceables_controller_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/glanceables/glanceables_controller.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/glanceables/glanceables_view.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+
+namespace ash {
+namespace {
+
+// Use a "no session" test so the glanceables widget is not automatically
+// created at the start of the test.
+// TODO(crbug.com/1353119): Once glanceables are shown by code in the
+// chrome/browser/ash layer, switch this to AshTestBase.
+class GlanceablesControllerTest : public NoSessionAshTestBase {
+ protected:
+  base::test::ScopedFeatureList feature_list_{features::kGlanceables};
+};
+
+TEST_F(GlanceablesControllerTest, CreateUi) {
+  GlanceablesController* controller = Shell::Get()->glanceables_controller();
+  ASSERT_TRUE(controller);
+
+  controller->CreateUi();
+
+  // A fullscreen widget was created.
+  views::Widget* widget = controller->widget_for_test();
+  ASSERT_TRUE(widget);
+  EXPECT_TRUE(widget->IsFullscreen());
+
+  // The controller's view is the widget's contents view.
+  views::View* view = controller->view_for_test();
+  EXPECT_TRUE(view);
+  EXPECT_EQ(view, widget->GetContentsView());
+}
+
+TEST_F(GlanceablesControllerTest, DestroyUi) {
+  auto* controller = Shell::Get()->glanceables_controller();
+  ASSERT_TRUE(controller);
+
+  controller->CreateUi();
+  controller->DestroyUi();
+
+  EXPECT_FALSE(controller->widget_for_test());
+  EXPECT_FALSE(controller->view_for_test());
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/glanceables/glanceables_view.cc b/ash/glanceables/glanceables_view.cc
new file mode 100644
index 0000000..46b7d7f
--- /dev/null
+++ b/ash/glanceables/glanceables_view.cc
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/glanceables/glanceables_view.h"
+
+#include <memory>
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+GlanceablesView::GlanceablesView() {
+  views::BoxLayout* layout =
+      SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal));
+  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
+
+  welcome_label_ = AddChildView(std::make_unique<views::Label>());
+  welcome_label_->SetAutoColorReadabilityEnabled(false);
+  // TODO(crbug.com/1353119): Use color provider and move to OnThemeChanged().
+  welcome_label_->SetEnabledColor(SK_ColorWHITE);
+  // TODO(crbug.com/1353119): Localized text.
+  welcome_label_->SetText(u"Placeholder");
+}
+
+GlanceablesView::~GlanceablesView() = default;
+
+}  // namespace ash
diff --git a/ash/glanceables/glanceables_view.h b/ash/glanceables/glanceables_view.h
new file mode 100644
index 0000000..edebd47d
--- /dev/null
+++ b/ash/glanceables/glanceables_view.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_GLANCEABLES_GLANCEABLES_VIEW_H_
+#define ASH_GLANCEABLES_GLANCEABLES_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Label;
+}  // namespace views
+
+namespace ash {
+
+// Container view for the "welcome back" glanceables screen shown on login.
+class ASH_EXPORT GlanceablesView : public views::View {
+ public:
+  GlanceablesView();
+  GlanceablesView(const GlanceablesView&) = delete;
+  GlanceablesView& operator=(const GlanceablesView&) = delete;
+  ~GlanceablesView() override;
+
+  views::Label* welcome_label_for_test() { return welcome_label_; }
+
+ private:
+  views::Label* welcome_label_ = nullptr;
+};
+
+}  // namespace ash
+
+#endif  // ASH_GLANCEABLES_GLANCEABLES_VIEW_H_
diff --git a/ash/glanceables/glanceables_view_unittest.cc b/ash/glanceables/glanceables_view_unittest.cc
new file mode 100644
index 0000000..0b3ad0c
--- /dev/null
+++ b/ash/glanceables/glanceables_view_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/glanceables/glanceables_view.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/glanceables/glanceables_controller.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+#include "ui/views/controls/label.h"
+
+namespace ash {
+namespace {
+
+// Use a "no session" test so the glanceables widget is not automatically
+// created at the start of the test.
+// TODO(crbug.com/1353119): Once glanceables are shown by code in the
+// chrome/browser/ash layer, switch this to AshTestBase.
+class GlanceablesViewTest : public NoSessionAshTestBase {
+ protected:
+  base::test::ScopedFeatureList feature_list_{features::kGlanceables};
+};
+
+TEST_F(GlanceablesViewTest, Basics) {
+  GlanceablesController* controller = Shell::Get()->glanceables_controller();
+  ASSERT_TRUE(controller);
+  controller->CreateUi();
+
+  GlanceablesView* view = controller->view_for_test();
+  ASSERT_TRUE(view);
+
+  // Welcome label was created.
+  views::Label* welcome_label = view->welcome_label_for_test();
+  ASSERT_TRUE(welcome_label);
+  EXPECT_FALSE(welcome_label->GetText().empty());
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 3fa40787..697f576 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -80,7 +80,6 @@
       case WindowStateType::kNormal:
       case WindowStateType::kMinimized:
       case WindowStateType::kInactive:
-      case WindowStateType::kAutoPositioned:
         active_window_state_type = ACTIVE_WINDOW_STATE_TYPE_OTHER;
         break;
     }
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h
index aec7b75..570a935 100644
--- a/ash/public/cpp/shell_window_ids.h
+++ b/ash/public/cpp/shell_window_ids.h
@@ -58,6 +58,9 @@
   // The wallpaper (desktop background) window.
   kShellWindowId_WallpaperContainer,
 
+  // The glanceables ("welcome back") window container.
+  kShellWindowId_GlanceablesContainer,
+
   // The containers for standard top-level windows per active desks.
   // * Notes:
   //   - There are no direct mapping between `kShellWindowId_DeskContainerA` and
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 06d97d5..ad63d7b 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -1086,6 +1086,15 @@
   wallpaper_container->SetLayoutManager(
       new FillLayoutManager(wallpaper_container));
 
+  if (features::AreGlanceablesEnabled()) {
+    aura::Window* glanceables_container =
+        CreateContainer(kShellWindowId_GlanceablesContainer,
+                        "GlanceablesContainer", magnified_container);
+    glanceables_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
+    glanceables_container->SetLayoutManager(
+        new FillLayoutManager(glanceables_container));  // Takes ownership.
+  }
+
   aura::Window* non_lock_screen_containers =
       CreateContainer(kShellWindowId_NonLockScreenContainersContainer,
                       "NonLockScreenContainersContainer", magnified_container);
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl.cc b/ash/services/device_sync/cryptauth_device_manager_impl.cc
index 88915a0..04a8731 100644
--- a/ash/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_manager_impl.cc
@@ -820,8 +820,8 @@
           "CryptAuth."
         trigger:
           "Once every day, or by API request. Periodic calls happen because "
-          "devides that do not re-enrolled for more than X days (currently 45) "
-          "are automatically removed from the server."
+          "devices that are not re-enrolled for more than X days (currently "
+          "45) are automatically removed from the server."
         data: "OAuth 2.0 token."
         destination: GOOGLE_OWNED_SERVICE
       }
diff --git a/ash/shell.cc b/ash/shell.cc
index 7db1488b..11f1b68a 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -33,6 +33,7 @@
 #include "ash/child_accounts/parent_access_controller_impl.h"
 #include "ash/clipboard/clipboard_history_controller_impl.h"
 #include "ash/clipboard/control_v_histogram_recorder.h"
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/controls/contextual_tooltip.h"
 #include "ash/dbus/ash_dbus_services.h"
@@ -66,6 +67,7 @@
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/frame/snap_controller_impl.h"
 #include "ash/frame_throttler/frame_throttling_controller.h"
+#include "ash/glanceables/glanceables_controller.h"
 #include "ash/high_contrast/high_contrast_controller.h"
 #include "ash/highlighter/highlighter_controller.h"
 #include "ash/host/ash_window_tree_host_init_params.h"
@@ -588,6 +590,10 @@
       native_cursor_manager_(nullptr) {
   AccelerometerReader::GetInstance()->Initialize();
 
+  if (features::AreGlanceablesEnabled()) {
+    glanceables_controller_ = std::make_unique<GlanceablesController>();
+  }
+
   login_screen_controller_ =
       std::make_unique<LoginScreenController>(system_tray_notifier_.get());
   display_manager_ = ScreenAsh::CreateDisplayManager();
@@ -713,6 +719,11 @@
   // Accelerometer file reader stops listening to tablet mode controller.
   AccelerometerReader::GetInstance()->StopListenToTabletModeController();
 
+  if (features::AreGlanceablesEnabled()) {
+    // Close all glanceables so that all widgets are destroyed.
+    glanceables_controller_->DestroyUi();
+  }
+
   // Destroy |ambient_controller_| before |assistant_controller_|.
   ambient_controller_.reset();
 
@@ -1596,6 +1607,12 @@
   // the session starts.
   app_list_feature_usage_metrics_ =
       std::make_unique<AppListFeatureUsageMetrics>();
+
+  if (features::AreGlanceablesEnabled()) {
+    // Show glanceables after signin.
+    // TODO(crbug.com/1353119): Show only when session restore would trigger.
+    glanceables_controller_->CreateUi();
+  }
 }
 
 void Shell::OnSessionStateChanged(session_manager::SessionState state) {
diff --git a/ash/shell.h b/ash/shell.h
index 51ca95d..559899f 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -132,6 +132,7 @@
 class FrameThrottlingController;
 class FullscreenMagnifierController;
 class GeolocationController;
+class GlanceablesController;
 class HighContrastController;
 class HighlighterController;
 class HoldingSpaceController;
@@ -455,6 +456,9 @@
   GeolocationController* geolocation_controller() {
     return geolocation_controller_.get();
   }
+  GlanceablesController* glanceables_controller() {
+    return glanceables_controller_.get();
+  }
   HighlighterController* highlighter_controller() {
     return highlighter_controller_.get();
   }
@@ -815,6 +819,7 @@
   std::unique_ptr<FocusCycler> focus_cycler_;
   std::unique_ptr<FloatController> float_controller_;
   std::unique_ptr<GeolocationController> geolocation_controller_;
+  std::unique_ptr<GlanceablesController> glanceables_controller_;
   std::unique_ptr<HoldingSpaceController> holding_space_controller_;
   std::unique_ptr<PowerPrefs> power_prefs_;
   std::unique_ptr<SnoopingProtectionController> snooping_protection_controller_;
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 1681d011..9ac9966 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -304,6 +304,12 @@
   return context_menu_runner_ && context_menu_runner_->IsRunning();
 }
 
+void TrayBackgroundView::SetRoundedCornerBehavior(
+    RoundedCornerBehavior corner_behavior) {
+  corner_behavior_ = corner_behavior;
+  UpdateBackground();
+}
+
 gfx::RoundedCornersF TrayBackgroundView::GetRoundedCorners() {
   const float radius = ShelfConfig::Get()->control_border_radius();
   if (shelf_->IsHorizontalAlignment()) {
@@ -314,6 +320,10 @@
                                         radius, radius,
                                         kUnifiedTrayNonRoundedSideRadius};
     switch (corner_behavior_) {
+      case kNotRounded:
+        return {
+            kUnifiedTrayNonRoundedSideRadius, kUnifiedTrayNonRoundedSideRadius,
+            kUnifiedTrayNonRoundedSideRadius, kUnifiedTrayNonRoundedSideRadius};
       case kAllRounded:
         return {radius, radius, radius, radius};
       case kStartRounded:
@@ -324,6 +334,10 @@
   }
 
   switch (corner_behavior_) {
+    case kNotRounded:
+      return {
+          kUnifiedTrayNonRoundedSideRadius, kUnifiedTrayNonRoundedSideRadius,
+          kUnifiedTrayNonRoundedSideRadius, kUnifiedTrayNonRoundedSideRadius};
     case kAllRounded:
       return {radius, radius, radius, radius};
     case kStartRounded:
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index f928a9f..4d0894f 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -43,7 +43,12 @@
  public:
   METADATA_HEADER(TrayBackgroundView);
 
-  enum RoundedCornerBehavior { kStartRounded, kEndRounded, kAllRounded };
+  enum RoundedCornerBehavior {
+    kNotRounded,
+    kStartRounded,
+    kEndRounded,
+    kAllRounded
+  };
 
   TrayBackgroundView(Shelf* shelf,
                      RoundedCornerBehavior corner_behavior = kAllRounded);
@@ -178,6 +183,9 @@
   // Returns true if the view is showing a context menu.
   bool IsShowingMenu() const;
 
+  // Set the rounded corner behavior for this tray item.
+  void SetRoundedCornerBehavior(RoundedCornerBehavior corner_behavior);
+
   // Returns the corners based on the `corner_behavior_`;
   gfx::RoundedCornersF GetRoundedCorners();
 
@@ -295,7 +303,7 @@
 
   // The shape of this tray which is only applied to the horizontal tray.
   // Defaults to `kAllRounded`.
-  const RoundedCornerBehavior corner_behavior_;
+  RoundedCornerBehavior corner_behavior_;
 
   std::unique_ptr<TrayWidgetObserver> widget_observer_;
   std::unique_ptr<TrayEventFilter> tray_event_filter_;
diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc
index 7805575..381a07d 100644
--- a/ash/tooltips/tooltip_controller_unittest.cc
+++ b/ash/tooltips/tooltip_controller_unittest.cc
@@ -171,23 +171,4 @@
   EXPECT_TRUE(helper_->IsTooltipVisible());
 }
 
-TEST_F(TooltipControllerTest, HideTooltipWhenViewHidden) {
-  std::unique_ptr<views::Widget> widget(CreateNewWidgetOn(0));
-  TooltipTestView* view = new TooltipTestView;
-  AddViewToWidgetAndResize(widget.get(), view);
-  view->set_tooltip_text(u"Tooltip Text");
-  EXPECT_EQ(std::u16string(), helper_->GetTooltipText());
-  EXPECT_EQ(nullptr, helper_->GetTooltipParentWindow());
-
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  generator.MoveMouseRelativeTo(widget->GetNativeView(),
-                                view->bounds().CenterPoint());
-
-  // Mouse event triggers tooltip update so it becomes visible.
-  EXPECT_TRUE(helper_->IsTooltipVisible());
-
-  view->SetVisible(false);
-  EXPECT_FALSE(helper_->IsTooltipVisible());
-}
-
 }  // namespace ash
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom
index 7f0d167..c734e5ff 100644
--- a/ash/webui/personalization_app/mojom/personalization_app.mojom
+++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -187,6 +187,9 @@
 
 // Receives updated wallpaper information whenever it is changed.
 interface WallpaperObserver {
+  // Triggered by |WallpaperControllerObserver::OnWallpaperPreviewEnded|.
+  OnWallpaperPreviewEnded();
+
   // Triggered by |WallpaperControllerObserver::OnWallpaperChanged|. Retrieves
   // updated wallpaper information and calls the observer. |image| will be null
   // if there is an error retrieving wallpaper information.
diff --git a/ash/webui/personalization_app/resources/css/base.css b/ash/webui/personalization_app/resources/css/base.css
index a3abaa8..6f964e47 100644
--- a/ash/webui/personalization_app/resources/css/base.css
+++ b/ash/webui/personalization_app/resources/css/base.css
@@ -6,7 +6,6 @@
 body {
     height: 100%;
     margin: 0;
-    overscroll-behavior: none;
 }
 
 body {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.html b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.html
index c00289e..9a3beb0e 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.html
@@ -2,7 +2,6 @@
   #container {
     background-color: transparent;
     height: 100%;
-    overscroll-behavior: none;
     pointer-events: auto;
     position: relative;
     width: 100%;
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.ts
index 15378151..50236ac 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_fullscreen_element.ts
@@ -13,7 +13,7 @@
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
 
-import {CurrentWallpaper, WallpaperLayout, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
+import {CurrentWallpaper, WallpaperLayout} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
 import {DisplayableImage} from './constants.js';
@@ -68,14 +68,20 @@
   private pendingSelected_: DisplayableImage|null = null;
   private selectedLayout_: WallpaperLayout|null = null;
 
-  private wallpaperProvider_: WallpaperProviderInterface;
+  private onVisibilityChange_ = () => {
+    if (document.visibilityState === 'hidden' && this.visible_) {
+      // Cancel preview immediately instead of waiting for fullscreenchange
+      // event.
+      cancelPreviewWallpaper(getWallpaperProvider());
+    }
+  };
 
-  constructor() {
-    super();
-    this.wallpaperProvider_ = getWallpaperProvider();
-  }
+  private onPopState_ = () => {
+    if (this.visible_) {
+      cancelPreviewWallpaper(getWallpaperProvider());
+    }
+  };
 
-  /** Add override when tsc is updated to 4.3+. */
   override connectedCallback() {
     super.connectedCallback();
     this.$.container.addEventListener(
@@ -95,15 +101,14 @@
 
     // Visibility change will fire in case of alt+tab, closing the window, or
     // anything else that exits out of full screen mode.
-    window.addEventListener('visibilitychange', () => {
-      if (document.visibilityState === 'hidden' &&
-          !!this.getFullscreenElement()) {
-        this.exitFullscreen();
-        // Cancel preview immediately instead of waiting for fullscreenchange
-        // event.
-        cancelPreviewWallpaper(this.wallpaperProvider_);
-      }
-    });
+    window.addEventListener('visibilitychange', this.onVisibilityChange_);
+    window.addEventListener('popstate', this.onPopState_);
+  }
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    window.removeEventListener('visibilitychange', this.onVisibilityChange_);
+    window.removeEventListener('popstate', this.onPopState_);
   }
 
   /** Wrapper function to mock out for testing. */
@@ -136,7 +141,7 @@
       // case, the preview mode may be still on so we have to call cancel
       // preview. This call is no-op when the user clicks on set as wallpaper
       // button.
-      cancelPreviewWallpaper(this.wallpaperProvider_);
+      cancelPreviewWallpaper(getWallpaperProvider());
       this.dispatch(setFullscreenEnabledAction(/*enabled=*/ false));
       document.body.classList.remove(fullscreenClass);
     } else {
@@ -146,14 +151,14 @@
 
   private async onClickExit_() {
     await this.exitFullscreen();
-    await cancelPreviewWallpaper(this.wallpaperProvider_);
+    await cancelPreviewWallpaper(getWallpaperProvider());
   }
 
   private async onClickConfirm_() {
     // Begin to exit fullscreen mode before confirming preview wallpaper. This
     // makes local images and online images execute updates in the same order.
     await this.exitFullscreen();
-    await confirmPreviewWallpaper(this.wallpaperProvider_);
+    await confirmPreviewWallpaper(getWallpaperProvider());
   }
 
   private async onClickLayout_(event: MouseEvent) {
@@ -164,8 +169,7 @@
     const layout = getWallpaperLayoutEnum(
         (event.currentTarget as HTMLButtonElement).dataset['layout']!);
     await selectWallpaper(
-        this.pendingSelected_, this.wallpaperProvider_, this.getStore(),
-        layout);
+        this.pendingSelected_, getWallpaperProvider(), this.getStore(), layout);
     this.selectedLayout_ = layout;
   }
 
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
index 78945e3..697bf6f8 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
@@ -5,7 +5,7 @@
 import {CurrentWallpaper, WallpaperObserverInterface, WallpaperObserverReceiver, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
-import {setSelectedImageAction, setUpdatedDailyRefreshImageAction} from './wallpaper_actions.js';
+import {setFullscreenEnabledAction, setSelectedImageAction, setUpdatedDailyRefreshImageAction} from './wallpaper_actions.js';
 import {getDailyRefreshState} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
@@ -49,6 +49,11 @@
   private receiver_: WallpaperObserverReceiver =
       initWallpaperObserver(getWallpaperProvider(), this);
 
+  onWallpaperPreviewEnded() {
+    const store = PersonalizationStore.getInstance();
+    store.dispatch(setFullscreenEnabledAction(false));
+  }
+
   onWallpaperChanged(currentWallpaper: CurrentWallpaper|null) {
     // Ignore updates while in fullscreen preview mode. The attribution
     // information is for the old (non-preview) wallpaper. This is because
diff --git a/ash/webui/shortcut_customization_ui/resources/accelerator_edit_dialog.js b/ash/webui/shortcut_customization_ui/resources/accelerator_edit_dialog.js
index 2ad0c25d..cc3c37f 100644
--- a/ash/webui/shortcut_customization_ui/resources/accelerator_edit_dialog.js
+++ b/ash/webui/shortcut_customization_ui/resources/accelerator_edit_dialog.js
@@ -24,10 +24,6 @@
     return 'accelerator-edit-dialog';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       description: {
@@ -151,6 +147,10 @@
   onRestoreDefaultButtonClicked_() {
     // TODO(jimmyxgong): Implement this function.
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AcceleratorEditDialogElement.is,
diff --git a/ash/webui/shortcut_customization_ui/resources/accelerator_edit_view.js b/ash/webui/shortcut_customization_ui/resources/accelerator_edit_view.js
index ca5e17a..ecdb39e 100644
--- a/ash/webui/shortcut_customization_ui/resources/accelerator_edit_view.js
+++ b/ash/webui/shortcut_customization_ui/resources/accelerator_edit_view.js
@@ -27,10 +27,6 @@
     return 'accelerator-edit-view';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       /** @type {!AcceleratorInfo} */
@@ -158,6 +154,10 @@
   computeIsAddView_() {
     return this.viewState === ViewState.ADD;
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AcceleratorEditViewElement.is,
diff --git a/ash/webui/shortcut_customization_ui/resources/accelerator_row.js b/ash/webui/shortcut_customization_ui/resources/accelerator_row.js
index c352cc2..68fbf112 100644
--- a/ash/webui/shortcut_customization_ui/resources/accelerator_row.js
+++ b/ash/webui/shortcut_customization_ui/resources/accelerator_row.js
@@ -26,10 +26,6 @@
     return 'accelerator-row';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       description: {
@@ -109,6 +105,10 @@
         },
         ));
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AcceleratorRowElement.is, AcceleratorRowElement);
\ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/resources/accelerator_subsection.js b/ash/webui/shortcut_customization_ui/resources/accelerator_subsection.js
index fca2c2b..4c4519b9 100644
--- a/ash/webui/shortcut_customization_ui/resources/accelerator_subsection.js
+++ b/ash/webui/shortcut_customization_ui/resources/accelerator_subsection.js
@@ -21,10 +21,6 @@
     return 'accelerator-subsection';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       title: {
@@ -116,6 +112,10 @@
     });
     this.acceleratorContainer = tempAccelContainer;
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AcceleratorSubsectionElement.is,
diff --git a/ash/webui/shortcut_customization_ui/resources/accelerator_view.js b/ash/webui/shortcut_customization_ui/resources/accelerator_view.js
index 8c9fad36..2899e20 100644
--- a/ash/webui/shortcut_customization_ui/resources/accelerator_view.js
+++ b/ash/webui/shortcut_customization_ui/resources/accelerator_view.js
@@ -75,10 +75,6 @@
     return 'accelerator-view';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       /** @type {!AcceleratorInfo} */
@@ -534,6 +530,10 @@
     // Always end input capturing if an update event was fired.
     this.endCapture_();
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AcceleratorViewElement.is, AcceleratorViewElement);
diff --git a/ash/webui/shortcut_customization_ui/resources/input_key.js b/ash/webui/shortcut_customization_ui/resources/input_key.js
index 61e5365c..e8fdee9 100644
--- a/ash/webui/shortcut_customization_ui/resources/input_key.js
+++ b/ash/webui/shortcut_customization_ui/resources/input_key.js
@@ -26,10 +26,6 @@
     return 'input-key';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       key: {
@@ -44,6 +40,10 @@
       },
     };
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(InputKeyElement.is, InputKeyElement);
\ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.js b/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.js
index baf7cb4..c6588e91 100644
--- a/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.js
+++ b/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.js
@@ -26,10 +26,6 @@
     return 'shortcut-customization-app';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       /** @private */
@@ -218,6 +214,10 @@
   closeRestoreAllDialog_() {
     this.showRestoreAllDialog_ = false;
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(
diff --git a/ash/webui/shortcut_customization_ui/resources/shortcuts_page.js b/ash/webui/shortcut_customization_ui/resources/shortcuts_page.js
index 1afdcc4..24394e2 100644
--- a/ash/webui/shortcut_customization_ui/resources/shortcuts_page.js
+++ b/ash/webui/shortcut_customization_ui/resources/shortcuts_page.js
@@ -23,10 +23,6 @@
     return 'shortcuts-page';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
       /**
@@ -84,6 +80,10 @@
       subsection.updateSubsection();
     }
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(ShortcutsPageElement.is, ShortcutsPageElement);
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc
index 86eb2b0..5e111e7 100644
--- a/ash/wm/default_state.cc
+++ b/ash/wm/default_state.cc
@@ -659,7 +659,6 @@
       break;
     }
     case WindowStateType::kInactive:
-    case WindowStateType::kAutoPositioned:
     case WindowStateType::kPip:
       return;
   }
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 873ea777..fdb9193 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -1245,7 +1245,6 @@
             break;
           case chromeos::WindowStateType::kInactive:
           case chromeos::WindowStateType::kFullscreen:
-          case chromeos::WindowStateType::kAutoPositioned:
           case chromeos::WindowStateType::kPinned:
           case chromeos::WindowStateType::kTrustedPinned:
           case chromeos::WindowStateType::kPip:
diff --git a/ash/wm/desks/templates/saved_desk_presenter.cc b/ash/wm/desks/templates/saved_desk_presenter.cc
index b3dd9c41..9255338 100644
--- a/ash/wm/desks/templates/saved_desk_presenter.cc
+++ b/ash/wm/desks/templates/saved_desk_presenter.cc
@@ -345,15 +345,6 @@
   }
 }
 
-void SavedDeskPresenter::GetAllEntries(const base::GUID& item_to_focus,
-                                       const std::u16string& saved_desk_name,
-                                       aura::Window* const root_window) {
-  weak_ptr_factory_.InvalidateWeakPtrs();
-  GetDeskModel()->GetAllEntries(base::BindOnce(
-      &SavedDeskPresenter::OnGetAllEntries, weak_ptr_factory_.GetWeakPtr(),
-      item_to_focus, saved_desk_name, root_window));
-}
-
 void SavedDeskPresenter::DeleteEntry(
     const std::string& uuid,
     absl::optional<DeskTemplateType> record_for_type) {
@@ -461,13 +452,12 @@
   RemoveUIEntries(uuids);
 }
 
-void SavedDeskPresenter::OnGetAllEntries(
-    const base::GUID& item_to_focus,
-    const std::u16string& saved_desk_name,
-    aura::Window* const root_window,
-    desks_storage::DeskModel::GetAllEntriesStatus status,
-    const std::vector<const DeskTemplate*>& entries) {
-  if (status != desks_storage::DeskModel::GetAllEntriesStatus::kOk)
+void SavedDeskPresenter::GetAllEntries(const base::GUID& item_to_focus,
+                                       const std::u16string& saved_desk_name,
+                                       aura::Window* const root_window) {
+  auto result = GetDeskModel()->GetAllEntries();
+
+  if (result.status != desks_storage::DeskModel::GetAllEntriesStatus::kOk)
     return;
 
   // This updates `should_show_templates_ui_`.
@@ -477,7 +467,7 @@
     // Populate `SavedDeskLibraryView` with the desk template entries.
     if (SavedDeskLibraryView* library_view =
             overview_grid->GetSavedDeskLibraryView()) {
-      library_view->PopulateGridUI(entries,
+      library_view->PopulateGridUI(result.entries,
                                    overview_grid->GetGridEffectiveBounds(),
                                    /*last_saved_desk_uuid=*/item_to_focus);
       SavedDeskItemView* item_view =
diff --git a/ash/wm/desks/templates/saved_desk_presenter.h b/ash/wm/desks/templates/saved_desk_presenter.h
index c9b6ab9..8de0918 100644
--- a/ash/wm/desks/templates/saved_desk_presenter.h
+++ b/ash/wm/desks/templates/saved_desk_presenter.h
@@ -96,14 +96,6 @@
  private:
   friend class SavedDeskPresenterTestApi;
 
-  // Callback ran after querying the model for a list of entries. This function
-  // also contains logic for updating the UI.
-  void OnGetAllEntries(const base::GUID& item_to_focus,
-                       const std::u16string& saved_desk_name,
-                       aura::Window* const root_window,
-                       desks_storage::DeskModel::GetAllEntriesStatus status,
-                       const std::vector<const DeskTemplate*>& entries);
-
   // Callback after deleting an entry. Will then call `RemoveUIEntries` to
   // update the UI by removing the deleted saved desk.
   void OnDeleteEntry(const std::string& uuid,
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index 656810f..8c0e5129 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -165,19 +165,10 @@
   // Gets the current list of template entries from the desk model directly
   // without updating the UI.
   const std::vector<const DeskTemplate*> GetAllEntries() {
-    std::vector<const DeskTemplate*> templates;
-
-    base::RunLoop loop;
-    desk_model()->GetAllEntries(base::BindLambdaForTesting(
-        [&](desks_storage::DeskModel::GetAllEntriesStatus status,
-            const std::vector<const DeskTemplate*>& entries) {
-          EXPECT_EQ(desks_storage::DeskModel::GetAllEntriesStatus::kOk, status);
-          templates = entries;
-          loop.Quit();
-        }));
-    loop.Run();
-
-    return templates;
+    auto result = desk_model()->GetAllEntries();
+    EXPECT_EQ(desks_storage::DeskModel::GetAllEntriesStatus::kOk,
+              result.status);
+    return result.entries;
   }
 
   // Deletes an entry to the desks model directly without interacting with the
@@ -325,11 +316,9 @@
   void OpenOverviewAndShowTemplatesGrid() {
     if (!GetOverviewSession()) {
       ToggleOverview();
-      WaitForDesksTemplatesUI();
     }
 
     ShowDesksTemplatesGrids();
-    WaitForDesksTemplatesUI();
     WaitForLibraryUI();
   }
 
@@ -366,7 +355,6 @@
   void OpenOverviewAndSaveTemplate(aura::Window* root) {
     if (!GetOverviewSession()) {
       ToggleOverview();
-      WaitForDesksTemplatesUI();
     }
 
     auto* save_template_button = GetSaveDeskAsTemplateButtonForRoot(root);
@@ -387,7 +375,6 @@
   void OpenOverviewAndSaveDeskForLater(aura::Window* root) {
     if (!GetOverviewSession()) {
       ToggleOverview();
-      WaitForDesksTemplatesUI();
     }
 
     auto* save_desk_button = GetSaveDeskForLaterButtonForRoot(root);
@@ -510,7 +497,6 @@
   // There are no entries initially, so the none of the desks templates buttons
   // are visible.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   verify_button_visibilities(/*zero_state_shown=*/false,
                              /*expanded_state_shown=*/false,
                              /*trace_string=*/"one-desk-zero-entries");
@@ -523,7 +509,6 @@
   // Reenter overview and verify the zero state desks templates buttons are
   // visible since there is one entry to view.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   verify_button_visibilities(/*zero_state_shown=*/true,
                              /*expanded_state_shown=*/false,
                              /*trace_string=*/"one-desk-one-entry");
@@ -542,7 +527,6 @@
   // Reenter overview and verify the expanded state desks templates buttons are
   // visible since there is one entry to view.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   verify_button_visibilities(/*zero_state_shown=*/false,
                              /*expanded_state_shown=*/true,
                              /*trace_string=*/"two-desk-one-entry");
@@ -553,7 +537,6 @@
 
   // Reenter overview and verify neither of the buttons are shown.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   verify_button_visibilities(/*zero_state_shown=*/false,
                              /*expanded_state_shown=*/false,
                              /*trace_string=*/"two-desk-zero-entries");
@@ -570,7 +553,6 @@
 
   // Start overview mode. The no windows widget should be visible.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   auto& grid_list = GetOverviewGridList();
   ASSERT_EQ(2u, grid_list.size());
   EXPECT_TRUE(grid_list[0]->no_windows_widget());
@@ -597,7 +579,6 @@
   // shown.
   test_window.reset();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   EXPECT_TRUE(GetOverviewGridList()[0]->no_windows_widget());
 
   // Open the desk templates grid. The no windows widget should now be hidden
@@ -635,7 +616,6 @@
 
   // Start overview mode.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   ASSERT_TRUE(GetOverviewSession());
 
   ASSERT_TRUE(GetOverviewController()->InOverviewSession());
@@ -676,7 +656,6 @@
 
   // Start overview mode.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // Alert for entering overview mode should be sent.
   EXPECT_EQ(AccessibilityAlert::WINDOW_OVERVIEW_MODE_ENTERED,
@@ -701,7 +680,6 @@
 
   // Start overview mode. The window is visible in the overview mode.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   ASSERT_TRUE(GetOverviewSession());
   EXPECT_EQ(1.0f, test_window->layer()->opacity());
 
@@ -743,7 +721,6 @@
 
   // Start overview mode. `test_window_2` should be visible in overview mode.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   ASSERT_TRUE(GetOverviewSession());
   EXPECT_EQ(1.0f, test_window_2->layer()->opacity());
   auto& overview_grid = GetOverviewGridList()[0];
@@ -752,7 +729,6 @@
 
   // Open the desk templates grid. This should hide `test_window_2`.
   ShowDesksTemplatesGrids();
-  WaitForDesksTemplatesUI();
   EXPECT_EQ(0.0f, test_window_2->layer()->opacity());
 
   // While in the desk templates grid, delete the active desk by clicking on the
@@ -1026,7 +1002,6 @@
 
     // Open overview and expect the button to be disabled.
     ToggleOverview();
-    WaitForDesksTemplatesUI();
     EXPECT_EQ(views::Button::STATE_DISABLED,
               GetSaveDeskAsTemplateButtonForRoot(root_window)->GetState());
 
@@ -1071,7 +1046,6 @@
 
     // Open overview and expect the button to be disabled.
     ToggleOverview();
-    WaitForDesksTemplatesUI();
     EXPECT_EQ(views::Button::STATE_DISABLED,
               GetSaveDeskForLaterButtonForRoot(root_window)->GetState());
 
@@ -1120,7 +1094,6 @@
 
   // Toggle overview and set the save desk buttons to disabled.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   auto* save_as_template_button =
       GetSaveDeskAsTemplateButtonForRoot(root_window);
   auto* save_for_later_button = GetSaveDeskForLaterButtonForRoot(root_window);
@@ -1156,7 +1129,6 @@
   // There are no saved template entries and one test window initially.
   auto test_window = CreateAppWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // The `save_desk_as_template_button` is visible when at least one window is
   // open.
@@ -1170,6 +1142,8 @@
   ClickOnView(save_desk_as_template_button);
   ASSERT_EQ(1ul, GetAllEntries().size());
 
+  WaitForDesksTemplatesUI();
+
   // Expect that the Desk Templates grid is visible.
   EXPECT_TRUE(GetOverviewGridList()[0]->IsShowingDesksTemplatesGrid());
 }
@@ -1868,7 +1842,6 @@
   // Test that the templates buttons are created but invisible. The save desk as
   // template button is not created.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   aura::Window* root = Shell::GetPrimaryRootWindow();
   auto* zero_state = GetDesksTemplatesButtonForRoot(root,
                                                     /*zero_state=*/true);
@@ -1891,7 +1864,6 @@
   // Test that on entering overview, the zero state desks templates button and
   // the save template button are visible.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   aura::Window* root = Shell::GetPrimaryRootWindow();
   auto* zero_state = GetDesksTemplatesButtonForRoot(root,
                                                     /*zero_state=*/true);
@@ -2005,7 +1977,6 @@
 TEST_F(SavedDeskTest, TabbingInvisibleTemplatesButton) {
   // First test the case there are no templates.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   auto* overview_grid = GetOverviewSession()->GetGridWithRootWindow(
       Shell::GetPrimaryRootWindow());
@@ -2098,7 +2069,6 @@
   // dialog should show up.
   auto* root = Shell::Get()->GetPrimaryRootWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   auto* save_template_button = GetSaveDeskAsTemplateButtonForRoot(root);
   ASSERT_TRUE(
       GetOverviewGridForRoot(root)->IsSaveDeskAsTemplateButtonVisible());
@@ -2388,7 +2358,6 @@
       AshColorProvider::ControlsLayerType::kFocusRingColor);
 
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   views::View* button = GetDesksTemplatesButtonForRoot(
       Shell::GetPrimaryRootWindow(), /*zero_state=*/false);
@@ -2781,7 +2750,6 @@
   auto window = CreateTestWindow(gfx::Rect(100, 100));
 
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   auto* focus_widget = views::Widget::GetWidgetForNativeWindow(
       GetOverviewSession()->GetOverviewFocusWindow());
@@ -3061,7 +3029,6 @@
   // There are no saved template entries and one test window initially.
   auto test_window = CreateAppWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // Record histogram
   base::HistogramTester histogram_tester;
@@ -3076,6 +3043,7 @@
 
   // Click on `save_desk_as_template_button`.
   ClickOnView(save_desk_as_template_button);
+  WaitForDesksTemplatesUI();
   ASSERT_EQ(1ul, GetAllEntries().size());
 
   // Expect that the Desk Templates grid is visible.
@@ -3110,7 +3078,6 @@
   // dialog should show up.
   auto* root = Shell::Get()->GetPrimaryRootWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   SaveDeskTemplateButton* save_template_button =
       GetSaveDeskAsTemplateButtonForRoot(root);
   ASSERT_TRUE(
@@ -3171,7 +3138,6 @@
   base::HistogramTester histogram_tester;
 
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // Mocks saving templates with some browsers.
   saved_desk_util::GetSavedDeskPresenter()->SaveOrUpdateDeskTemplate(
@@ -3198,7 +3164,6 @@
     // to save a template via the UI.
     if (!GetOverviewSession()) {
       ToggleOverview();
-      WaitForDesksTemplatesUI();
     }
 
     // The `save_desk_as_template_button` is visible when at least one window is
@@ -3211,6 +3176,7 @@
 
     // Click on `save_desk_as_template_button` button.
     ClickOnView(save_desk_as_template_button);
+    WaitForDesksTemplatesUI();
     ASSERT_EQ(num_templates + 1, GetAllEntries().size());
 
     // Expect that the Desk Templates grid is visible.
@@ -3682,7 +3648,6 @@
   // There are no saved template entries and one test window initially.
   auto test_window = CreateAppWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // The `save_desk_as_template_button` is visible when at least one window is
   // open.
@@ -3705,7 +3670,6 @@
   ToggleOverview();
   ASSERT_FALSE(InOverviewSession());
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   save_desk_as_template_button =
       GetSaveDeskAsTemplateButtonForRoot(Shell::GetPrimaryRootWindow());
@@ -3798,7 +3762,6 @@
 
   auto test_window = CreateAppWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // Click on `save_desk_as_template_button` button.
   SaveDeskTemplateButton* save_desk_as_template_button =
@@ -3828,7 +3791,6 @@
 
   // Enter overview and save the same desk again.
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   // The `save_desk_as_template_button` is visible when at least one window is
   // open.
   SaveDeskTemplateButton* save_desk_as_template_button =
@@ -3867,7 +3829,6 @@
 
   auto test_window = CreateAppWindow();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   // Click on `save_desk_as_template_button` button on the primary display.
   SaveDeskTemplateButton* save_desk_as_template_button =
@@ -3891,7 +3852,6 @@
   auto widget1 = CreateTestWidget();
 
   ToggleOverview();
-  WaitForDesksTemplatesUI();
 
   OverviewItem* item1 = GetOverviewItemForWindow(widget1->GetNativeWindow());
   ASSERT_TRUE(item1);
@@ -4096,7 +4056,6 @@
   // Create a window then save the current desk for later.
   CreateAppWindow().release();
   ToggleOverview();
-  WaitForDesksTemplatesUI();
   auto* save_desk_button =
       GetSaveDeskForLaterButtonForRoot(Shell::Get()->GetPrimaryRootWindow());
   ClickOnView(save_desk_button);
@@ -4316,8 +4275,9 @@
     ToggleOverview();
     ClickOnView(
         GetSaveDeskForLaterButtonForRoot(Shell::Get()->GetPrimaryRootWindow()));
-    for (int i = 0; i != 3; ++i)
-      WaitForDesksTemplatesUI();
+
+    WaitForDesksTemplatesUI();
+    WaitForDesksTemplatesUI();
 
     // Expect that the last added template item name view has focus, and verify
     // that we have a saved desk with the expected `name`.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 741cd65..149e8e99 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -217,7 +217,6 @@
     "base64.h",
     "base64url.cc",
     "base64url.h",
-    "base_export.h",
     "base_switches.h",
     "big_endian.cc",
     "big_endian.h",
@@ -369,7 +368,6 @@
     "hash/hash.h",
     "hash/legacy_hash.cc",
     "hash/legacy_hash.h",
-    "immediate_crash.h",
     "json/json_common.h",
     "json/json_file_value_serializer.cc",
     "json/json_file_value_serializer.h",
@@ -1522,6 +1520,10 @@
     "//third_party/abseil-cpp:absl",
   ]
 
+  if (use_custom_libcxx && !is_debug) {
+    public_deps += [ ":nodebug_assertion" ]
+  }
+
   # Needed for <atomic> if using newer C++ library than sysroot, except if
   # building inside the cros_sdk environment - use host_toolchain as a
   # more robust check for this.
@@ -2661,8 +2663,10 @@
 # base_static.
 static_library("base_static") {
   sources = [
+    "base_export.h",
     "base_switches.cc",
     "base_switches.h",
+    "immediate_crash.h",
   ]
 
   deps = [ "//build:chromeos_buildflags" ]
@@ -2688,6 +2692,23 @@
   }
 }
 
+if (use_custom_libcxx && !is_debug) {
+  #  nodebug_assertion.cc has to be in its own source_set instead of being
+  #  included as a source in //base to prevent the linker from removing its
+  #  contents during dead code elimination.
+  source_set("nodebug_assertion") {
+    defines = [ "BASE_IMPLEMENTATION" ]
+    sources = [ "nodebug_assertion.cc" ]
+    deps = [ ":base_static" ]
+    if (is_win && is_component_build) {
+      # For Windows component builds, we need to set __declspec(dllexport) on
+      # our redeclaration of __libcpp_verbose_abort(...) so it can be picked up.
+      # Therefore, we disable the corresponding error.
+      cflags = [ "-Wno-dll-attribute-on-redeclaration" ]
+    }
+  }
+}
+
 component("i18n") {
   output_name = "base_i18n"
   sources = [
@@ -3403,6 +3424,10 @@
     "vlog_unittest.cc",
   ]
 
+  if (use_custom_libcxx) {
+    sources += [ "libcpp_hardening_test.cc" ]
+  }
+
   # TODO(crbug.com/1304253): iOS test() targets don't support mixing Rust code
   # yet.
   if (!is_ios) {
diff --git a/base/allocator/dispatcher/dispatcher_unittest.cc b/base/allocator/dispatcher/dispatcher_unittest.cc
index c5cc153..2bcfe72b 100644
--- a/base/allocator/dispatcher/dispatcher_unittest.cc
+++ b/base/allocator/dispatcher/dispatcher_unittest.cc
@@ -119,16 +119,24 @@
 }
 #endif
 
-#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !BUILDFLAG(IS_IOS)
-// Don't enable this test when compiling for iOS. For some yet unknown reason
-// the test crashes on iOS due to invalid malloc zone. It is yet unclear whether
-// this is an incomplete setup or a bug.
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
 struct AllocatorShimAllocator {
   void* Alloc(size_t size) { return base::allocator::UncheckedAlloc(size); }
   void Free(void* data) { base::allocator::UncheckedFree(data); }
 };
 
-TEST_F(BaseAllocatorDispatcherTest, VerifyNotificationUsingAllocatorShim) {
+#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+// Disable the test when running on any of Apple's OSs without PartitionAlloc
+// being the default allocator. In this case, all allocations are routed to
+// MallocImpl, which then causes the test to terminate unexpectedly.
+#define MAYBE_VerifyNotificationUsingAllocatorShim \
+  DISABLED_VerifyNotificationUsingAllocatorShim
+#else
+#define MAYBE_VerifyNotificationUsingAllocatorShim \
+  VerifyNotificationUsingAllocatorShim
+#endif
+
+TEST_F(BaseAllocatorDispatcherTest, MAYBE_VerifyNotificationUsingAllocatorShim) {
   AllocatorShimAllocator allocator;
   DoBasicTest(allocator);
 }
diff --git a/base/libcpp_hardening_test.cc b/base/libcpp_hardening_test.cc
new file mode 100644
index 0000000..7ff04d14b
--- /dev/null
+++ b/base/libcpp_hardening_test.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+#if !_LIBCPP_ENABLE_ASSERTIONS
+#error \
+    "Define _LIBCPP_ENABLE_ASSERTIONS to 1 in \
+buildtools/third_party/libc++/__config_site"
+
+#endif
+
+using ::testing::ContainsRegex;
+using ::testing::Not;
+
+TEST(LibcppHardeningTest, Assertions) {
+  std::vector<int> vec = {0, 1, 2};
+#ifdef NDEBUG
+// We have to explicitly check for the GTEST_HAS_DEATH_TEST macro instead of
+// using EXPECT_DEATH_IF_SUPPORTED(...) for the following reasons:
+//
+// 0. EXPECT_DEATH(...) does not support (non-escaped) parentheses in the regex,
+//    so we can't use negative look arounds (https://stackoverflow.com/a/406408)
+//    to check that the error message doesn't exist.
+// 1. EXPECT_DEATH_IF_SUPPORTED(...) does not support having gmock matchers as
+//    the second argument if GTEST_HAS_DEATH_TEST is false.
+#if GTEST_HAS_DEATH_TEST
+  EXPECT_DEATH(vec[3], Not(ContainsRegex(".*assertion.*failed:")));
+#else
+  GTEST_UNSUPPORTED_DEATH_TEST(vec[3], "", );
+#endif  // GTEST_HAS_DEATH_TEST
+#else
+  EXPECT_DEATH_IF_SUPPORTED(vec[3], ".*assertion.*failed:");
+#endif  // NDEBUG
+}
+
+}  // namespace
diff --git a/base/nodebug_assertion.cc b/base/nodebug_assertion.cc
new file mode 100644
index 0000000..ebf6c2f
--- /dev/null
+++ b/base/nodebug_assertion.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <__assert>
+#include <__config>
+#include <__verbose_abort>
+
+#include "base/base_export.h"
+#include "base/immediate_crash.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+_LIBCPP_NORETURN BASE_EXPORT void __libcpp_verbose_abort(char const* format,
+                                                         ...) {
+  IMMEDIATE_CRASH();
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 96712ad..7675257 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
-#include "base/check_is_test.h"
 #include "base/compiler_specific.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/stack_trace.h"
@@ -250,11 +249,10 @@
   main_thread_only().queues_to_gracefully_shutdown.clear();
   main_thread_only().selector.SetTaskQueueSelectorObserver(nullptr);
 
-  // In some tests a NestingObserver may not have been registered.
+  // In the case of an early startup exits or in some tests a NestingObserver
+  // may not have been registered.
   if (main_thread_only().nesting_observer_registered_)
     controller_->RemoveNestingObserver(this);
-  else
-    CHECK_IS_TEST();
 
   // Let interested parties have one last shot at accessing this.
   for (auto& observer : main_thread_only().destruction_observers)
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 7fc0450ec..2aa326b7 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -86,7 +86,7 @@
   if (is_msan) {
     defines += [ "MEMORY_SANITIZER" ]
   }
-  if (is_ubsan || is_ubsan_null || is_ubsan_vptr || is_ubsan_security) {
+  if (is_ubsan || is_ubsan_vptr || is_ubsan_security) {
     defines += [ "UNDEFINED_SANITIZER" ]
   }
   if (is_official_build) {
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index fa20bd8..e1f23cd 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -172,9 +172,6 @@
     if (is_ubsan || is_ubsan_security) {
       ldflags += [ "-fsanitize=undefined" ]
     }
-    if (is_ubsan_null) {
-      ldflags += [ "-fsanitize=null" ]
-    }
     if (is_ubsan_vptr) {
       ldflags += [ "-fsanitize=vptr" ]
     }
@@ -486,12 +483,6 @@
   }
 }
 
-config("ubsan_null_flags") {
-  if (is_ubsan_null) {
-    cflags = [ "-fsanitize=null" ]
-  }
-}
-
 config("ubsan_vptr_flags") {
   if (is_ubsan_vptr) {
     if (!defined(ubsan_vptr_ignorelist_path)) {
@@ -523,7 +514,6 @@
   ":tsan_flags",
   ":ubsan_flags",
   ":ubsan_no_recover",
-  ":ubsan_null_flags",
   ":ubsan_security_flags",
   ":ubsan_vptr_flags",
   ":fuzzing_build_mode",
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 3dedd2d..609de62 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -34,9 +34,6 @@
   # Halt the program if a problem is detected.
   is_ubsan_no_recover = false
 
-  # Compile for Undefined Behaviour Sanitizer's null pointer checks.
-  is_ubsan_null = false
-
   # Track where uninitialized memory originates from. From fastest to slowest:
   # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the
   # chain of stores leading from allocation site to use site.
@@ -130,7 +127,6 @@
   is_msan = false
   is_tsan = false
   is_ubsan = false
-  is_ubsan_null = false
   is_ubsan_no_recover = false
   is_ubsan_security = false
   is_ubsan_vptr = false
@@ -189,9 +185,9 @@
 # Whether we are linking against a sanitizer runtime library. Among other
 # things, this changes the default symbol level and other settings in order to
 # prepare to create stack traces "live" using the sanitizer runtime.
-using_sanitizer = is_asan || is_hwasan || is_lsan || is_tsan || is_msan ||
-                  is_ubsan || is_ubsan_null || is_ubsan_vptr ||
-                  is_ubsan_security || use_sanitizer_coverage || use_cfi_diag
+using_sanitizer =
+    is_asan || is_hwasan || is_lsan || is_tsan || is_msan || is_ubsan ||
+    is_ubsan_vptr || is_ubsan_security || use_sanitizer_coverage || use_cfi_diag
 
 assert(!using_sanitizer || is_clang,
        "Sanitizers (is_*san) require setting is_clang = true in 'gn args'")
@@ -223,7 +219,7 @@
 # unsupported or unadvisable configurations.
 #
 # For one-off testing, just comment this assertion out.
-assert(!is_debug || !(is_msan || is_ubsan || is_ubsan_null || is_ubsan_vptr),
+assert(!is_debug || !(is_msan || is_ubsan || is_ubsan_vptr),
        "Sanitizers should generally be used in release (set is_debug=false).")
 
 assert(!is_msan || ((is_linux || is_chromeos) && current_cpu == "x64"),
diff --git a/build/fuchsia/binary_size_differ.py b/build/fuchsia/binary_size_differ.py
index 379fd44..d3c2c4f 100755
--- a/build/fuchsia/binary_size_differ.py
+++ b/build/fuchsia/binary_size_differ.py
@@ -26,7 +26,13 @@
 from binary_sizes import ReadPackageSizesJson
 from binary_sizes import PACKAGES_SIZES_FILE
 
-_MAX_DELTA_BYTES = 12 * 1024  # 12 KiB
+# Eng is not responsible for changes that cause "reasonable growth" if the
+# uncompressed binary size does not grow.
+# First-warning will fail the test if the uncompressed and compressed size
+# grow, while always-fail will fail the test regardless of uncompressed growth
+# (solely based on compressed growth).
+_FIRST_WARNING_DELTA_BYTES = 12 * 1024  # 12 KiB
+_ALWAYS_FAIL_DELTA_BYTES = 100 * 1024  # 100 KiB
 _TRYBOT_DOC = 'https://chromium.googlesource.com/chromium/src/+/main/docs/speed/binary_size/fuchsia_binary_size_trybot.md'
 
 
@@ -38,7 +44,7 @@
   assert before_sizes.keys() == after_sizes.keys(), (
       'Package files cannot'
       ' be compared with different packages: '
-      '%s vs %s' % (before_sizes.keys(), after_sizes.keys()))
+      '{} vs {}'.format(before_sizes.keys(), after_sizes.keys()))
 
   growth = {'compressed': {}, 'uncompressed': {}}
   status_code = 0
@@ -49,17 +55,22 @@
     growth['uncompressed'][package_name] = (
         after_sizes[package_name].uncompressed -
         before_sizes[package_name].uncompressed)
-    if growth['compressed'][package_name] >= _MAX_DELTA_BYTES:
-      if status_code == 1 and not summary:
-        summary = 'Size check failed! The following package(s) are affected:\n'
+    # Developers are only responsible if uncompressed increases.
+    if ((growth['compressed'][package_name] >= _FIRST_WARNING_DELTA_BYTES
+         and growth['uncompressed'][package_name] > 0)
+        # However, if compressed growth is unusually large, fail always.
+        or growth['compressed'][package_name] >= _ALWAYS_FAIL_DELTA_BYTES):
+      if not summary:
+        summary = ('Size check failed! The following package(s) are affected:'
+                   '<br>')
       status_code = 1
-      summary += ('- %s grew by %d bytes\n' %
-                  (package_name, growth['compressed'][package_name]))
+      summary += ('- {} grew by {} bytes<br>'.format(
+          package_name, growth['compressed'][package_name]))
 
   growth['status_code'] = status_code
-  summary += ('\nSee the following document for more information about'
-              ' this trybot:\n%s' % _TRYBOT_DOC)
-  growth['summary'] = summary.replace('\n', '<br>')
+  summary += ('<br>See the following document for more information about'
+              ' this trybot:<br>{}'.format(_TRYBOT_DOC))
+  growth['summary'] = summary
 
   # TODO(crbug.com/1266085): Investigate using these fields.
   growth['archive_filenames'] = []
@@ -101,8 +112,9 @@
       print('  {}: {}'.format(var, getattr(args, var) or ''))
 
   if not os.path.isdir(args.before_dir) or not os.path.isdir(args.after_dir):
-    raise Exception('Could not find build output directory "%s" or "%s".' %
-                    (args.before_dir, args.after_dir))
+    raise Exception(
+        'Could not find build output directory "{}" or "{}".'.format(
+            args.before_dir, args.after_dir))
 
   test_name = 'sizes'
   before_sizes_file = os.path.join(args.before_dir, test_name,
@@ -110,12 +122,12 @@
   after_sizes_file = os.path.join(args.after_dir, test_name,
                                   PACKAGES_SIZES_FILE)
   if not os.path.isfile(before_sizes_file):
-    raise Exception('Could not find before sizes file: "%s"' %
-                    (before_sizes_file))
+    raise Exception(
+        'Could not find before sizes file: "{}"'.format(before_sizes_file))
 
   if not os.path.isfile(after_sizes_file):
-    raise Exception('Could not find after sizes file: "%s"' %
-                    (after_sizes_file))
+    raise Exception(
+        'Could not find after sizes file: "{}"'.format(after_sizes_file))
 
   test_completed = False
   try:
diff --git a/build/fuchsia/binary_size_differ_test.py b/build/fuchsia/binary_size_differ_test.py
index 6c015f29..b22872d 100755
--- a/build/fuchsia/binary_size_differ_test.py
+++ b/build/fuchsia/binary_size_differ_test.py
@@ -6,6 +6,7 @@
 import copy
 import os
 import tempfile
+from typing import MutableMapping, Optional
 import unittest
 
 import binary_size_differ
@@ -50,20 +51,18 @@
 
 
 class BinarySizeDifferTest(unittest.TestCase):
-  def ChangeBlobSize(self, blobs, package, name, increase):
-    original_blob = blobs[package][name]
-    new_blob = binary_sizes.Blob(name=original_blob.name,
-                                 hash=original_blob.hash,
-                                 uncompressed=original_blob.uncompressed,
-                                 compressed=original_blob.compressed + increase,
-                                 is_counted=original_blob.is_counted)
-    blobs[package][name] = new_blob
-
-  def ChangePackageSize(self, packages, name, increase):
+  def ChangePackageSize(
+      self,
+      packages: MutableMapping[str, binary_sizes.PackageSizes],
+      name: str,
+      compressed_increase: int,
+      uncompressed_increase: Optional[int] = None):
+    if uncompressed_increase is None:
+      uncompressed_increase = compressed_increase
     original_package = packages[name]
     new_package = binary_sizes.PackageSizes(
-        compressed=original_package.compressed + increase,
-        uncompressed=original_package.uncompressed)
+        compressed=original_package.compressed + compressed_increase,
+        uncompressed=original_package.uncompressed + uncompressed_increase)
     packages[name] = new_package
 
   def testComputePackageDiffs(self):
@@ -104,6 +103,43 @@
                                                         after_file.name)
         self.assertEqual(growth['status_code'], 1)
         self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)
+
+        # Increase beyond the limit, but compressed does not increase.
+        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
+        self.ChangePackageSize(other_sizes,
+                               'web_engine',
+                               16 * 1024 + 1,
+                               uncompressed_increase=0)
+        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
+        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
+                                                        after_file.name)
+        self.assertEqual(growth['uncompressed']['web_engine'], 0)
+        self.assertEqual(growth['status_code'], 0)
+        self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)
+
+        # Increase beyond the limit, but compressed goes down.
+        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
+        self.ChangePackageSize(other_sizes,
+                               'web_engine',
+                               16 * 1024 + 1,
+                               uncompressed_increase=-4 * 1024)
+        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
+        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
+                                                        after_file.name)
+        self.assertEqual(growth['status_code'], 0)
+        self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)
+
+        # Increase beyond the second limit. Fails, regardless of uncompressed.
+        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
+        self.ChangePackageSize(other_sizes,
+                               'web_engine',
+                               100 * 1024 + 1,
+                               uncompressed_increase=-4 * 1024)
+        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
+        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
+                                                        after_file.name)
+        self.assertEqual(growth['status_code'], 1)
+        self.assertEqual(growth['compressed']['web_engine'], 100 * 1024 + 1)
       finally:
         os.remove(after_file.name)
 
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 5eaa9fb4..62f8084 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -113,6 +113,19 @@
     'lacros_chrome_browsertests_run_in_series'
 ]
 
+# Default test filter file for each target. These filter files will be
+# used by default if no other filter file get specified.
+_DEFAULT_FILTER_FILES_MAPPING = {
+    'browser_tests': 'linux-lacros.browser_tests.filter',
+    'components_unittests': 'linux-lacros.components_unittests.filter',
+    'content_browsertests': 'linux-lacros.content_browsertests.filter',
+    'interactive_ui_tests': 'linux-lacros.interactive_ui_tests.filter',
+    'lacros_chrome_browsertests':
+    'linux-lacros.lacros_chrome_browsertests.filter',
+    'sync_integration_tests': 'linux-lacros.sync_integration_tests.filter',
+    'unit_tests': 'linux-lacros.unit_tests.filter',
+}
+
 
 def _GetAshChromeDirPath(version):
   """Returns a path to the dir storing the downloaded version of ash-chrome."""
@@ -547,6 +560,16 @@
   sys.exit(128 + sig)
 
 
+def _ExpandFilterFileIfNeeded(test_target, forward_args):
+  if (test_target in _DEFAULT_FILTER_FILES_MAPPING.keys() and not any(
+      [arg.startswith('--test-launcher-filter-file') for arg in forward_args])):
+    file_path = os.path.abspath(
+        os.path.join(os.path.dirname(__file__), '..', '..', 'testing',
+                     'buildbot', 'filters',
+                     _DEFAULT_FILTER_FILES_MAPPING[test_target]))
+    forward_args.append(f'--test-launcher-filter-file={file_path}')
+
+
 def _RunTest(args, forward_args):
   """Runs tests with given args.
 
@@ -562,13 +585,15 @@
     raise RuntimeError('Specified test command: "%s" doesn\'t exist' %
                        args.command)
 
+  test_target = os.path.basename(args.command)
+  _ExpandFilterFileIfNeeded(test_target, forward_args)
+
   # |_TARGETS_REQUIRE_ASH_CHROME| may not always be accurate as it is updated
   # with a best effort only, therefore, allow the invoker to override the
   # behavior with a specified ash-chrome version, which makes sure that
   # automated CI/CQ builders would always work correctly.
   requires_ash_chrome = any(
-      re.match(t, os.path.basename(args.command))
-      for t in _TARGETS_REQUIRE_ASH_CHROME)
+      re.match(t, test_target) for t in _TARGETS_REQUIRE_ASH_CHROME)
   if not requires_ash_chrome and not args.ash_chrome_version:
     return _RunTestDirectly(args, forward_args)
 
diff --git a/build/lacros/test_runner_test.py b/build/lacros/test_runner_test.py
index 13a87d07..46544497 100755
--- a/build/lacros/test_runner_test.py
+++ b/build/lacros/test_runner_test.py
@@ -25,6 +25,26 @@
   def tearDown(self):
     logging.disable(logging.NOTSET)
 
+  @mock.patch.object(os.path,
+                     'dirname',
+                     return_value='chromium/src/build/lacros')
+  def test_expand_filter_file(self, _):
+    args = ['--some_flag="flag"']
+    test_runner._ExpandFilterFileIfNeeded('browser_tests', args)
+    self.assertTrue(args[1].endswith(
+        'chromium/src/'
+        'testing/buildbot/filters/linux-lacros.browser_tests.filter'))
+    self.assertTrue(args[1].startswith('--test-launcher-filter-file='))
+
+    args = ['--some_flag="flag"']
+    test_runner._ExpandFilterFileIfNeeded('random_tests', args)
+    self.assertEqual(len(args), 1)
+
+    args = ['--test-launcher-filter-file=new/filter']
+    test_runner._ExpandFilterFileIfNeeded('browser_tests', args)
+    self.assertEqual(len(args), 1)
+    self.assertTrue(args[0].endswith('new/filter'))
+
   @parameterized.expand([
       'url_unittests',
       './url_unittests',
@@ -60,6 +80,7 @@
   @mock.patch.object(os.environ, 'copy', side_effect=[{}, {}])
   @mock.patch.object(os.path, 'exists', return_value=True)
   @mock.patch.object(os.path, 'isfile', return_value=True)
+  @mock.patch.object(os.path, 'abspath', return_value='/a/b/filter')
   @mock.patch.object(test_runner,
                      '_GetLatestVersionOfAshChrome',
                      return_value='793554')
@@ -96,10 +117,11 @@
       if command == 'lacros_chrome_browsertests':
         self.assertListEqual([
             command,
-            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock'
+            '--test-launcher-filter-file=/a/b/filter',
+            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock',
         ], test_args)
       else:
-        self.assertListEqual([command], test_args)
+        self.assertListEqual([command], [test_args[0]])
 
       test_env = mock_popen.call_args_list[1][1].get('env', {})
       self.assertDictEqual(
diff --git a/build/toolchain/cros/BUILD.gn b/build/toolchain/cros/BUILD.gn
index ebe1459b..0ebb06af 100644
--- a/build/toolchain/cros/BUILD.gn
+++ b/build/toolchain/cros/BUILD.gn
@@ -125,6 +125,24 @@
   }
 }
 
+# This is a special toolchain needed just for the nacl_helper target for
+# building an Arm32 nacl_helper binary on Arm64 ChromeOS targets.
+cros_toolchain("nacl_helper_arm32") {
+  toolchain_args = {
+    current_cpu = "arm"
+    current_os = "chromeos"
+    use_debug_fission = false
+    use_gold = false
+    needs_gomacc_path_arg = false
+    sysroot = cros_nacl_helper_arm32_sysroot
+  }
+  ar = cros_nacl_helper_arm32_ar
+  cc = cros_nacl_helper_arm32_cc
+  cxx = cros_nacl_helper_arm32_cxx
+  ld = cros_nacl_helper_arm32_ld
+  readelf = cros_nacl_helper_arm32_readelf
+}
+
 cros_toolchain("host") {
   toolchain_args = {
     current_cpu = host_cpu
diff --git a/build/toolchain/cros_toolchain.gni b/build/toolchain/cros_toolchain.gni
index 9b7e3b6..8dce7f12 100644
--- a/build/toolchain/cros_toolchain.gni
+++ b/build/toolchain/cros_toolchain.gni
@@ -76,10 +76,17 @@
   cros_nacl_bootstrap_extra_cppflags = ""
   cros_nacl_bootstrap_extra_cxxflags = ""
   cros_nacl_bootstrap_extra_ldflags = ""
+
+  cros_nacl_helper_arm32_ar = "ar"
+  cros_nacl_helper_arm32_cc = "gcc"
+  cros_nacl_helper_arm32_cxx = "g++"
+  cros_nacl_helper_arm32_readelf = ""
+  cros_nacl_helper_arm32_sysroot = ""
 }
 
 declare_args() {
   cros_target_ld = cros_target_cxx
   cros_host_ld = cros_host_cxx
   cros_v8_snapshot_ld = cros_v8_snapshot_cxx
+  cros_nacl_helper_arm32_ld = cros_nacl_helper_arm32_cxx
 }
diff --git a/buildtools/third_party/libc++/__config_site b/buildtools/third_party/libc++/__config_site
index 98383cd..691c324 100644
--- a/buildtools/third_party/libc++/__config_site
+++ b/buildtools/third_party/libc++/__config_site
@@ -28,4 +28,8 @@
 /* #undef _LIBCPP_HAS_NO_LOCALIZATION */
 #define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
 
+// Enable libc++ assertions for hardening and safety
+// (https://crbug.com/1335422).
+#define _LIBCPP_ENABLE_ASSERTIONS 1
+
 #endif // _LIBCPP_CONFIG_SITE
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
index 0e49cf0..e5a547a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
@@ -159,23 +159,26 @@
             boolean enabled, Callback<Integer> callback) {
         if (bookmarkId == null || subscriptionsManager == null) return;
 
-        PowerBookmarkMeta meta = bookmarkBridge.getPowerBookmarkMeta(bookmarkId);
-        if (meta == null || meta.getType() != PowerBookmarkType.SHOPPING) return;
+        bookmarkBridge.finishLoadingBookmarkModel(() -> {
+            PowerBookmarkMeta meta = bookmarkBridge.getPowerBookmarkMeta(bookmarkId);
+            if (meta == null || meta.getType() != PowerBookmarkType.SHOPPING) return;
 
-        CommerceSubscription subscription = createCommerceSubscriptionForPowerBookmarkMeta(meta);
-        Callback<Integer> wrapperCallback = (status) -> {
-            if (bookmarkBridge.isDestroyed()) return;
-            if (status == SubscriptionsManager.StatusCode.OK) {
-                setPriceTrackingEnabledInMetadata(bookmarkBridge, bookmarkId, enabled);
+            CommerceSubscription subscription =
+                    createCommerceSubscriptionForPowerBookmarkMeta(meta);
+            Callback<Integer> wrapperCallback = (status) -> {
+                if (bookmarkBridge.isDestroyed()) return;
+                if (status == SubscriptionsManager.StatusCode.OK) {
+                    setPriceTrackingEnabledInMetadata(bookmarkBridge, bookmarkId, enabled);
+                }
+                callback.onResult(status);
+            };
+
+            if (enabled) {
+                subscriptionsManager.subscribe(subscription, wrapperCallback);
+            } else {
+                subscriptionsManager.unsubscribe(subscription, wrapperCallback);
             }
-            callback.onResult(status);
-        };
-
-        if (enabled) {
-            subscriptionsManager.subscribe(subscription, wrapperCallback);
-        } else {
-            subscriptionsManager.unsubscribe(subscription, wrapperCallback);
-        }
+        });
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
index ac24487..384c63a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
@@ -48,7 +48,6 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.CommandLineFlags.Add;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -84,7 +83,9 @@
  * Render tests for sync consent fragment.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, ChromeSwitches.FORCE_ENABLE_SIGNIN_FRE})
+@CommandLineFlags.
+Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, ChromeSwitches.FORCE_ENABLE_SIGNIN_FRE})
+@DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
 public class SyncConsentFragmentTest {
     private static final int RENDER_REVISION = 1;
     private static final String RENDER_DESCRIPTION = "Change button style";
@@ -164,7 +165,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSyncConsentFragmentDefaultAccount() throws IOException {
         CoreAccountInfo accountInfo =
                 mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
@@ -198,41 +198,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @EnableFeatures({ChromeFeatureList.TANGIBLE_SYNC + ":group_id/2"})
-    public void testTangibleSyncConsentFragmentVariationTwoDefaultAccount() throws IOException {
-        CoreAccountInfo accountInfo =
-                mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
-        mSyncConsentActivity = ActivityTestUtils.waitForActivity(
-                InstrumentationRegistry.getInstrumentation(), SyncConsentActivity.class, () -> {
-                    SyncConsentActivityLauncherImpl.get().launchActivityForPromoDefaultFlow(
-                            mChromeActivityTestRule.getActivity(),
-                            SigninAccessPoint.BOOKMARK_MANAGER, accountInfo.getEmail());
-                });
-        mRenderTestRule.render(mSyncConsentActivity.findViewById(R.id.fragment_container),
-                "tangible_sync_consent_fragment_variation_two_default_account");
-    }
-
-    @Test
-    @LargeTest
-    @Feature("RenderTest")
-    @EnableFeatures({ChromeFeatureList.TANGIBLE_SYNC + ":group_id/3"})
-    public void testTangibleSyncConsentFragmentVariationThreeDefaultAccount() throws IOException {
-        CoreAccountInfo accountInfo =
-                mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
-        mSyncConsentActivity = ActivityTestUtils.waitForActivity(
-                InstrumentationRegistry.getInstrumentation(), SyncConsentActivity.class, () -> {
-                    SyncConsentActivityLauncherImpl.get().launchActivityForPromoDefaultFlow(
-                            mChromeActivityTestRule.getActivity(),
-                            SigninAccessPoint.BOOKMARK_MANAGER, accountInfo.getEmail());
-                });
-        mRenderTestRule.render(mSyncConsentActivity.findViewById(R.id.fragment_container),
-                "tangible_sync_consent_fragment_variation_three_default_account");
-    }
-
-    @Test
-    @LargeTest
-    @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSyncConsentFragmentNewAccount() throws IOException {
         mSyncConsentActivity = ActivityTestUtils.waitForActivity(
                 InstrumentationRegistry.getInstrumentation(), SyncConsentActivity.class, () -> {
@@ -247,7 +212,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSyncConsentFragmentNotDefaultAccountWithPrimaryAccount() throws IOException {
         CoreAccountInfo accountInfo =
                 mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
@@ -288,7 +252,6 @@
     // This test is only relevant if child users do not have sync force-enabled (if they do, then
     // they can only ever access this fragment from the FRE).
     @EnableFeatures({ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS})
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSyncConsentFragmentWithChildAccount() throws IOException {
         CoreAccountInfo accountInfo = mSigninTestRule.addChildTestAccountThenWaitForSignin();
         mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
@@ -305,7 +268,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWithNoAccountsOnDevice() throws IOException {
         HistogramDelta startPageHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.START_PAGE);
@@ -324,7 +286,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWithAdultAccount() throws IOException {
         HistogramDelta startPageHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.START_PAGE);
@@ -364,10 +325,8 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures(
-            {ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS, ChromeFeatureList.TANGIBLE_SYNC})
-    public void
-    testFRESyncConsentFragmentWithChildAccount() throws IOException {
+    @DisableFeatures({ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS})
+    public void testFRESyncConsentFragmentWithChildAccount() throws IOException {
         HistogramDelta startPageHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.START_PAGE);
         mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
@@ -392,7 +351,6 @@
     @LargeTest
     @Feature("RenderTest")
     @EnableFeatures({ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS})
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWithChildAccountAllowSyncOff() throws IOException {
         HistogramDelta startPageHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.START_PAGE);
@@ -419,7 +377,6 @@
     @Feature("RenderTest")
     @CommandLineFlags.Remove({ChromeSwitches.FORCE_ENABLE_SIGNIN_FRE})
     @EnableFeatures({ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS})
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWithChildAccountLegacy() throws IOException {
         HistogramDelta startPageHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.START_PAGE);
@@ -444,7 +401,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWhenSignedInWithoutSync() throws IOException {
         mSigninTestRule.addTestAccountThenSignin();
         CustomSyncConsentFirstRunFragment fragment = new CustomSyncConsentFirstRunFragment();
@@ -460,7 +416,6 @@
 
     @Test
     @MediumTest
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWhenSelectedAccountIsRemoved() {
         final CoreAccountInfo defaultAccount =
                 mSigninTestRule.addAccount("test.default.account@gmail.com");
@@ -483,7 +438,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWhenSignedInWithoutSyncDynamically() throws IOException {
         CustomSyncConsentFirstRunFragment fragment = new CustomSyncConsentFirstRunFragment();
         Bundle bundle = new Bundle();
@@ -505,7 +459,6 @@
 
     @Test
     @LargeTest
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testClickingSettingsDoesNotSetFirstSetupComplete() {
         CoreAccountInfo accountInfo =
                 mSigninTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
@@ -581,7 +534,6 @@
 
     @Test
     @LargeTest
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testFRESyncConsentFragmentWithoutSelectedAccount() {
         CustomSyncConsentFirstRunFragment fragment = new CustomSyncConsentFirstRunFragment();
         Bundle bundle = new Bundle();
@@ -597,7 +549,6 @@
 
     @Test
     @MediumTest
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSyncConsentFragmentWithDefaultFlow() {
         HistogramDelta settingsHistogram =
                 new HistogramDelta("Signin.SigninStartedAccessPoint", SigninAccessPoint.SETTINGS);
@@ -613,7 +564,6 @@
 
     @Test
     @MediumTest
-    @DisableFeatures({ChromeFeatureList.TANGIBLE_SYNC})
     public void testSelectNonDefaultAccountInAccountPickerDialog() {
         HistogramDelta bookmarkHistogram = new HistogramDelta(
                 "Signin.SigninStartedAccessPoint", SigninAccessPoint.BOOKMARK_MANAGER);
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 6145ac8..3799b4be 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -231,6 +231,11 @@
 
 namespace {
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+const base::FilePath::CharType kUserHomeDirPrefix[] =
+    FILE_PATH_LITERAL("/home/user");
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #if BUILDFLAG(IS_WIN)
 // Early versions of Chrome incorrectly registered a chromehtml: URL handler,
 // which gives us nothing but trouble. Avoid launching chrome this way since
@@ -1154,6 +1159,16 @@
         chromeos::BrowserParamsProxy::Get();
     chrome::SetLacrosDefaultPathsFromInitParams(
         init_params->DefaultPaths().get());
+
+    // Override the login user DIR_HOME path for the Lacros browser process. The
+    // primary user id hash is expected to be already set, because Lacros should
+    // only run inside the user session.
+    if (init_params->CrosUserIdHash().has_value()) {
+      base::FilePath homedir(kUserHomeDirPrefix);
+      homedir = homedir.Append(init_params->CrosUserIdHash().value());
+      base::PathService::OverrideAndCreateIfNeeded(
+          base::DIR_HOME, homedir, /*is_absolute=*/true, /*create=*/false);
+    }
   }
 
   // Generate shared resource file only on browser process. This is to avoid
@@ -1188,7 +1203,7 @@
           ui::k200Percent);
     }
   }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
   // Register component_updater PathProvider after DIR_USER_DATA overridden by
   // command line flags. Maybe move the chrome PathProvider down here also?
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ca6e82c1..7ceb48d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1543,8 +1543,6 @@
     "security_events/security_event_sync_bridge_impl.h",
     "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.cc",
     "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.h",
-    "segmentation_platform/model_provider_factory_impl.cc",
-    "segmentation_platform/model_provider_factory_impl.h",
     "segmentation_platform/segmentation_platform_config.cc",
     "segmentation_platform/segmentation_platform_config.h",
     "segmentation_platform/segmentation_platform_profile_observer.cc",
@@ -3106,8 +3104,6 @@
       "notifications/scheduler/display_agent_android.h",
       "notifications/scheduler/notification_background_task_scheduler_android.cc",
       "notifications/scheduler/notification_background_task_scheduler_android.h",
-      "optimization_guide/android/android_push_notification_manager.cc",
-      "optimization_guide/android/android_push_notification_manager.h",
       "optimization_guide/android/optimization_guide_bridge.cc",
       "optimization_guide/android/optimization_guide_bridge.h",
       "optimization_guide/android/optimization_guide_tab_url_provider_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4705ecf..29fa3e8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -797,17 +797,6 @@
      std::size(kShowSingleRowMVTiles), nullptr},
     {"(show two rows of MV tiles)", kShowTwoRowsMVTiles,
      std::size(kShowTwoRowsMVTiles), nullptr}};
-
-const FeatureEntry::FeatureParam kTangibleSyncGroupA[] = {{"group_id", "1"}};
-const FeatureEntry::FeatureParam kTangibleSyncGroupB[] = {{"group_id", "2"}};
-const FeatureEntry::FeatureParam kTangibleSyncGroupC[] = {{"group_id", "3"}};
-const FeatureEntry::FeatureVariation kTangibleSyncVariations[] = {
-    {"(turn on sync and back up)", kTangibleSyncGroupA,
-     std::size(kTangibleSyncGroupA), nullptr},
-    {"(sync and back up)", kTangibleSyncGroupB, std::size(kTangibleSyncGroupB),
-     nullptr},
-    {"(turn on sync and sync data)", kTangibleSyncGroupC,
-     std::size(kTangibleSyncGroupC), nullptr}};
 #endif  // BUILDFLAG(IS_ANDROID)
 
 const FeatureEntry::Choice kEnableGpuRasterizationChoices[] = {
@@ -6446,9 +6435,7 @@
 
     {"tangible-sync", flag_descriptions::kTangibleSyncName,
      flag_descriptions::kTangibleSyncDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(switches::kTangibleSync,
-                                    kTangibleSyncVariations,
-                                    "TangibleSyncVariations")},
+     FEATURE_VALUE_TYPE(switches::kTangibleSync)},
 
     {"enable-cbd-sign-out", flag_descriptions::kEnableCbdSignOutName,
      flag_descriptions::kEnableCbdSignOutDescription, kOsAndroid,
diff --git a/chrome/browser/accessibility/accessibility_labels_service_browsertest.cc b/chrome/browser/accessibility/accessibility_labels_service_browsertest.cc
index e8afaca..7526871 100644
--- a/chrome/browser/accessibility/accessibility_labels_service_browsertest.cc
+++ b/chrome/browser/accessibility/accessibility_labels_service_browsertest.cc
@@ -16,8 +16,6 @@
 #include "content/public/test/browser_test.h"
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
-#else
-#include "content/public/browser/browser_accessibility_state.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 class AccessibilityLabelsBrowserTest : public InProcessBrowserTest {
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 1a6a11c7..88582b8 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -24,7 +24,6 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
-#include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_change_registrar.h"
 
 class AppControllerProfileObserver;
diff --git a/chrome/browser/ash/authpolicy/kerberos_files_handler.cc b/chrome/browser/ash/authpolicy/kerberos_files_handler.cc
index e7253fbb..34bc6e4 100644
--- a/chrome/browser/ash/authpolicy/kerberos_files_handler.cc
+++ b/chrome/browser/ash/authpolicy/kerberos_files_handler.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/base_paths.h"
 #include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index f12fcc0..14e7fad 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -319,7 +319,6 @@
     "//extensions/browser/api/networking_private",
     "//extensions/common",
     "//media/mojo/mojom/stable:stable_video_decoder",
-    "//printing",
     "//printing/backend",
     "//remoting/host/chromeos:remoting_service",
     "//services/device/wake_lock/power_save_blocker",
@@ -346,6 +345,7 @@
   public_deps = [
     ":browser_util",
     "//media/gpu:buildflags",
+    "//printing",
   ]
 }
 
diff --git a/chrome/browser/ash/login/screens/gaia_screen.cc b/chrome/browser/ash/login/screens/gaia_screen.cc
index fea7d02..8edb1a4 100644
--- a/chrome/browser/ash/login/screens/gaia_screen.cc
+++ b/chrome/browser/ash/login/screens/gaia_screen.cc
@@ -127,6 +127,7 @@
     exit_callback_.Run(Result::ENTERPRISE_ENROLL);
   } else if (action_id == kUserActionReloadDefault) {
     DCHECK(features::IsRedirectToDefaultIdPEnabled());
+    Reset();
     LoadOnline(EmptyAccountId());
   } else if (action_id == kUserActionRetry) {
     LoadOnline(EmptyAccountId());
diff --git a/chrome/browser/ash/login/screens/welcome_screen.cc b/chrome/browser/ash/login/screens/welcome_screen.cc
index 7d59d9b..20e5805 100644
--- a/chrome/browser/ash/login/screens/welcome_screen.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen.cc
@@ -16,7 +16,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/default_tick_clock.h"
-#include "build/config/chromebox_for_meetings/buildflags.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
 #include "chrome/browser/ash/base/locale_util.h"
@@ -152,12 +151,8 @@
 // for testing. Note: Can be overridden with the command line switch
 // --enable-requisition-edits.
 bool IsRemoraRequisitionConfigurable() {
-#if BUILDFLAG(PLATFORM_CFM)
-  return true;
-#else
-  return policy::EnrollmentRequisitionManager::IsRemoraRequisition() ||
+  return policy::EnrollmentRequisitionManager::IsMeetDevice() ||
          switches::IsDeviceRequisitionConfigurable();
-#endif
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 361282f..0f3fda8 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -2113,6 +2113,12 @@
             << requisition_value->GetString();
     policy::EnrollmentRequisitionManager::SetDeviceRequisition(
         requisition_value->GetString());
+  } else if (policy::EnrollmentRequisitionManager::IsMeetDevice()) {
+    VLOG(1)
+        << "Using default Device Requisition value for CFM build configuration"
+        << policy::EnrollmentRequisitionManager::kRemoraRequisition;
+    policy::EnrollmentRequisitionManager::SetDeviceRequisition(
+        policy::EnrollmentRequisitionManager::kRemoraRequisition);
   }
 
   auto* network_config_value = wizard_context_->configuration.FindKeyOfType(
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
index 10f8ce9..0e71a4d7 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 
 #include "base/logging.h"
+#include "build/chromeos_buildflags.h"
+#include "build/config/chromebox_for_meetings/buildflags.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/browser_process.h"
@@ -133,6 +135,14 @@
   return GetDeviceRequisition() == kSharkRequisition;
 }
 
+bool EnrollmentRequisitionManager::IsMeetDevice() {
+#if BUILDFLAG(PLATFORM_CFM)
+  return true;
+#else
+  return IsRemoraRequisition();
+#endif  // BUILDFLAG(PLATFORM_CFM)
+}
+
 // static
 std::string EnrollmentRequisitionManager::GetSubOrganization() {
   const PrefService::Preference* pref =
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h
index 8c47e0d..0d96e5cf 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h
+++ b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h
@@ -36,6 +36,9 @@
   static bool IsRemoraRequisition();
   static bool IsSharkRequisition();
 
+  // If the current device extends the CFM Overlay or has Remora bit set
+  static bool IsMeetDevice();
+
   // Gets/Sets the sub organization.
   static std::string GetSubOrganization();
   static void SetSubOrganization(const std::string& sub_organization);
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
index 5efccf9..f723591a 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -437,7 +437,11 @@
 }
 
 void PersonalizationAppWallpaperProviderImpl::OnWallpaperPreviewEnded() {
-  PersonalizationAppWallpaperProviderImpl::OnWallpaperChanged();
+  DCHECK(wallpaper_observer_remote_.is_bound());
+  wallpaper_observer_remote_->OnWallpaperPreviewEnded();
+  // Make sure to fire another |OnWallpaperChanged| after preview is over so
+  // that personalization app ends up with correct wallpaper state.
+  OnWallpaperChanged();
 }
 
 void PersonalizationAppWallpaperProviderImpl::SelectWallpaper(
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 0f1e370..1833934 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -187,6 +187,9 @@
 class TestWallpaperObserver
     : public ash::personalization_app::mojom::WallpaperObserver {
  public:
+  // WallpaperObserver:
+  void OnWallpaperPreviewEnded() override {}
+
   void OnWallpaperChanged(
       ash::personalization_app::mojom::CurrentWallpaperPtr image) override {
     current_wallpaper_ = std::move(image);
diff --git a/chrome/browser/cart/cart_handler_unittest.cc b/chrome/browser/cart/cart_handler_unittest.cc
index ef511d47..5ce8016 100644
--- a/chrome/browser/cart/cart_handler_unittest.cc
+++ b/chrome/browser/cart/cart_handler_unittest.cc
@@ -86,8 +86,6 @@
         {kFakeMerchant, kFakeProto},
         {kMockMerchantB, kMockProtoB},
 };
-const std::vector<SessionProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>
-    kEmptyExpected = {};
 }  // namespace
 
 class CartHandlerTest : public testing::Test {
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 293386b..4c074a7 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -177,7 +177,6 @@
 #include "chrome/browser/ui/webui/whats_new/whats_new_ui.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/search/ntp_features.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
 #include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
 #include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 6375ccd1..4103160 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -90,7 +90,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/web_applications/isolation_prefs_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "content/public/browser/storage_partition_config.h"
diff --git a/chrome/browser/download/download_query.cc b/chrome/browser/download/download_query.cc
index ffba87c..8aae800 100644
--- a/chrome/browser/download/download_query.cc
+++ b/chrome/browser/download/download_query.cc
@@ -220,6 +220,68 @@
 
 }  // anonymous namespace
 
+// AddSorter() creates a Sorter and pushes it onto sorters_. A Sorter is a
+// direction and a Callback to Compare<>(). After filtering, Search() makes a
+// DownloadComparator functor from the sorters_ and passes the
+// DownloadComparator to std::partial_sort. std::partial_sort calls the
+// DownloadComparator with different pairs of DownloadItems.  DownloadComparator
+// iterates over the sorters until a callback returns ComparisonType LT or GT.
+// DownloadComparator returns true or false depending on that ComparisonType and
+// the sorter's direction in order to indicate to std::partial_sort whether the
+// left item is after or before the right item. If all sorters return EQ, then
+// DownloadComparator compares GetId. A DownloadQuery may have zero or more
+// Sorters, but there is one DownloadComparator per call to Search().
+
+struct DownloadQuery::Sorter {
+  using SortType = base::RepeatingCallback<ComparisonType(const DownloadItem&,
+                                                          const DownloadItem&)>;
+
+  template <typename ValueType>
+  static Sorter Build(DownloadQuery::SortDirection adirection,
+                      ValueType (*accessor)(const DownloadItem&)) {
+    return Sorter(adirection,
+                  base::BindRepeating(&Compare<ValueType>,
+                                      base::BindRepeating(accessor)));
+  }
+
+  Sorter(DownloadQuery::SortDirection adirection, const SortType& asorter)
+      : direction(adirection), sorter(asorter) {}
+  ~Sorter() = default;
+
+  DownloadQuery::SortDirection direction;
+  SortType sorter;
+};
+
+class DownloadQuery::DownloadComparator {
+ public:
+  explicit DownloadComparator(const DownloadQuery::SorterVector& terms)
+      : terms_(terms) {}
+
+  // Returns true if |left| sorts before |right|.
+  bool operator()(const DownloadItem* left, const DownloadItem* right);
+
+ private:
+  const DownloadQuery::SorterVector& terms_;
+
+  // std::sort requires this class to be copyable.
+};
+
+bool DownloadQuery::DownloadComparator::operator()(const DownloadItem* left,
+                                                   const DownloadItem* right) {
+  for (auto term = terms_.begin(); term != terms_.end(); ++term) {
+    switch (term->sorter.Run(*left, *right)) {
+      case LT:
+        return term->direction == DownloadQuery::ASCENDING;
+      case GT:
+        return term->direction == DownloadQuery::DESCENDING;
+      case EQ:
+        break;  // break the switch but not the loop
+    }
+  }
+  CHECK_NE(left->GetId(), right->GetId());
+  return left->GetId() < right->GetId();
+}
+
 // static
 bool DownloadQuery::MatchesQuery(const std::vector<std::u16string>& query_terms,
                                  const DownloadItem& item) {
@@ -342,66 +404,6 @@
   return true;
 }
 
-// AddSorter() creates a Sorter and pushes it onto sorters_. A Sorter is a
-// direction and a Callback to Compare<>(). After filtering, Search() makes a
-// DownloadComparator functor from the sorters_ and passes the
-// DownloadComparator to std::partial_sort. std::partial_sort calls the
-// DownloadComparator with different pairs of DownloadItems.  DownloadComparator
-// iterates over the sorters until a callback returns ComparisonType LT or GT.
-// DownloadComparator returns true or false depending on that ComparisonType and
-// the sorter's direction in order to indicate to std::partial_sort whether the
-// left item is after or before the right item. If all sorters return EQ, then
-// DownloadComparator compares GetId. A DownloadQuery may have zero or more
-// Sorters, but there is one DownloadComparator per call to Search().
-
-struct DownloadQuery::Sorter {
-  using SortType = base::RepeatingCallback<ComparisonType(const DownloadItem&,
-                                                          const DownloadItem&)>;
-
-  template<typename ValueType>
-  static Sorter Build(DownloadQuery::SortDirection adirection,
-                         ValueType (*accessor)(const DownloadItem&)) {
-    return Sorter(adirection,
-                  base::BindRepeating(&Compare<ValueType>,
-                                      base::BindRepeating(accessor)));
-  }
-
-  Sorter(DownloadQuery::SortDirection adirection, const SortType& asorter)
-      : direction(adirection), sorter(asorter) {}
-  ~Sorter() = default;
-
-  DownloadQuery::SortDirection direction;
-  SortType sorter;
-};
-
-class DownloadQuery::DownloadComparator {
- public:
-  explicit DownloadComparator(const DownloadQuery::SorterVector& terms)
-    : terms_(terms) {
-  }
-
-  // Returns true if |left| sorts before |right|.
-  bool operator() (const DownloadItem* left, const DownloadItem* right);
-
- private:
-  const DownloadQuery::SorterVector& terms_;
-
-  // std::sort requires this class to be copyable.
-};
-
-bool DownloadQuery::DownloadComparator::operator() (
-    const DownloadItem* left, const DownloadItem* right) {
-  for (auto term = terms_.begin(); term != terms_.end(); ++term) {
-    switch (term->sorter.Run(*left, *right)) {
-      case LT: return term->direction == DownloadQuery::ASCENDING;
-      case GT: return term->direction == DownloadQuery::DESCENDING;
-      case EQ: break;  // break the switch but not the loop
-    }
-  }
-  CHECK_NE(left->GetId(), right->GetId());
-  return left->GetId() < right->GetId();
-}
-
 void DownloadQuery::AddSorter(DownloadQuery::SortType type,
                               DownloadQuery::SortDirection direction) {
   switch (type) {
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 b6170a5..594d2ff 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
@@ -27,11 +27,6 @@
 namespace enterprise_reporting {
 namespace {
 
-bool IsRequestInDict(const std::string& extension_id,
-                     const base::Value* requests) {
-  return requests->FindKey(extension_id) != nullptr;
-}
-
 // Create the ExtensionsWorkflowEvent based on the |extension_id| and
 // |request_data|. |request_data| is nullptr for remove-request and used for
 // add-request.
@@ -102,12 +97,12 @@
   std::string webstore_update_url =
       extension_urls::GetDefaultWebstoreUpdateUrl().spec();
 
-  const base::Value* pending_requests =
-      profile->GetPrefs()->GetDictionary(prefs::kCloudExtensionRequestIds);
-  const base::Value* uploaded_requests =
-      profile->GetPrefs()->GetDictionary(kCloudExtensionRequestUploadedIds);
+  const base::Value::Dict& pending_requests =
+      profile->GetPrefs()->GetValueDict(prefs::kCloudExtensionRequestIds);
+  const base::Value::Dict& uploaded_requests =
+      profile->GetPrefs()->GetValueDict(kCloudExtensionRequestUploadedIds);
 
-  for (auto it : pending_requests->DictItems()) {
+  for (auto it : pending_requests) {
     const std::string& extension_id = it.first;
     if (!ShouldUploadExtensionRequest(extension_id, webstore_update_url,
                                       extension_management)) {
@@ -115,18 +110,18 @@
     }
 
     // Request has already been uploaded.
-    if (IsRequestInDict(extension_id, uploaded_requests))
+    if (uploaded_requests.contains(extension_id))
       continue;
 
     reports.push_back(
         GenerateReport(extension_id, /*request_data=*/&it.second));
   }
 
-  for (auto it : uploaded_requests->DictItems()) {
+  for (auto it : uploaded_requests) {
     const std::string& extension_id = it.first;
 
     // Request is still pending, no need to send remove request.
-    if (IsRequestInDict(extension_id, pending_requests))
+    if (pending_requests.contains(extension_id))
       continue;
 
     reports.push_back(GenerateReport(extension_id, /*request_data=*/nullptr));
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index ee3f137d..fffbced 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -72,7 +72,6 @@
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/web_applications/web_app_utils.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/extensions/api/management/management_api_unittest.cc b/chrome/browser/extensions/api/management/management_api_unittest.cc
index 8d5f31f..f7078e2 100644
--- a/chrome/browser/extensions/api/management/management_api_unittest.cc
+++ b/chrome/browser/extensions/api/management/management_api_unittest.cc
@@ -47,8 +47,6 @@
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_test_util.h"
 #include "content/public/browser/gpu_data_manager.h"
-#include "extensions/browser/api/management/management_api_constants.h"
-#include "extensions/common/error_utils.h"
 #endif
 
 using extensions::mojom::ManifestLocation;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 0f4f148..b637991 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -58,7 +58,6 @@
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #else
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
 #endif
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index c26b0cb..4771abf0 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -27,7 +27,6 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/webui/os_feedback_ui/url_constants.h"
 #include "base/bind.h"
-#include "base/strings/escape.h"
 #include "base/strings/strcat.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h"
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index f0aade0..9275f104 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -41,7 +41,6 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/components/settings/cros_settings_names.h"
 #include "ash/public/ash_interfaces.h"
-#include "base/strings/stringprintf.h"
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/policy/arc_policy_bridge.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
diff --git a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc
index 87d7e8ee..7f9484cb 100644
--- a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc
@@ -18,7 +18,6 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
 #include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #endif
 
 namespace system_logs {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8ca45ef..8d85039 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3352,7 +3352,7 @@
   {
     "name": "fast-pair",
     "owners": [ "//ash/quick_pair/OWNERS", "jonmann@chromium.org" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 109
   },
   {
     "name": "fast-pair-low-power",
@@ -3360,17 +3360,17 @@
       "//ash/quick_pair/OWNERS",
       "jonmann@chromium.org"
     ],
-    "expiry_milestone": 105
+    "expiry_milestone": 109
   },
   {
     "name": "fast-pair-saved-devices",
     "owners": [ "//ash/quick_pair/OWNERS", "julietlevesque@google.com" ],
-    "expiry_milestone": 108
+    "expiry_milestone": 109
   },
   {
     "name": "fast-pair-software-scanning",
     "owners": [ "//ash/quick_pair/OWNERS", "jonmann@chromium.org" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 109
   },
   {
     "name": "fast-pair-subsequent-pairing-ux",
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
index cc7720c..f7d0ff8 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
@@ -80,17 +80,14 @@
     return;
 
   DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
-  auto* origin_id_dict = update.Get();
-  origin_id_dict->SetKey(
-      kExpirableToken, base::TimeToValue(base::Time::Now() + kExpirationDelta));
+  auto& origin_id_dict = update->GetDict();
+  origin_id_dict.Set(kExpirableToken,
+                     base::TimeToValue(base::Time::Now() + kExpirationDelta));
 }
 
-void RemoveExpirableToken(base::Value* origin_id_dict) {
+void RemoveExpirableToken(base::Value::Dict& origin_id_dict) {
   DVLOG(3) << __func__;
-  DCHECK(origin_id_dict);
-  DCHECK(origin_id_dict->is_dict());
-
-  origin_id_dict->RemoveKey(kExpirableToken);
+  origin_id_dict.Remove(kExpirableToken);
 }
 
 // On devices that don't support per-application provisioning attempts to
@@ -101,7 +98,7 @@
 // application provisioning pre-provisioning is always allowed. If
 // |kExpirableToken| is expired or corrupt, it will be removed for privacy
 // reasons.
-bool CanPreProvision(base::Value* origin_id_dict) {
+bool CanPreProvision(base::Value::Dict& origin_id_dict) {
   DVLOG(3) << __func__;
 
   // On devices that support per-application provisioning, this is always true.
@@ -110,15 +107,11 @@
 
   // Device doesn't support per-application provisioning, so check if
   // "expirable_token" is still valid.
-  if (!origin_id_dict || !origin_id_dict->is_dict())
-    return false;
-
-  const base::Value* token_value =
-      origin_id_dict->FindKeyOfType(kExpirableToken, base::Value::Type::STRING);
+  const base::Value* token_value = origin_id_dict.Find(kExpirableToken);
   if (!token_value)
     return false;
 
-  absl::optional<base::Time> expiration_time = base::ValueToTime(token_value);
+  absl::optional<base::Time> expiration_time = base::ValueToTime(*token_value);
   if (!expiration_time) {
     RemoveExpirableToken(origin_id_dict);
     return false;
@@ -148,35 +141,29 @@
   DVLOG(3) << __func__;
 
   DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
-  auto* origin_id_dict = update.Get();
-  DCHECK(origin_id_dict->is_dict());
+  base::Value::Dict& origin_id_dict = update->GetDict();
 
-  base::Value* origin_ids = origin_id_dict->FindListKey(kOriginIds);
+  base::Value::List* origin_ids = origin_id_dict.FindList(kOriginIds);
   if (!origin_ids)
     return base::UnguessableToken::Null();
 
-  if (origin_ids->GetListDeprecated().empty())
+  if (origin_ids->empty())
     return base::UnguessableToken::Null();
 
-  auto first_entry = origin_ids->GetListDeprecated().begin();
+  auto first_entry = origin_ids->begin();
   absl::optional<base::UnguessableToken> result =
       base::ValueToUnguessableToken(*first_entry);
   if (!result)
     return base::UnguessableToken::Null();
 
-  origin_ids->EraseListIter(first_entry);
+  origin_ids->erase(first_entry);
   return *result;
 }
 
-void AddOriginId(base::Value* origin_id_dict,
+void AddOriginId(base::Value::Dict& origin_id_dict,
                  const base::UnguessableToken& origin_id) {
   DVLOG(3) << __func__;
-  DCHECK(origin_id_dict);
-  DCHECK(origin_id_dict->is_dict());
-
-  base::Value* origin_ids = origin_id_dict->FindListKey(kOriginIds);
-  if (!origin_ids)
-    origin_ids = origin_id_dict->SetKey(kOriginIds, base::ListValue());
+  base::Value::List* origin_ids = origin_id_dict.EnsureList(kOriginIds);
   origin_ids->Append(base::UnguessableTokenToValue(origin_id));
 }
 
@@ -379,7 +366,7 @@
   // On devices that need to, check that the user has recently requested
   // an origin ID. If not, then skip pre-provisioning on those devices.
   DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
-  if (!CanPreProvision(update.Get())) {
+  if (!CanPreProvision(update->GetDict())) {
     // Disable any network monitoring, if it exists.
     network_observer_.reset();
     return;
@@ -508,14 +495,14 @@
     pending_provisioned_origin_id_cbs_.pop();
   } else {
     DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
-    AddOriginId(update.Get(), origin_id.value());
+    AddOriginId(update->GetDict(), origin_id.value());
 
     // If we already have enough pre-provisioned origin IDs, we're done.
     // Stop watching for network change events.
     if (CountAvailableOriginIds(update->GetDict()) >=
         kMaxPreProvisionedOriginIds) {
       network_observer_.reset();
-      RemoveExpirableToken(update.Get());
+      RemoveExpirableToken(update->GetDict());
       is_provisioning_ = false;
       return;
     }
@@ -526,9 +513,8 @@
 }
 
 void MediaDrmOriginIdManager::RecordCountOfPreprovisionedOriginIds() {
-  int available_origin_ids = 0;
   const auto& pref = pref_service_->GetValueDict(kMediaDrmOriginIds);
-  available_origin_ids = CountAvailableOriginIds(pref);
+  int available_origin_ids = CountAvailableOriginIds(pref);
 
   if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported()) {
     UMA_HISTOGRAM_EXACT_LINEAR(
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 2ece070..06ba3f6d 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -138,11 +138,11 @@
   network::mojom::HttpAuthStaticParamsPtr auth_static_params =
       network::mojom::HttpAuthStaticParams::New();
 
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
   auth_static_params->gssapi_library_name =
       local_state->GetString(prefs::kGSSAPILibraryName);
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) &&
-        // !BUILDFLAG(IS_CHROMEOS_ASH)
+        // !BUILDFLAG(IS_CHROMEOS)
 
   return auth_static_params;
 }
@@ -188,14 +188,20 @@
       local_state->GetString(prefs::kAuthAndroidNegotiateAccountType);
 #endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // TODO: Use KerberosCredentialsManager to determine whether Kerberos is
-  // enabled instead of relying directly on the preference.
-  policy::BrowserPolicyConnectorAsh* connector =
-      g_browser_process->platform_part()->browser_policy_connector_ash();
+#if BUILDFLAG(IS_CHROMEOS)
   auth_dynamic_params->allow_gssapi_library_load =
-      connector->IsActiveDirectoryManaged() ||
       local_state->GetBoolean(prefs::kKerberosEnabled);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
+// TODO(crbug.com/1295308): Remove the following check after Chromad is
+// deprecated.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (!auth_dynamic_params->allow_gssapi_library_load) {
+    policy::BrowserPolicyConnectorAsh* connector =
+        g_browser_process->platform_part()->browser_policy_connector_ash();
+    auth_dynamic_params->allow_gssapi_library_load =
+        connector->IsActiveDirectoryManaged();
+  }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return auth_dynamic_params;
@@ -492,11 +498,9 @@
                              auth_pref_callback);
 #endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // TODO: Use KerberosCredentialsManager::Observer to be notified of when the
-  // enabled state changes instead of relying directly on the preference.
+#if BUILDFLAG(IS_CHROMEOS)
   pref_change_registrar_.Add(prefs::kKerberosEnabled, auth_pref_callback);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   local_state_->SetDefaultPrefValue(
       prefs::kEnableReferrers,
@@ -531,10 +535,10 @@
   // Static auth params
   registry->RegisterStringPref(prefs::kAuthSchemes,
                                "basic,digest,ntlm,negotiate");
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
   registry->RegisterStringPref(prefs::kGSSAPILibraryName, std::string());
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) &&
-        // !BUILDFLAG(IS_CHROMEOS_ASH)
+        // !BUILDFLAG(IS_CHROMEOS)
 
   // Dynamic auth params.
   registry->RegisterListPref(prefs::kAllHttpAuthSchemesAllowedForOrigins,
@@ -545,6 +549,13 @@
   registry->RegisterStringPref(prefs::kAuthServerAllowlist, std::string());
   registry->RegisterStringPref(prefs::kAuthNegotiateDelegateAllowlist,
                                std::string());
+
+// On ChromeOS Ash, the pref below is registered by the
+// `KerberosCredentialsManager`.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  registry->RegisterBooleanPref(prefs::kKerberosEnabled, false);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
   registry->RegisterBooleanPref(prefs::kAuthNegotiateDelegateByKdcPolicy,
                                 false);
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index c73337ac..9fe30dba 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -71,7 +71,7 @@
   network::mojom::HttpAuthStaticParamsPtr static_params =
       SystemNetworkContextManager::GetHttpAuthStaticParamsForTesting();
   EXPECT_EQ("", static_params->gssapi_library_name);
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
   // Test that prefs are reflected in params.
 
   PrefService* local_state = g_browser_process->local_state();
@@ -81,7 +81,7 @@
       SystemNetworkContextManager::GetHttpAuthStaticParamsForTesting();
   EXPECT_EQ(dev_null, static_params->gssapi_library_name);
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) &&
-        // !BUILDFLAG(IS_CHROMEOS_ASH)
+        // !BUILDFLAG(IS_CHROMEOS)
 }
 
 IN_PROC_BROWSER_TEST_F(SystemNetworkContextManagerBrowsertest, AuthParams) {
@@ -193,7 +193,7 @@
   EXPECT_TRUE(dynamic_params->patterns_allowed_to_use_all_schemes.empty());
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // The kerberos.enabled pref is false and the device is not Active Directory
   // managed by default.
   EXPECT_FALSE(dynamic_params->allow_gssapi_library_load);
@@ -202,7 +202,7 @@
       SystemNetworkContextManager::GetHttpAuthDynamicParamsForTesting();
   EXPECT_TRUE(dynamic_params->allow_gssapi_library_load);
   EXPECT_TRUE(dynamic_params->patterns_allowed_to_use_all_schemes.empty());
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   base::Value patterns_allowed_to_use_all_schemes(base::Value::Type::LIST);
   patterns_allowed_to_use_all_schemes.Append(
diff --git a/chrome/browser/optimization_guide/android/android_push_notification_manager.cc b/chrome/browser/optimization_guide/android/android_push_notification_manager.cc
deleted file mode 100644
index 08ab91f..0000000
--- a/chrome/browser/optimization_guide/android/android_push_notification_manager.cc
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/optimization_guide/android/android_push_notification_manager.h"
-
-#include <string>
-
-#include "base/barrier_closure.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/containers/flat_set.h"
-#include "base/metrics/histogram_functions.h"
-#include "chrome/browser/optimization_guide/android/optimization_guide_bridge.h"
-#include "components/optimization_guide/core/push_notification_manager.h"
-#include "url/gurl.h"
-
-namespace optimization_guide {
-namespace android {
-
-namespace {
-
-// All of the code within OptimizationGuideStore uses OnceClosure's and trying
-// to insert the usage of a <void(bool)> callback to indicate success/failure is
-// not worth the effort. This class is a workaround.
-//
-// The pattern of the PushNotificationManager is to run a OnceClosure when an
-// action succeeds, and to have the closure not be run on failure. When the
-// closure is not run, any objects that are owned by the callback (like a
-// unique_ptr that is passed to the callback-invoked method) are destroyed.
-//
-// This helper takes advantage of that destruction to detect when an action has
-// failed because the callback was never run and then dropped out of scope. When
-// this class is destroyed, if the given |on_failure| closure has not be reset,
-// then it is run. Calling |Disarm| will reset the failure callback, so when the
-// async action succeeds, then |Disarm| should be called before |this| can be
-// destroyed.
-//
-//
-// For example:
-//
-// void DisarmHelperOnSuccess(
-//        unique_ptr<DroppedSuccessCallbackHelper> helper,
-//        base::OnceClosure internal_on_success) {
-//    helper->Disarm();
-//    std::move(internal_on_success).Run();
-// }
-//
-// base::OnceClosure internal_on_failure =
-//    base::BindOnce(&MyClass::HandleFailure);
-// base::OnceClosure internal_on_success =
-//    base::BindOnce(&MyClass::HandleSuccess);
-//
-// unique_ptr<DroppedSuccessCallbackHelper> helper =
-//    CreateAndArm(std::move(internal_on_failure));
-//
-// base::OnceClosure call_me_on_success_else_destroy =
-//    base::BindOnce(&DisarmHelperOnSuccess,
-//                    std::move(helper),
-//                    std::move(internal_on_success));
-class DroppedSuccessCallbackHelper {
- public:
-  static std::unique_ptr<DroppedSuccessCallbackHelper> CreateAndArm(
-      base::OnceClosure on_failure) {
-    return std::make_unique<DroppedSuccessCallbackHelper>(
-        std::move(on_failure));
-  }
-
-  explicit DroppedSuccessCallbackHelper(base::OnceClosure on_failure)
-      : on_failure_(std::move(on_failure)) {}
-  ~DroppedSuccessCallbackHelper() {
-    bool did_succeed = !on_failure_;
-
-    if (report_result_to_boolean_histogram_) {
-      base::UmaHistogramBoolean(*report_result_to_boolean_histogram_,
-                                did_succeed);
-    }
-
-    if (!did_succeed) {
-      std::move(on_failure_).Run();
-    }
-  }
-
-  void SetReportResultHistogram(const std::string& histogram) {
-    report_result_to_boolean_histogram_ = histogram;
-  }
-
-  void Disarm() { on_failure_.Reset(); }
-
- private:
-  base::OnceClosure on_failure_;
-  absl::optional<std::string> report_result_to_boolean_histogram_;
-};
-
-// Called on success of an action to disarm the helper.
-void SimpleDisarmHelper(std::unique_ptr<DroppedSuccessCallbackHelper> helper) {
-  helper->Disarm();
-}
-
-// Called when all the push notifications for the given optimization type have
-// been processed. This clears the Android cache and disarms the helper.
-void OnOptimizationTypeHandled(
-    std::unique_ptr<DroppedSuccessCallbackHelper> helper,
-    proto::OptimizationType opt_type) {
-  helper->Disarm();
-  OptimizationGuideBridge::ClearCacheForOptimizationType(opt_type);
-}
-
-class ScopedBooleanHistogramRecorder {
- public:
-  explicit ScopedBooleanHistogramRecorder(const std::string& histogram_name)
-      : histogram_name_(histogram_name) {}
-  ~ScopedBooleanHistogramRecorder() {
-    base::UmaHistogramBoolean(histogram_name_, sample_);
-  }
-
-  void SetSample(bool sample) { sample_ = sample; }
-
- private:
-  const std::string histogram_name_;
-  bool sample_ = false;
-};
-
-}  // namespace
-
-AndroidPushNotificationManager::~AndroidPushNotificationManager() = default;
-AndroidPushNotificationManager::AndroidPushNotificationManager(
-    PrefService* pref_service)
-    : pref_service_(pref_service) {
-  DCHECK(pref_service_);
-}
-
-void AndroidPushNotificationManager::OnDelegateReady() {
-  PushNotificationManager::OnDelegateReady();
-  // Quickly check that nothing overflowed. That way we don't risk some
-  // notifications being processed just before a purge sweeps everything out.
-  base::flat_set<proto::OptimizationType> overflowed_types =
-      OptimizationGuideBridge::GetOptTypesThatOverflowedPushNotifications();
-  bool did_overflow = !overflowed_types.empty();
-  base::UmaHistogramBoolean("OptimizationGuide.PushNotifications.DidOverflow",
-                            did_overflow);
-  if (did_overflow) {
-    OnNeedToPurgeStore();
-    return;
-  }
-
-  size_t cached_notifications_total = 0;
-  for (proto::OptimizationType opt_type :
-       OptimizationGuideBridge::GetOptTypesWithPushNotifications()) {
-    std::vector<proto::HintNotificationPayload> notifications =
-        OptimizationGuideBridge::GetCachedNotifications(opt_type);
-    cached_notifications_total += notifications.size();
-
-    // The delegate expects to only get one type of a key representation at a
-    // time, so separate those out.
-    std::map<proto::KeyRepresentation, base::flat_set<std::string>>
-        hints_keys_by_key_rep;
-    for (const proto::HintNotificationPayload& notification : notifications) {
-      if (!notification.has_hint_key())
-        continue;
-      if (!notification.has_key_representation())
-        continue;
-
-      if (hints_keys_by_key_rep.find(notification.key_representation()) ==
-          hints_keys_by_key_rep.end()) {
-        hints_keys_by_key_rep.emplace(notification.key_representation(),
-                                      base::flat_set<std::string>{});
-      }
-      hints_keys_by_key_rep.find(notification.key_representation())
-          ->second.emplace(notification.hint_key());
-    }
-
-    if (hints_keys_by_key_rep.empty()) {
-      continue;
-    }
-
-    // The helper here is used only for tracking success and logging that to
-    // metrics. In this case, nothing needs to be done in the event of failure.
-    auto helper = DroppedSuccessCallbackHelper::CreateAndArm(base::DoNothing());
-    helper->SetReportResultHistogram(
-        "OptimizationGuide.PushNotifications."
-        "CachedNotificationsHandledSuccessfully");
-
-    // The barrier closure will once run the given once closure after it is run
-    // |hints_keys_by_key_rep.size()| times.
-    base::RepeatingClosure barrier =
-        base::BarrierClosure(hints_keys_by_key_rep.size(),
-                             base::BindOnce(&OnOptimizationTypeHandled,
-                                            std::move(helper), opt_type));
-    for (const auto& pair : hints_keys_by_key_rep) {
-      delegate_->RemoveFetchedEntriesByHintKeys(barrier, pair.first,
-                                                pair.second);
-    }
-  }
-
-  base::UmaHistogramCounts100(
-      "OptimizationGuide.PushNotifications.CachedNotificationCount",
-      cached_notifications_total);
-}
-
-void AndroidPushNotificationManager::OnNeedToPurgeStore() {
-  DCHECK(delegate_);
-
-  delegate_->PurgeFetchedEntries(
-      base::BindOnce(&AndroidPushNotificationManager::OnPurgeCompleted,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void AndroidPushNotificationManager::OnNewPushNotification(
-    const proto::HintNotificationPayload& notification) {
-  base::UmaHistogramBoolean(
-      "OptimizationGuide.PushNotifications.GotPushNotification", true);
-
-  if (!delegate_) {
-    // Cache the notification into Android shared preference.
-    OnNewPushNotificationNotHandled(notification);
-    return;
-  }
-
-  if (!notification.has_hint_key())
-    return;
-
-  if (!notification.has_key_representation())
-    return;
-
-  PushNotificationManager::OnNewPushNotification(notification);
-  InvalidateHints(notification);
-}
-
-void AndroidPushNotificationManager::InvalidateHints(
-    const proto::HintNotificationPayload& notification) {
-  // If the notification can't be handled right now, make sure it gets pushed
-  // back to Android to be cached.
-  auto helper = DroppedSuccessCallbackHelper::CreateAndArm(base::BindOnce(
-      &AndroidPushNotificationManager::OnNewPushNotificationNotHandled,
-      weak_ptr_factory_.GetWeakPtr(), notification));
-
-  helper->SetReportResultHistogram(
-      "OptimizationGuide.PushNotifications."
-      "PushNotificationHandledSuccessfully");
-
-  delegate_->RemoveFetchedEntriesByHintKeys(
-      base::BindOnce(&SimpleDisarmHelper, std::move(helper)),
-      notification.key_representation(), {notification.hint_key()});
-}
-
-void AndroidPushNotificationManager::OnPurgeCompleted() {
-  for (int int_opt_type = proto::OptimizationType_MIN;
-       int_opt_type <= proto::OptimizationType_MAX; int_opt_type++) {
-    OptimizationGuideBridge::ClearCacheForOptimizationType(
-        static_cast<proto::OptimizationType>(int_opt_type));
-  }
-}
-
-void AndroidPushNotificationManager::OnNewPushNotificationNotHandled(
-    const proto::HintNotificationPayload& notification) {
-  OptimizationGuideBridge::OnNotificationNotHandledByNative(notification);
-}
-
-}  // namespace android
-}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/android/android_push_notification_manager.h b/chrome/browser/optimization_guide/android/android_push_notification_manager.h
deleted file mode 100644
index 500ae943..0000000
--- a/chrome/browser/optimization_guide/android/android_push_notification_manager.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_OPTIMIZATION_GUIDE_ANDROID_ANDROID_PUSH_NOTIFICATION_MANAGER_H_
-#define CHROME_BROWSER_OPTIMIZATION_GUIDE_ANDROID_ANDROID_PUSH_NOTIFICATION_MANAGER_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/core/push_notification_manager.h"
-#include "components/optimization_guide/proto/hints.pb.h"
-#include "components/optimization_guide/proto/push_notification.pb.h"
-
-class PrefService;
-
-namespace optimization_guide {
-namespace android {
-
-// This class manages incoming push notifications from Android and any cached
-// notifications that were received while native was not alive. Note that the
-// only handling that occurs with push notifications is that any previously
-// fetched hint (that matches the pushed notification) should be removed from
-// in-memory and persistent stores within optimization guide.
-//
-// Cached notification handling:
-// In the Android code, cached notifications are sharded by optimization type.
-// Once all the notifications for a given optimization type have been handled,
-// the optimization type's cache can be cleared, but this must be explicitly
-// requested by native code.
-// Memory Management/Overflow: Each optimization type's cache of notifications
-// has a maximum size which can overflow. In this
-// case, all the cached notifications are lost and the Android layer only
-// reports that there was an overflow. When this happens, the entire hint store
-// is purged (this should not happen very often).
-//
-// Push notifications:
-// When a notification arrives while native is alive, it is attempted to be
-// handled. In the event that backing stores or otherwise are not available when
-// the notification arrives, it can be pushed back to the Android layer to be
-// cached.
-class AndroidPushNotificationManager : public PushNotificationManager {
- public:
-  explicit AndroidPushNotificationManager(PrefService* pref_service);
-  ~AndroidPushNotificationManager() override;
-
-  // PushNotificationManager implementation:
-  void OnDelegateReady() override;
-  void OnNewPushNotification(
-      const proto::HintNotificationPayload& notification) override;
-
- private:
-  // Called when the store needs to be purged because there was a cache
-  // overflow.
-  void OnNeedToPurgeStore();
-  void OnPurgeCompleted();
-
-  // Invalidates hints data based on hints keys in |notification| proto.
-  void InvalidateHints(const proto::HintNotificationPayload& notification);
-
-  // Called when a non-cached notification can't be processed right now so it
-  // should be cached in Android.
-  void OnNewPushNotificationNotHandled(
-      const proto::HintNotificationPayload& notification);
-
-  // Not owned, but expected to outlive |this|.
-  raw_ptr<PrefService> pref_service_ = nullptr;
-
-  base::WeakPtrFactory<AndroidPushNotificationManager> weak_ptr_factory_{this};
-};
-
-}  // namespace android
-}  // namespace optimization_guide
-
-#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_ANDROID_ANDROID_PUSH_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc b/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc
deleted file mode 100644
index 23d9f85..0000000
--- a/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc
+++ /dev/null
@@ -1,919 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/optimization_guide/android/android_push_notification_manager.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
-#include "base/containers/contains.h"
-#include "base/memory/raw_ptr.h"
-#include "base/strings/strcat.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/optimization_guide/android/native_j_unittests_jni_headers/OptimizationGuidePushNotificationTestHelper_jni.h"
-#include "chrome/browser/optimization_guide/android/optimization_guide_bridge.h"
-#include "chrome/browser/optimization_guide/chrome_hints_manager.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.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/optimization_guide/core/hint_cache.h"
-#include "components/optimization_guide/core/hints_fetcher.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_prefs.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::SaveArg;
-
-namespace optimization_guide {
-namespace android {
-
-class TestDelegate : public PushNotificationManager::Delegate {
- public:
-  using RemoveMultiplePair =
-      std::pair<proto::KeyRepresentation, base::flat_set<std::string>>;
-
-  TestDelegate() = default;
-  ~TestDelegate() = default;
-
-  void SetRunSuccessCallbacks(bool run_success_callbacks) {
-    run_success_callbacks_ = run_success_callbacks;
-  }
-
-  const RemoveMultiplePair& last_remove_many() const {
-    return last_remove_many_;
-  }
-
-  const std::vector<RemoveMultiplePair>& all_remove_multiples() const {
-    return all_remove_multiples_;
-  }
-
-  bool did_call_purge() const { return did_call_purge_; }
-
-  void RemoveFetchedEntriesByHintKeys(
-      base::OnceClosure on_success,
-      proto::KeyRepresentation key_representation,
-      const base::flat_set<std::string>& hint_keys) override {
-    last_remove_many_ = std::make_pair(key_representation, hint_keys);
-    all_remove_multiples_.push_back(last_remove_many_);
-    if (run_success_callbacks_)
-      std::move(on_success).Run();
-  }
-
-  void PurgeFetchedEntries(base::OnceClosure on_success) override {
-    did_call_purge_ = true;
-    if (run_success_callbacks_)
-      std::move(on_success).Run();
-  }
-
- private:
-  RemoveMultiplePair last_remove_many_;
-  std::vector<RemoveMultiplePair> all_remove_multiples_;
-  bool did_call_purge_ = false;
-  bool run_success_callbacks_ = true;
-};
-
-class MockObserver : public PushNotificationManager::Observer {
- public:
-  MockObserver() = default;
-  ~MockObserver() override = default;
-  MOCK_METHOD(void,
-              OnNotificationPayload,
-              (proto::OptimizationType, const optimization_guide::proto::Any&),
-              (override));
-};
-
-const int kOverflowSize = 5;
-
-class AndroidPushNotificationManagerJavaTest : public testing::Test {
- public:
-  AndroidPushNotificationManagerJavaTest()
-      : j_test_(Java_OptimizationGuidePushNotificationTestHelper_Constructor(
-            base::android::AttachCurrentThread())),
-        env_(base::android::AttachCurrentThread()),
-        profile_manager_(TestingBrowserProcess::GetGlobal()) {
-    scoped_feature_list_.InitAndEnableFeature(
-        optimization_guide::features::kPushNotifications);
-  }
-  ~AndroidPushNotificationManagerJavaTest() override = default;
-
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ASSERT_TRUE(profile_manager_.SetUp(temp_dir_.GetPath()));
-    profile_ = profile_manager_.CreateTestingProfile(chrome::kInitialProfile);
-
-    Java_OptimizationGuidePushNotificationTestHelper_setUpMocks(env_, j_test_);
-
-    service_ = static_cast<OptimizationGuideKeyedService*>(
-        OptimizationGuideKeyedServiceFactory::GetInstance()
-            ->SetTestingFactoryAndUse(
-                profile(),
-                base::BindRepeating(&AndroidPushNotificationManagerJavaTest::
-                                        CreateServiceForProfile,
-                                    base::Unretained(this))));
-    service_->GetHintsManager()->push_notification_manager()->AddObserver(
-        &observer_);
-
-    // It takes two session starts for experimental params and feature flags to
-    // be picked up by Java, so override them manually.
-    Java_OptimizationGuidePushNotificationTestHelper_setOverflowSizeForTesting(
-        env_, kOverflowSize);
-    Java_OptimizationGuidePushNotificationTestHelper_setFeatureEnabled(env_);
-  }
-
-  void TearDown() override {
-    Java_OptimizationGuidePushNotificationTestHelper_clearAllCaches(env_);
-  }
-
-  std::unique_ptr<KeyedService> CreateServiceForProfile(
-      content::BrowserContext* browser_context) {
-    return std::make_unique<OptimizationGuideKeyedService>(
-        Profile::FromBrowserContext(browser_context));
-  }
-
-  void PushNotificationNative(
-      const proto::HintNotificationPayload& notification) {
-    std::string encoded_notification;
-    notification.SerializeToString(&encoded_notification);
-
-    OptimizationGuideBridge bridge(service());
-    bridge.OnNewPushNotification(
-        env_, base::android::ToJavaByteArray(env_, encoded_notification));
-  }
-
-  bool PushNotificationJava(
-      const proto::HintNotificationPayload& notification) {
-    std::string encoded_notification;
-    if (!notification.SerializeToString(&encoded_notification))
-      return false;
-
-    return Java_OptimizationGuidePushNotificationTestHelper_pushNotification(
-        env_, base::android::ToJavaByteArray(env_, encoded_notification));
-  }
-
-  void CauseOverflow(proto::OptimizationType opt_type) {
-    for (int i = 0; i < kOverflowSize + 1; i++) {
-      SCOPED_TRACE(i);
-      proto::HintNotificationPayload notification;
-      notification.set_hint_key("hint_" + base::NumberToString(i));
-      notification.set_optimization_type(opt_type);
-      notification.set_key_representation(proto::KeyRepresentation::HOST);
-      ASSERT_TRUE(CacheNotification(notification));
-    }
-  }
-
-  bool CacheNotification(const proto::HintNotificationPayload& notification) {
-    std::string encoded_notification;
-    if (!notification.SerializeToString(&encoded_notification))
-      return false;
-
-    return Java_OptimizationGuidePushNotificationTestHelper_cacheNotification(
-        env_, base::android::ToJavaByteArray(env_, encoded_notification));
-  }
-
-  bool DidOverflow(proto::OptimizationType opt_type) {
-    return Java_OptimizationGuidePushNotificationTestHelper_didOverflow(
-        env_, static_cast<int>(opt_type));
-  }
-
-  size_t AndroidNotificationCacheSize(proto::OptimizationType opt_type) {
-    return Java_OptimizationGuidePushNotificationTestHelper_countCachedNotifications(
-        env_, static_cast<int>(opt_type));
-  }
-
-  OptimizationGuideKeyedService* service() { return service_; }
-
-  optimization_guide::ChromeHintsManager* hints_manager() {
-    return service()->GetHintsManager();
-  }
-
-  MockObserver* observer() { return &observer_; }
-
-  JNIEnv* env() { return env_; }
-
-  TestingProfile* profile() { return profile_; }
-
-  PrefService* prefs() { return profile()->GetPrefs(); }
-
- private:
-  content::BrowserTaskEnvironment task_environment_{
-      base::test::TaskEnvironment::MainThreadType::UI};
-  base::android::ScopedJavaGlobalRef<jobject> j_test_;
-  raw_ptr<JNIEnv> env_;
-  TestingProfileManager profile_manager_;
-  raw_ptr<TestingProfile> profile_;
-  MockObserver observer_;
-  raw_ptr<OptimizationGuideKeyedService> service_;
-  base::ScopedTempDir temp_dir_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       SingleCachedNotification_SuccessCallback) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(CacheNotification(notification));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-  const auto& last_remove_many = delegate.last_remove_many();
-  EXPECT_EQ(proto::KeyRepresentation::HOST, last_remove_many.first);
-  EXPECT_EQ(base::flat_set<std::string>{"hintkey"}, last_remove_many.second);
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.CachedNotificationCount", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.DidOverflow", false, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications."
-      "CachedNotificationsHandledSuccessfully",
-      true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       Cached_SingleNotification_FailedCallback) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(false);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(CacheNotification(notification));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-  const auto& last_remove_many = delegate.last_remove_many();
-  EXPECT_EQ(proto::KeyRepresentation::HOST, last_remove_many.first);
-  EXPECT_EQ(base::flat_set<std::string>{"hintkey"}, last_remove_many.second);
-
-  // The callback wasn't run, indicating failure, so the notification should be
-  // put back into the Android cache.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications."
-      "CachedNotificationsHandledSuccessfully",
-      false, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, TwoCachedNotifications) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(CacheNotification(notification));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  proto::HintNotificationPayload notification2;
-  notification2.set_hint_key("hintkey2");
-  notification2.set_key_representation(proto::KeyRepresentation::HOST);
-  notification2.set_optimization_type(
-      proto::OptimizationType::RESOURCE_LOADING);
-  ASSERT_TRUE(CacheNotification(notification2));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::RESOURCE_LOADING));
-
-  manager.OnDelegateReady();
-  const auto& last_remove_many = delegate.last_remove_many();
-  EXPECT_EQ(proto::KeyRepresentation::HOST, last_remove_many.first);
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::RESOURCE_LOADING));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.CachedNotificationCount", 2, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.DidOverflow", false, 1);
-  // One sample is logged for each OptimizationType.
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications."
-      "CachedNotificationsHandledSuccessfully",
-      true, 2);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Cached_Overflow_HandledSuccess) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  CauseOverflow(proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-  EXPECT_TRUE(delegate.did_call_purge());
-
-  EXPECT_FALSE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.DidOverflow", true, 1);
-  histogram_tester.ExpectTotalCount(
-      "OptimizationGuide.PushNotifications."
-      "CachedNotificationsHandledSuccessfully",
-      0);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Cached_Overflow_HandledFailure) {
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(false);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  CauseOverflow(proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-  EXPECT_TRUE(delegate.did_call_purge());
-
-  EXPECT_TRUE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Cached_OverflowPurgesAllTypes) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload supported_notification;
-  supported_notification.set_hint_key("other hintkey");
-  supported_notification.set_key_representation(proto::KeyRepresentation::HOST);
-  supported_notification.set_optimization_type(
-      proto::OptimizationType::LINK_PERFORMANCE);
-  ASSERT_TRUE(CacheNotification(supported_notification));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::LINK_PERFORMANCE));
-
-  CauseOverflow(proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-  EXPECT_TRUE(delegate.did_call_purge());
-
-  // All types should be purged.
-  EXPECT_FALSE(DidOverflow(proto::OptimizationType::PERFORMANCE_HINTS));
-  EXPECT_FALSE(DidOverflow(proto::OptimizationType::LINK_PERFORMANCE));
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::LINK_PERFORMANCE));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.DidOverflow", true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, PushNotification_Success) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-
-  manager.OnNewPushNotification(notification);
-
-  // Because there was a delegate, the notification should not be cached.
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.PushNotificationHandledSuccessfully",
-      true, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.GotPushNotification", true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, PushNotification_Failure) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(false);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-
-  manager.OnNewPushNotification(notification);
-
-  // The notification should be cached because it was not handled successfully.
-  EXPECT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.PushNotificationHandledSuccessfully",
-      false, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.GotPushNotification", true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       PushNotificationCachedWhenNoDelegate) {
-  base::HistogramTester histogram_tester;
-  AndroidPushNotificationManager manager(prefs());
-
-  proto::HintNotificationPayload notification;
-  notification.set_hint_key("hintkey");
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-
-  manager.OnNewPushNotification(notification);
-
-  // Because there was not delegate, the notification should be cached.
-  EXPECT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  // But the same notification can be pulled with a delegate.
-  TestDelegate delegate;
-  manager.SetDelegate(&delegate);
-  manager.OnDelegateReady();
-  const auto& last_remove_many = delegate.last_remove_many();
-  EXPECT_EQ(proto::KeyRepresentation::HOST, last_remove_many.first);
-  EXPECT_EQ(base::flat_set<std::string>{"hintkey"}, last_remove_many.second);
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.GotPushNotification", true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       Cached_MultipleKeyRepresentations) {
-  base::HistogramTester histogram_tester;
-  TestDelegate delegate;
-  delegate.SetRunSuccessCallbacks(true);
-
-  AndroidPushNotificationManager manager(prefs());
-  manager.SetDelegate(&delegate);
-
-  proto::HintNotificationPayload host_notification;
-  host_notification.set_hint_key("host-key.com");
-  host_notification.set_key_representation(proto::KeyRepresentation::HOST);
-  host_notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(CacheNotification(host_notification));
-  ASSERT_EQ(1U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  proto::HintNotificationPayload url_notification;
-  url_notification.set_hint_key("http://url-key.com/page");
-  url_notification.set_key_representation(proto::KeyRepresentation::FULL_URL);
-  url_notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  ASSERT_TRUE(CacheNotification(url_notification));
-  ASSERT_EQ(2U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  manager.OnDelegateReady();
-
-  const std::vector<TestDelegate::RemoveMultiplePair>& multi_pair_removes =
-      delegate.all_remove_multiples();
-  EXPECT_EQ(2U, multi_pair_removes.size());
-  EXPECT_TRUE(base::Contains(
-      multi_pair_removes,
-      std::make_pair(proto::KeyRepresentation::HOST,
-                     base::flat_set<std::string>{"host-key.com"})));
-  EXPECT_TRUE(base::Contains(
-      multi_pair_removes,
-      std::make_pair(proto::KeyRepresentation::FULL_URL,
-                     base::flat_set<std::string>{"http://url-key.com/page"})));
-
-  EXPECT_EQ(0U, AndroidNotificationCacheSize(
-                    proto::OptimizationType::PERFORMANCE_HINTS));
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.CachedNotificationCount", 2, 1);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications."
-      "CachedNotificationsHandledSuccessfully",
-      true, 1);
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Pushed_URL_SuccessCase) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-  EXPECT_CALL(*observer(), OnNotificationPayload(_, _)).Times(0);
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_key_representation(proto::KeyRepresentation::FULL_URL);
-  notification.set_hint_key(url.spec());
-
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Pushed_Host_SuccessCase) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-  EXPECT_CALL(*observer(), OnNotificationPayload(_, _)).Times(0);
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_hint_key(url.host());
-
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, PushedJava_URL_SuccessCase) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-  EXPECT_CALL(*observer(), OnNotificationPayload(_, _)).Times(0);
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_key_representation(proto::KeyRepresentation::FULL_URL);
-  notification.set_hint_key(url.spec());
-
-  PushNotificationJava(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, PushedJava_Host_SuccessCase) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-  EXPECT_CALL(*observer(), OnNotificationPayload(_, _)).Times(0);
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  notification.set_hint_key(url.host());
-
-  PushNotificationJava(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       Pushed_KeyRepresentationRequired) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_hint_key(url.spec());
-
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       Pushed_OptimizationTypeNotRequired) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-
-  proto::HintNotificationPayload notification;
-  notification.set_key_representation(proto::KeyRepresentation::FULL_URL);
-  notification.set_hint_key(url.spec());
-
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-TEST_F(AndroidPushNotificationManagerJavaTest, Pushed_HintKeyRequired) {
-  // Pre-populate the store with some hints.
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("page/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(
-      proto::OptimizationType::PERFORMANCE_HINTS);
-  notification.set_key_representation(proto::KeyRepresentation::FULL_URL);
-
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
-// Send non-empty HintNotificationPayload.payload and valid hints data in the
-// HintNotificationPayload proto, observer should receive
-// the optimization_guide::proto::Any.
-TEST_F(AndroidPushNotificationManagerJavaTest, PayloadDispatched_WithHintsKey) {
-  base::HistogramTester histogram_tester;
-  optimization_guide::proto::Any payload_to_observer;
-  EXPECT_CALL(*observer(),
-              OnNotificationPayload(proto::OptimizationType::PRICE_TRACKING, _))
-      .WillOnce(SaveArg<1>(&payload_to_observer));
-
-  proto::HintNotificationPayload notification;
-  notification.set_optimization_type(proto::OptimizationType::PRICE_TRACKING);
-  notification.set_key_representation(proto::KeyRepresentation::HOST);
-  GURL url("https://host.com/r/cats");
-  notification.set_hint_key(url.host());
-  optimization_guide::proto::Any* payload = new optimization_guide::proto::Any;
-  payload->set_type_url("type_url");
-  payload->set_value("value");
-  notification.set_allocated_payload(payload);
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(payload_to_observer.type_url(), "type_url");
-  EXPECT_EQ(payload_to_observer.value(), "value");
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.ReceivedNotificationType",
-      proto::OptimizationType::PRICE_TRACKING, 1);
-}
-
-// Send non-empty HintNotificationPayload.payload and empty hints key in the
-// HintNotificationPayload proto, observer should not receive
-// optimization_guide::proto::Any.
-TEST_F(AndroidPushNotificationManagerJavaTest,
-       PayloadNotDispatched_InvalidPayload) {
-  base::HistogramTester histogram_tester;
-  EXPECT_CALL(*observer(), OnNotificationPayload(_, _)).Times(0);
-
-  // No hints key.
-  proto::HintNotificationPayload notification;
-  optimization_guide::proto::Any* payload = new optimization_guide::proto::Any;
-  notification.set_optimization_type(proto::OptimizationType::PRICE_TRACKING);
-  payload->set_type_url("type_url");
-  payload->set_value("value");
-  notification.set_allocated_payload(payload);
-  PushNotificationNative(notification);
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.PushNotifications.ReceivedNotificationType",
-      proto::OptimizationType::PRICE_TRACKING, 0);
-}
-
-}  // namespace android
-}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/chrome_hints_manager.cc b/chrome/browser/optimization_guide/chrome_hints_manager.cc
index a94a864..9f951c5 100644
--- a/chrome/browser/optimization_guide/chrome_hints_manager.cc
+++ b/chrome/browser/optimization_guide/chrome_hints_manager.cc
@@ -19,10 +19,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/optimization_guide/android/android_push_notification_manager.h"
-#endif
-
 namespace {
 
 // Returns true if we can make a request for hints for |prediction|.
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index f52eb9b..bd928f4 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -53,7 +53,6 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h"
-#include "chrome/browser/optimization_guide/android/android_push_notification_manager.h"
 #include "chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h"
 #else
 #include "chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h"
@@ -151,19 +150,13 @@
 OptimizationGuideKeyedService::MaybeCreatePushNotificationManager(
     Profile* profile) {
   if (optimization_guide::features::IsPushNotificationsEnabled()) {
-// TODO(crbug.com/1347657) use PushNotificationManager for Android.
-// OptimizationGuideBridge specific pieces of AndroidPushNotificationManager
-// were for Chime which went unused.
+    auto push_notification_manager =
+        std::make_unique<optimization_guide::PushNotificationManager>();
 #if BUILDFLAG(IS_ANDROID)
-    auto push_notification_manager = std::make_unique<
-        optimization_guide::android::AndroidPushNotificationManager>(
-        profile->GetPrefs());
     push_notification_manager->AddObserver(
         PriceTrackingNotificationBridge::GetForBrowserContext(profile));
-    return push_notification_manager;
-#else
-    return std::make_unique<optimization_guide::PushNotificationManager>();
 #endif
+    return push_notification_manager;
   }
   return nullptr;
 }
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index 960df862..dc7525c 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -32,7 +32,6 @@
 
 namespace optimization_guide {
 namespace android {
-class AndroidPushNotificationManagerJavaTest;
 class OptimizationGuideBridge;
 }  // namespace android
 class ChromeHintsManager;
@@ -143,8 +142,6 @@
   friend class OptimizationGuideWebContentsObserver;
   friend class optimization_guide::PredictionModelDownloadClient;
   friend class optimization_guide::PredictionManagerBrowserTestBase;
-  friend class optimization_guide::android::
-      AndroidPushNotificationManagerJavaTest;
   friend class optimization_guide::android::OptimizationGuideBridge;
 
   // Initializes |this|.
diff --git a/chrome/browser/plugins/plugin_prefs.cc b/chrome/browser/plugins/plugin_prefs.cc
index 81784a5d..471b6675 100644
--- a/chrome/browser/plugins/plugin_prefs.cc
+++ b/chrome/browser/plugins/plugin_prefs.cc
@@ -123,7 +123,7 @@
         }
         base::Value::Dict& plugin = plugin_value.GetDict();
 
-        // The plugin list constains all the plugin files in addition to the
+        // The plugin list contains all the plugin files in addition to the
         // plugin groups.
         if (const std::string* path = plugin.FindString("path")) {
           // Files have a path attribute, groups don't.
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 2cc128d1f..c6c856b 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -817,6 +817,12 @@
     base::Value::Type::STRING },
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_CHROMEOS)
+  { key::kKerberosEnabled,
+    prefs::kKerberosEnabled,
+    base::Value::Type::BOOLEAN },
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   { key::kChromeOsLockOnIdleSuspend,
     ash::prefs::kEnableAutoScreenLock,
@@ -1169,9 +1175,6 @@
   { key::kDeviceUsbPowerShareEnabled,
     ash::prefs::kUsbPowerShareEnabled,
     base::Value::Type::BOOLEAN },
-  { key::kKerberosEnabled,
-    prefs::kKerberosEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kKerberosRememberPasswordEnabled,
     prefs::kKerberosRememberPasswordEnabled,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
index da4daa8..902fd691 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
@@ -325,13 +325,12 @@
 
 std::vector<std::string>
 PrivacySandboxService::GetBlockedFledgeJoiningTopFramesForDisplay() const {
-  auto* pref_value =
-      pref_service_->GetDictionary(prefs::kPrivacySandboxFledgeJoinBlocked);
-  DCHECK(pref_value->is_dict());
+  const base::Value::Dict& pref_value =
+      pref_service_->GetValueDict(prefs::kPrivacySandboxFledgeJoinBlocked);
 
   std::vector<std::string> blocked_top_frames;
 
-  for (auto entry : pref_value->DictItems())
+  for (auto entry : pref_value)
     blocked_top_frames.emplace_back(entry.first);
 
   // Apply a lexographic ordering to match other settings permission surfaces.
diff --git a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
index 3759124..fb96b9e3 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
@@ -440,7 +440,15 @@
     this.isDefaultSsoProvider_ = doSamlRedirect;
     this.startLoadingTimer_();
 
+    // Don't enable GAIA action buttons when doing SAML redirect.
+    this.authenticatorParams_.enableGaiaActionButtons = !doSamlRedirect;
     this.authenticatorParams_.doSamlRedirect = doSamlRedirect;
+    // Set `isSaml_` flag here to make sure we show the correct UI. If we do a
+    // redirect, but it fails due to an error, there won't be any `authFlow`
+    // change triggered by `authenticator_`, however we should still show a
+    // button to let user go to GAIA page and keep original GAIA buttons
+    // hidden.
+    this.isSaml_ = doSamlRedirect;
     this.authenticator_.load(
         cr.login.Authenticator.AuthMode.DEFAULT, this.authenticatorParams_);
   }
@@ -628,7 +636,6 @@
         !(data.enterpriseManagedDevice || data.hasDeviceOwner);
     params.isFirstUser = !(data.enterpriseManagedDevice || data.hasDeviceOwner);
     params.obfuscatedOwnerId = data.obfuscatedOwnerId;
-    params.enableGaiaActionButtons = true;
 
     this.authenticatorParams_ = params;
 
@@ -686,6 +693,7 @@
     this.authCompleted_ = false;
     // Reset webview to prevent calls from authenticator.
     this.authenticator_.resetWebview();
+    this.authenticator_.resetStates();
     // Explicitly disable video here to let `onVideoEnabledChange_()` handle
     // timer start next time when `videoEnabled_` will be set to true on SAML
     // page.
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts
index 1f4e00c..9e4e45b 100644
--- a/chrome/browser/resources/new_tab_page/lazy_load.ts
+++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -20,7 +20,7 @@
 export {CustomizeDialogElement} from './customize_dialog.js';
 export {CustomizeModulesElement} from './customize_modules.js';
 export {CustomizeShortcutsElement} from './customize_shortcuts.js';
-export {MiddleSlotPromoElement} from './middle_slot_promo.js';
+export {MiddleSlotPromoElement, PromoDismissAction} from './middle_slot_promo.js';
 export {ChromeCartProxy} from './modules/cart/chrome_cart_proxy.js';
 export {DiscountConsentCard, DiscountConsentVariation} from './modules/cart/discount_consent_card.js';
 export {DiscountConsentDialog} from './modules/cart/discount_consent_dialog.js';
diff --git a/chrome/browser/resources/new_tab_page/middle_slot_promo.ts b/chrome/browser/resources/new_tab_page/middle_slot_promo.ts
index 28959c5..c1082a6 100644
--- a/chrome/browser/resources/new_tab_page/middle_slot_promo.ts
+++ b/chrome/browser/resources/new_tab_page/middle_slot_promo.ts
@@ -19,6 +19,22 @@
 import {WindowProxy} from './window_proxy.js';
 
 /**
+ * List of possible Promo Dismiss actions. This enum must match with the
+ * numbering for NtpPromoDismissAction in histogram/enums.xml. These values are
+ * persisted to logs. Entries should not be renumbered, removed or reused.
+ */
+export enum PromoDismissAction {
+  DISMISS = 0,
+  RESTORE = 1,
+}
+
+export function recordPromoDismissAction(action: PromoDismissAction) {
+  chrome.metricsPrivate.recordEnumerationValue(
+      'NewTabPage.Promos.DismissAction', action,
+      Object.keys(PromoDismissAction).length);
+}
+
+/**
  * If a promo exists with content and can be shown, an element containing
  * the rendered promo is returned with an id #promoContainer. Otherwise, null is
  * returned.
@@ -200,6 +216,7 @@
     NewTabPageProxy.getInstance().handler.blocklistPromo(
         this.middleSlotPromoId_);
     this.$.dismissPromoButtonToast.show();
+    recordPromoDismissAction(PromoDismissAction.DISMISS);
   }
 
   private onUndoDismissPromoButtonClick_() {
@@ -208,6 +225,7 @@
         this.middleSlotPromoId_);
     this.$.promoAndDismissContainer.hidden = false;
     this.$.dismissPromoButtonToast.hide();
+    recordPromoDismissAction(PromoDismissAction.RESTORE);
   }
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 075a150..27e427c 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -558,30 +558,46 @@
           .inspection_type();
   switch (inspection_type) {
     case DownloadFileType::NONE:
-      UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration.None",
-                          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramTimes("SBClientDownload.DownloadRequestDuration.None",
+                              base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramMediumTimes(
+          "SBClientDownload.DownloadRequestDurationMedium.None",
+          base::TimeTicks::Now() - start_time_);
       break;
     case DownloadFileType::ZIP:
-      UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration.Zip",
-                          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramTimes("SBClientDownload.DownloadRequestDuration.Zip",
+                              base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramMediumTimes(
+          "SBClientDownload.DownloadRequestDurationMedium.Zip",
+          base::TimeTicks::Now() - start_time_);
       break;
     case DownloadFileType::RAR:
-      UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration.Rar",
-                          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramTimes("SBClientDownload.DownloadRequestDuration.Rar",
+                              base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramMediumTimes(
+          "SBClientDownload.DownloadRequestDurationMedium.Rar",
+          base::TimeTicks::Now() - start_time_);
       break;
     case DownloadFileType::DMG:
-      UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration.Dmg",
-                          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramTimes("SBClientDownload.DownloadRequestDuration.Dmg",
+                              base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramMediumTimes(
+          "SBClientDownload.DownloadRequestDurationMedium.Dmg",
+          base::TimeTicks::Now() - start_time_);
       break;
     case DownloadFileType::OFFICE_DOCUMENT:
-      UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration.Document",
-                          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramTimes(
+          "SBClientDownload.DownloadRequestDuration.Document",
+          base::TimeTicks::Now() - start_time_);
+      base::UmaHistogramMediumTimes(
+          "SBClientDownload.DownloadRequestDurationMedium.Document",
+          base::TimeTicks::Now() - start_time_);
       break;
   }
-  UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
-                      base::TimeTicks::Now() - start_time_);
-  UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
-                      base::TimeTicks::Now() - request_start_time_);
+  base::UmaHistogramTimes("SBClientDownload.DownloadRequestDuration",
+                          base::TimeTicks::Now() - start_time_);
+  base::UmaHistogramTimes("SBClientDownload.DownloadRequestNetworkDuration",
+                          base::TimeTicks::Now() - request_start_time_);
 
   FinishRequest(result, reason);
 }
diff --git a/chrome/browser/safe_browsing/download_protection/download_request_maker.cc b/chrome/browser/safe_browsing/download_protection/download_request_maker.cc
index bd75dfb..5069927 100644
--- a/chrome/browser/safe_browsing/download_protection/download_request_maker.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_request_maker.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -166,10 +166,14 @@
       target_file_path_, full_path_,
       base::BindOnce(&DownloadRequestMaker::OnFileFeatureExtractionDone,
                      weakptr_factory_.GetWeakPtr()));
+  start_time_ = base::Time::Now();
 }
 
 void DownloadRequestMaker::OnFileFeatureExtractionDone(
     FileAnalyzer::Results results) {
+  base::UmaHistogramMediumTimes(
+      "SBClientDownload.FileFeatureExtractionDuration",
+      base::Time::Now() - start_time_);
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   request_->set_download_type(results.type);
@@ -199,6 +203,7 @@
 }
 
 void DownloadRequestMaker::GetTabRedirects() {
+  start_time_ = base::Time::Now();
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!tab_urls_.url.is_valid()) {
     OnGotTabRedirects({});
@@ -227,6 +232,8 @@
 
 void DownloadRequestMaker::OnGotTabRedirects(
     history::RedirectList redirect_list) {
+  base::UmaHistogramMediumTimes("SBClientDownload.GetTabRedirectsDuration",
+                                base::Time::Now() - start_time_);
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   for (size_t i = 0; i < redirect_list.size(); ++i) {
diff --git a/chrome/browser/safe_browsing/download_protection/download_request_maker.h b/chrome/browser/safe_browsing/download_protection/download_request_maker.h
index 4bacadc..529eeb43 100644
--- a/chrome/browser/safe_browsing/download_protection/download_request_maker.h
+++ b/chrome/browser/safe_browsing/download_protection/download_request_maker.h
@@ -93,6 +93,9 @@
 
   Callback callback_;
 
+  // Start time of a given asynchronous task. Used for metrics.
+  base::Time start_time_;
+
   base::WeakPtrFactory<DownloadRequestMaker> weakptr_factory_{this};
 };
 
diff --git a/chrome/browser/segmentation_platform/BUILD.gn b/chrome/browser/segmentation_platform/BUILD.gn
index 003d7a1..b324914 100644
--- a/chrome/browser/segmentation_platform/BUILD.gn
+++ b/chrome/browser/segmentation_platform/BUILD.gn
@@ -36,6 +36,7 @@
   deps = [
     "//base/test:test_support",
     "//chrome/browser",
+    "//components/segmentation_platform/embedder",
     "//components/segmentation_platform/internal",
     "//components/segmentation_platform/internal:test_support",
     "//components/segmentation_platform/internal/proto",
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
index fec56d8d..9634f39 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
@@ -14,12 +14,12 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_profile_observer.h"
 #include "chrome/browser/segmentation_platform/ukm_database_client.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/segmentation_platform/embedder/model_provider_factory_impl.h"
 #include "components/segmentation_platform/internal/dummy_segmentation_platform_service.h"
 #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
 #include "components/segmentation_platform/internal/ukm_data_manager.h"
diff --git a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
index 0c18f996..9fc61a06 100644
--- a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
+++ b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
@@ -5,10 +5,10 @@
 #include "chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h"
 
 #include "base/run_loop.h"
-#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
 #include "chrome/browser/segmentation_platform/ukm_database_client.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/segmentation_platform/embedder/model_provider_factory_impl.h"
 #include "components/segmentation_platform/internal/database/ukm_database.h"
 #include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextIPHController.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextIPHController.java
index 4c40591..b85e041 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextIPHController.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextIPHController.java
@@ -58,7 +58,16 @@
 
                 if (!LinkToTextHelper.hasTextFragment(url)) return;
 
-                mTracker = TrackerFactory.getTrackerForProfile(profileSupplier.get());
+                Profile profile = profileSupplier.get();
+                // In some cases, ProfileSupplier.get() will return null or will not be initialized
+                // in Native. See https://crbug.com/1346710 and https://crbug.com/1353138.
+                if (profile == null || !profile.isNativeInitialized()) {
+                    profile = Profile.getLastUsedRegularProfile();
+                }
+                if (profile == null) {
+                    return;
+                }
+                mTracker = TrackerFactory.getTrackerForProfile(profile);
                 if (!mTracker.wouldTriggerHelpUI(FEATURE_NAME)) {
                     return;
                 }
diff --git a/chrome/browser/shell_integration_mac.mm b/chrome/browser/shell_integration_mac.mm
index e6e12784..9c9f6a48 100644
--- a/chrome/browser/shell_integration_mac.mm
+++ b/chrome/browser/shell_integration_mac.mm
@@ -39,9 +39,6 @@
 // applies only for the current user. Returns false if this cannot be done, or
 // if the operation fails.
 bool SetAsDefaultBrowser() {
-  if (!CanSetAsDefaultBrowser())
-    return false;
-
   // We really do want the outer bundle here, not the main bundle since setting
   // a shortcut to Chrome as the default browser doesn't make sense.
   NSString* identifier = [base::mac::OuterBundle() bundleIdentifier];
@@ -94,10 +91,6 @@
 }
 
 DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
-  if (chrome::GetChannel() == version_info::Channel::CANARY) {
-    return SET_DEFAULT_NOT_ALLOWED;
-  }
-
   return SET_DEFAULT_UNATTENDED;
 }
 
diff --git a/chrome/browser/speech/tts_controller_delegate_impl.cc b/chrome/browser/speech/tts_controller_delegate_impl.cc
index 948ed02d..473d135 100644
--- a/chrome/browser/speech/tts_controller_delegate_impl.cc
+++ b/chrome/browser/speech/tts_controller_delegate_impl.cc
@@ -22,10 +22,10 @@
 namespace {
 
 absl::optional<content::TtsControllerDelegate::PreferredVoiceId>
-PreferredVoiceIdFromString(const base::Value* pref,
+PreferredVoiceIdFromString(const base::Value::Dict& pref,
                            const std::string& pref_key) {
   const std::string* voice_id =
-      pref->FindStringPath(l10n_util::GetLanguage(pref_key));
+      pref.FindStringByDottedPath(l10n_util::GetLanguage(pref_key));
   if (!voice_id || voice_id->empty())
     return absl::nullopt;
 
@@ -64,7 +64,7 @@
 std::unique_ptr<content::TtsControllerDelegate::PreferredVoiceIds>
 TtsControllerDelegateImpl::GetPreferredVoiceIdsForUtterance(
     content::TtsUtterance* utterance) {
-  const base::Value* lang_to_voice_pref = GetLangToVoicePref(utterance);
+  const base::Value::Dict* lang_to_voice_pref = GetLangToVoicePref(utterance);
   if (!lang_to_voice_pref)
     return nullptr;
 
@@ -73,15 +73,15 @@
 
   if (!utterance->GetLang().empty()) {
     preferred_ids->lang_voice_id = PreferredVoiceIdFromString(
-        lang_to_voice_pref, l10n_util::GetLanguage(utterance->GetLang()));
+        *lang_to_voice_pref, l10n_util::GetLanguage(utterance->GetLang()));
   }
 
   const std::string app_lang = g_browser_process->GetApplicationLocale();
   preferred_ids->locale_voice_id = PreferredVoiceIdFromString(
-      lang_to_voice_pref, l10n_util::GetLanguage(app_lang));
+      *lang_to_voice_pref, l10n_util::GetLanguage(app_lang));
 
   preferred_ids->any_locale_voice_id =
-      PreferredVoiceIdFromString(lang_to_voice_pref, "noLanguageCode");
+      PreferredVoiceIdFromString(*lang_to_voice_pref, "noLanguageCode");
   return preferred_ids;
 }
 
@@ -118,10 +118,10 @@
   return profile ? profile->GetPrefs() : nullptr;
 }
 
-const base::Value* TtsControllerDelegateImpl::GetLangToVoicePref(
+const base::Value::Dict* TtsControllerDelegateImpl::GetLangToVoicePref(
     content::TtsUtterance* utterance) {
   const PrefService* prefs = GetPrefService(utterance);
   return prefs == nullptr
              ? nullptr
-             : prefs->GetDictionary(prefs::kTextToSpeechLangToVoiceName);
+             : &prefs->GetValueDict(prefs::kTextToSpeechLangToVoiceName);
 }
diff --git a/chrome/browser/speech/tts_controller_delegate_impl.h b/chrome/browser/speech/tts_controller_delegate_impl.h
index d2f43dba..7b8b111c 100644
--- a/chrome/browser/speech/tts_controller_delegate_impl.h
+++ b/chrome/browser/speech/tts_controller_delegate_impl.h
@@ -7,14 +7,11 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/singleton.h"
+#include "base/values.h"
 #include "content/public/browser/tts_controller_delegate.h"
 
 class PrefService;
 
-namespace base {
-class Value;
-}
-
 // Singleton class that manages Chrome side logic for TTS and TTS engine
 // extension APIs. This is only used on ChromeOS.
 class TtsControllerDelegateImpl : public content::TtsControllerDelegate {
@@ -44,7 +41,7 @@
 
   virtual const PrefService* GetPrefService(content::TtsUtterance* utterance);
 
-  const base::Value* GetLangToVoicePref(content::TtsUtterance* utterance);
+  const base::Value::Dict* GetLangToVoicePref(content::TtsUtterance* utterance);
 
   friend struct base::DefaultSingletonTraits<TtsControllerDelegateImpl>;
 };
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
index adfe2ec..9e0d110 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -251,12 +251,15 @@
 }
 
 // static
-bool FamilyInfoFetcher::ParseMembers(const base::ListValue* list,
+bool FamilyInfoFetcher::ParseMembers(const base::Value::List& list,
                                      std::vector<FamilyMember>* members) {
-  for (const auto& entry : list->GetListDeprecated()) {
+  for (const auto& entry : list) {
     FamilyMember member;
-    const base::DictionaryValue* dict = NULL;
-    if (!entry.GetAsDictionary(&dict) || !ParseMember(dict, &member)) {
+    if (!entry.is_dict()) {
+      return false;
+    }
+    const base::Value::Dict& dict = entry.GetDict();
+    if (!ParseMember(dict, &member)) {
       return false;
     }
     members->push_back(member);
@@ -265,58 +268,72 @@
 }
 
 // static
-bool FamilyInfoFetcher::ParseMember(const base::DictionaryValue* dict,
+bool FamilyInfoFetcher::ParseMember(const base::Value::Dict& dict,
                                     FamilyMember* member) {
-  if (!dict->GetString(kIdUserId, &member->obfuscated_gaia_id))
+  const std::string* obfuscated_gaia_id = dict.FindString(kIdUserId);
+  if (!obfuscated_gaia_id)
     return false;
-  std::string role_str;
-  if (!dict->GetString(kIdRole, &role_str))
+  member->obfuscated_gaia_id = *obfuscated_gaia_id;
+  const std::string* role_str = dict.FindString(kIdRole);
+  if (!role_str)
     return false;
-  if (!StringToRole(role_str, &member->role))
+  if (!StringToRole(*role_str, &member->role))
     return false;
-  const base::DictionaryValue* profile_dict = NULL;
-  if (dict->GetDictionary(kIdProfile, &profile_dict))
-    ParseProfile(profile_dict, member);
+  const base::Value::Dict* profile_dict = dict.FindDict(kIdProfile);
+  if (profile_dict)
+    ParseProfile(*profile_dict, member);
   return true;
 }
 
 // static
-void FamilyInfoFetcher::ParseProfile(const base::DictionaryValue* dict,
+void FamilyInfoFetcher::ParseProfile(const base::Value::Dict& dict,
                                      FamilyMember* member) {
-  dict->GetString(kIdDisplayName, &member->display_name);
-  dict->GetString(kIdEmail, &member->email);
-  dict->GetString(kIdProfileUrl, &member->profile_url);
-  dict->GetString(kIdProfileImageUrl, &member->profile_image_url);
-  if (member->profile_image_url.empty())
-    dict->GetString(kIdDefaultProfileImageUrl, &member->profile_image_url);
+  const std::string* display_name = dict.FindString(kIdDisplayName);
+  if (display_name)
+    member->display_name = *display_name;
+  const std::string* email = dict.FindString(kIdEmail);
+  if (email)
+    member->email = *email;
+  const std::string* profile_url = dict.FindString(kIdProfileUrl);
+  if (profile_url)
+    member->profile_url = *profile_url;
+  const std::string* profile_image_url = dict.FindString(kIdProfileImageUrl);
+  if (profile_image_url)
+    member->profile_image_url = *profile_image_url;
+  if (member->profile_image_url.empty()) {
+    const std::string* def_profile_image_url =
+        dict.FindString(kIdDefaultProfileImageUrl);
+    if (def_profile_image_url)
+      member->profile_image_url = *def_profile_image_url;
+  }
 }
 
 void FamilyInfoFetcher::FamilyProfileFetched(const std::string& response) {
   std::unique_ptr<base::Value> value =
       base::JSONReader::ReadDeprecated(response);
-  const base::DictionaryValue* dict = NULL;
-  if (!value || !value->GetAsDictionary(&dict)) {
+  if (!value || !value->is_dict()) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
-  const base::DictionaryValue* family_dict = NULL;
-  if (!dict->GetDictionary(kIdFamily, &family_dict)) {
+  const base::Value::Dict& dict = value->GetDict();
+  const base::Value::Dict* family_dict = dict.FindDict(kIdFamily);
+  if (!family_dict) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
   FamilyProfile family;
-  const std::string* id = family_dict->FindStringKey(kIdFamilyId);
+  const std::string* id = family_dict->FindString(kIdFamilyId);
   if (!id) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
   family.id = *id;
-  const base::DictionaryValue* profile_dict = NULL;
-  if (!family_dict->GetDictionary(kIdProfile, &profile_dict)) {
+  const base::Value::Dict* profile_dict = family_dict->FindDict(kIdProfile);
+  if (!profile_dict) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
-  const std::string* name = profile_dict->FindStringKey(kIdFamilyName);
+  const std::string* name = profile_dict->FindString(kIdFamilyName);
   if (!name) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
@@ -326,20 +343,19 @@
 }
 
 void FamilyInfoFetcher::FamilyMembersFetched(const std::string& response) {
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::ReadDeprecated(response);
-  const base::DictionaryValue* dict = NULL;
-  if (!value || !value->GetAsDictionary(&dict)) {
+  absl::optional<base::Value> value = base::JSONReader::Read(response);
+  if (!value || !value->is_dict()) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
-  const base::ListValue* members_list = NULL;
-  if (!dict->GetList(kIdMembers, &members_list)) {
+  const base::Value::Dict& dict = value->GetDict();
+  const base::Value::List* members_list = dict.FindList(kIdMembers);
+  if (!members_list) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
   std::vector<FamilyMember> members;
-  if (!ParseMembers(members_list, &members)) {
+  if (!ParseMembers(*members_list, &members)) {
     consumer_->OnFailure(ErrorCode::kServiceError);
     return;
   }
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
index 7d93785..deec946 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
@@ -12,13 +12,9 @@
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 
-namespace base {
-class DictionaryValue;
-class ListValue;
-}
-
 namespace signin {
 struct AccessTokenInfo;
 class PrimaryAccountAccessTokenFetcher;
@@ -115,12 +111,10 @@
 
   void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
 
-  static bool ParseMembers(const base::ListValue* list,
+  static bool ParseMembers(const base::Value::List& list,
                            std::vector<FamilyMember>* members);
-  static bool ParseMember(const base::DictionaryValue* dict,
-                          FamilyMember* member);
-  static void ParseProfile(const base::DictionaryValue* dict,
-                           FamilyMember* member);
+  static bool ParseMember(const base::Value::Dict& dict, FamilyMember* member);
+  static void ParseProfile(const base::Value::Dict& dict, FamilyMember* member);
 
   void StartFetching();
   void StartFetchingAccessToken();
diff --git a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
index 36a6384..76d97c3 100644
--- a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
+++ b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
@@ -197,10 +197,10 @@
           }
         })");
 
-  base::DictionaryValue dict;
-  dict.SetKey(kEventTypeKey, base::Value((*it)->request_type));
-  dict.SetKey(kObjectRefKey, base::Value((*it)->object_ref));
-  dict.SetKey(kStateKey, base::Value(kState));
+  base::Value::Dict dict;
+  dict.Set(kEventTypeKey, base::Value((*it)->request_type));
+  dict.Set(kObjectRefKey, base::Value((*it)->object_ref));
+  dict.Set(kStateKey, base::Value(kState));
   std::string body;
   base::JSONWriter::Write(dict, &body);
 
@@ -269,21 +269,21 @@
   if (response_body)
     body = std::move(*response_body);
 
-  std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(body);
-  base::DictionaryValue* dict = NULL;
-  if (!value || !value->GetAsDictionary(&dict)) {
+  absl::optional<base::Value> value = base::JSONReader::Read(body);
+  if (!value || !value->is_dict()) {
     LOG(WARNING) << "Invalid top-level dictionary";
     DispatchResult(std::move(it), false);
     return;
   }
-  base::DictionaryValue* permission_dict = NULL;
-  if (!dict->GetDictionary(kPermissionRequestKey, &permission_dict)) {
+  const base::Value::Dict& dict = value->GetDict();
+  const base::Value::Dict* permission_dict =
+      dict.FindDict(kPermissionRequestKey);
+  if (!permission_dict) {
     LOG(WARNING) << "Permission request not found";
     DispatchResult(std::move(it), false);
     return;
   }
-  std::string id;
-  if (!permission_dict->GetString(kIdKey, &id)) {
+  if (!permission_dict->FindString(kIdKey)) {
     LOG(WARNING) << "ID not found";
     DispatchResult(std::move(it), false);
     return;
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 68498b2f0..98998e3 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -908,9 +908,9 @@
   // version information stored in the values is unnecessary. It is only there
   // for backwards compatibility. Remove the version information once sufficient
   // users have migrated away from M83.
-  const base::Value* dict = profile_->GetPrefs()->GetDictionary(
+  const base::Value::Dict& dict = profile_->GetPrefs()->GetValueDict(
       prefs::kSupervisedUserApprovedExtensions);
-  for (auto it : dict->DictItems()) {
+  for (auto it : dict) {
     approved_extensions_set_.insert(it.first);
     extensions_to_be_checked.insert(it.first);
   }
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SyncConsentFragmentBase.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SyncConsentFragmentBase.java
index 72ca926..b50e135 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SyncConsentFragmentBase.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SyncConsentFragmentBase.java
@@ -77,9 +77,6 @@
 
     private static final String ARGUMENT_ACCOUNT_NAME = "SyncConsentFragmentBase.AccountName";
 
-    /** Field trial group param for the tangible sync experiment. */
-    private static final String PARAM_TANGIBLE_SYNC_GROUP = "group_id";
-
     // This bundle argument is optional; it is set only if the child status cannot be reliably
     // inferred by looking at the last used regular profile, because child sign auto sign in may
     // not have completed.
@@ -98,15 +95,6 @@
         int ADD_ACCOUNT = 2;
     }
 
-    /** Group name for different UIs in tangible sync experiment. */
-    @IntDef({TangibleSyncGroup.GROUP_A, TangibleSyncGroup.GROUP_B, TangibleSyncGroup.GROUP_C})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface TangibleSyncGroup {
-        int GROUP_A = 1;
-        int GROUP_B = 2;
-        int GROUP_C = 3;
-    }
-
     private final AccountManagerFacade mAccountManagerFacade;
     protected boolean mIsChild;
 
@@ -501,41 +489,10 @@
         }
     }
 
-    private static @TangibleSyncGroup int getTangibleSyncGroup() {
-        return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(ChromeFeatureList.TANGIBLE_SYNC,
-                PARAM_TANGIBLE_SYNC_GROUP, TangibleSyncGroup.GROUP_A);
-    }
-
-    private static @StringRes int getSyncConsentViewTitleText() {
-        switch (getTangibleSyncGroup()) {
-            case TangibleSyncGroup.GROUP_A:
-                return R.string.sync_consent_title;
-            case TangibleSyncGroup.GROUP_B:
-                return R.string.sync_consent_title_variation;
-            case TangibleSyncGroup.GROUP_C:
-                return R.string.sync_consent_title;
-            default:
-                throw new IllegalStateException("Invalid group id");
-        }
-    }
-
-    private static @StringRes int getSyncConsentViewSubtitleText() {
-        switch (getTangibleSyncGroup()) {
-            case TangibleSyncGroup.GROUP_A:
-                return R.string.sync_consent_subtitle;
-            case TangibleSyncGroup.GROUP_B:
-                return R.string.sync_consent_subtitle;
-            case TangibleSyncGroup.GROUP_C:
-                return R.string.sync_consent_subtitle_variation;
-            default:
-                throw new IllegalStateException("Invalid group id");
-        }
-    }
-
     private void updateSyncConsentViewText(@StringRes int refuseButtonTextId) {
-        mConsentTextTracker.setText(mSyncConsentView.getTitleView(), getSyncConsentViewTitleText());
+        mConsentTextTracker.setText(mSyncConsentView.getTitleView(), R.string.sync_consent_title);
         mConsentTextTracker.setText(
-                mSyncConsentView.getSubtitleView(), getSyncConsentViewSubtitleText());
+                mSyncConsentView.getSubtitleView(), R.string.sync_consent_subtitle);
 
         mConsentTextTracker.setText(
                 mSyncConsentView.getBookmarksRow(), R.string.sync_consent_bookmarks_text);
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 6408a8f..a9c526c6 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2904,15 +2904,9 @@
       <message name="IDS_SYNC_CONSENT_TITLE" desc="This string appears as a heading on a full-page screen that asks users if they want to turn on sync. 'Sync' is short for 'synchronization'. The tone should be informative and inviting. [CHAR_LIMIT=35]">
         Turn on sync
       </message>
-      <message name="IDS_SYNC_CONSENT_TITLE_VARIATION" desc="This string appears as a heading on a full-page screen that asks users if they want to turn on sync. 'Sync' is short for 'synchronization'. [CHAR_LIMIT=20]">
-        Sync
-      </message>
-      <message name="IDS_SYNC_CONSENT_SUBTITLE" desc="This string appears as a subheading on a full-page screen that asks users if they want to turn on sync. This string explains to users that they can back up their Chrome data and use it on all their computers, phones, and other devices; a user's Chrome data includes their bookmarks, passwords, history, settings, and more. The word 'stuff' can be translated as 'data' or similar if there is no natural translation of 'stuff'. Maintain the level of formality that your language generally uses in the Chrome app. The tone should be informative and inviting. [CHAR_LIMIT=100]">
+      <message name="IDS_SYNC_CONSENT_SUBTITLE" desc="This string appears as a subheading a full-page screen that asks users if they want to turn on sync. This string explains to users that they can back up their Chrome data and use it on all their computers, phones, and other devices; a user's Chrome data includes their bookmarks, passwords, history, settings, and more. The word 'stuff' can be translated as 'data' or similar if there is no natural translation of 'stuff'. Maintain the level of formality that your language generally uses in the Chrome app. The tone should be informative and inviting. [CHAR_LIMIT=100]">
         Back up your stuff and use it on any device
       </message>
-      <message name="IDS_SYNC_CONSENT_SUBTITLE_VARIATION" desc="This string appears as a subheading on a full-page screen that asks users if they want to turn on sync. This string explains to users that they can get their Chrome data on all their computers, phones, and other devices; a user's Chrome data includes their bookmarks, passwords, history, settings, and more. Maintain the level of formality that your language generally uses in the Chrome app. The tone should be informative and inviting. [CHAR_LIMIT=100]">
-        Sync your data on all devices
-      </message>
       <message name="IDS_SIGNIN_SYNC_TITLE" desc="Title of Sync feature for the screen that asks users to sign-in and turn on Sync.">
         Sync your passwords, history &amp; more on all devices
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_SUBTITLE_VARIATION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_SUBTITLE_VARIATION.png.sha1
deleted file mode 100644
index 161bfd4..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_SUBTITLE_VARIATION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3f442f87735a3f48869d68ca76e8079dad85bd79
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_TITLE_VARIATION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_TITLE_VARIATION.png.sha1
deleted file mode 100644
index 470d795..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_CONSENT_TITLE_VARIATION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e80a43317ffed0a0e64029dd83ed8db90aae9356
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/desks/desks_client.cc b/chrome/browser/ui/ash/desks/desks_client.cc
index ed67b92..46bba5a 100644
--- a/chrome/browser/ui/ash/desks/desks_client.cc
+++ b/chrome/browser/ui/ash/desks/desks_client.cc
@@ -278,9 +278,14 @@
     return;
   }
 
-  GetDeskModel()->GetAllEntries(base::BindOnce(&DesksClient::OnGetAllTemplates,
-                                               weak_ptr_factory_.GetWeakPtr(),
-                                               std::move(callback)));
+  auto result = GetDeskModel()->GetAllEntries();
+
+  std::move(callback).Run(
+      result.entries,
+      std::string(result.status !=
+                          desks_storage::DeskModel::GetAllEntriesStatus::kOk
+                      ? kStorageError
+                      : ""));
 }
 
 void DesksClient::GetTemplateJson(const std::string uuid,
@@ -616,17 +621,6 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void DesksClient::OnGetAllTemplates(
-    GetDeskTemplatesCallback callback,
-    desks_storage::DeskModel::GetAllEntriesStatus status,
-    const std::vector<const ash::DeskTemplate*>& entries) {
-  std::move(callback).Run(
-      entries,
-      std::string(status != desks_storage::DeskModel::GetAllEntriesStatus::kOk
-                      ? kStorageError
-                      : ""));
-}
-
 void DesksClient::OnCapturedDeskTemplate(
     CaptureActiveDeskAndSaveTemplateCallback callback,
     std::unique_ptr<ash::DeskTemplate> desk_template) {
diff --git a/chrome/browser/ui/ash/desks/desks_client.h b/chrome/browser/ui/ash/desks/desks_client.h
index cb1e05f8..abe35ab 100644
--- a/chrome/browser/ui/ash/desks/desks_client.h
+++ b/chrome/browser/ui/ash/desks/desks_client.h
@@ -220,12 +220,6 @@
       desks_storage::DeskModel::GetEntryByUuidStatus status,
       std::unique_ptr<ash::DeskTemplate> entry);
 
-  // Callback function that handles getting all DeskTemplates from
-  // storage.
-  void OnGetAllTemplates(GetDeskTemplatesCallback callback,
-                         desks_storage::DeskModel::GetAllEntriesStatus status,
-                         const std::vector<const ash::DeskTemplate*>& entries);
-
   // Callback function that is called once the DesksController has captured the
   // active desk as a template. Invokes |callback| with |desk_template| as an
   // argument.
diff --git a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
index 6537998..f10c730 100644
--- a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
@@ -299,7 +299,6 @@
       ash::GetZeroStateDesksTemplatesButton();
   ASSERT_TRUE(zero_state_templates_button);
   ClickButton(zero_state_templates_button);
-  ash::WaitForDesksTemplatesUI();
 }
 
 void ClickExpandedStateTemplatesButton() {
@@ -317,16 +316,9 @@
 
 const std::vector<const ash::DeskTemplate*> GetAllEntries() {
   std::vector<const ash::DeskTemplate*> templates;
-  base::RunLoop loop;
-  DesksClient::Get()->GetDeskModel()->GetAllEntries(base::BindLambdaForTesting(
-      [&](desks_storage::DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        DCHECK_EQ(desks_storage::DeskModel::GetAllEntriesStatus::kOk, status);
-        templates = entries;
-        loop.Quit();
-      }));
-  loop.Run();
-  return templates;
+  auto result = DesksClient::Get()->GetDeskModel()->GetAllEntries();
+  DCHECK_EQ(desks_storage::DeskModel::GetAllEntriesStatus::kOk, result.status);
+  return result.entries;
 }
 
 // Creates a vector of tab groups based on the vector of GURLs passed into it.
@@ -2176,7 +2168,6 @@
   // Reenter overview and launch the template we saved.
   ash::ToggleOverview();
   ash::WaitForOverviewEnterAnimation();
-  ash::WaitForDesksTemplatesUI();
   ClickZeroStateTemplatesButton();
   ClickFirstTemplateItem();
   content::RunAllTasksUntilIdle();
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index e2aef29a8..30b82dc 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -297,7 +297,11 @@
 
 std::unique_ptr<webauthn::InternalAuthenticator>
 ChromeAutofillClient::CreateCreditCardInternalAuthenticator(
-    content::RenderFrameHost* rfh) {
+    AutofillDriver* driver) {
+  auto* cad = static_cast<ContentAutofillDriver*>(driver);
+  content::RenderFrameHost* rfh = cad->render_frame_host();
+  if (!rfh)
+    return nullptr;
 #if BUILDFLAG(IS_ANDROID)
   return std::make_unique<InternalAuthenticatorAndroid>(rfh);
 #else
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 567ca6b..111199c 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -89,7 +89,7 @@
   std::string GetVariationConfigCountryCode() const override;
   profile_metrics::BrowserProfileType GetProfileType() const override;
   std::unique_ptr<webauthn::InternalAuthenticator>
-  CreateCreditCardInternalAuthenticator(content::RenderFrameHost* rfh) override;
+  CreateCreditCardInternalAuthenticator(AutofillDriver* driver) override;
 
   void ShowAutofillSettings(bool show_credit_card_settings) override;
   void ShowCardUnmaskOtpInputDialog(
diff --git a/chrome/browser/ui/cocoa/first_run_dialog_cocoa.mm b/chrome/browser/ui/cocoa/first_run_dialog_cocoa.mm
index e046562b..476953b 100644
--- a/chrome/browser/ui/cocoa/first_run_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/first_run_dialog_cocoa.mm
@@ -106,9 +106,7 @@
 
 - (instancetype)init {
   _viewController.reset([[FirstRunDialogViewController alloc]
-      initWithStatsCheckboxInitiallyChecked:StatsCheckboxDefault()
-              defaultBrowserCheckboxVisible:shell_integration::
-                                                CanSetAsDefaultBrowser()]);
+      initWithStatsCheckboxInitiallyChecked:StatsCheckboxDefault()]);
 
   // Create the content view controller (and the content view) *before* the
   // window, so that we can find out what the content view's frame is supposed
diff --git a/chrome/browser/ui/cocoa/first_run_dialog_controller.h b/chrome/browser/ui/cocoa/first_run_dialog_controller.h
index 008f5108..b6fe26d 100644
--- a/chrome/browser/ui/cocoa/first_run_dialog_controller.h
+++ b/chrome/browser/ui/cocoa/first_run_dialog_controller.h
@@ -11,8 +11,7 @@
 // dialog's content view.
 @interface FirstRunDialogViewController : NSViewController
 
-- (instancetype)initWithStatsCheckboxInitiallyChecked:(BOOL)checked
-                        defaultBrowserCheckboxVisible:(BOOL)visible;
+- (instancetype)initWithStatsCheckboxInitiallyChecked:(BOOL)checked;
 
 - (NSString*)windowTitle;
 
diff --git a/chrome/browser/ui/cocoa/first_run_dialog_controller.mm b/chrome/browser/ui/cocoa/first_run_dialog_controller.mm
index a7a957a..23608bfc 100644
--- a/chrome/browser/ui/cocoa/first_run_dialog_controller.mm
+++ b/chrome/browser/ui/cocoa/first_run_dialog_controller.mm
@@ -85,14 +85,11 @@
   NSView* _view;
 
   BOOL _statsCheckboxInitiallyChecked;
-  BOOL _defaultBrowserCheckboxVisible;
 }
 
-- (instancetype)initWithStatsCheckboxInitiallyChecked:(BOOL)checked
-                        defaultBrowserCheckboxVisible:(BOOL)visible {
+- (instancetype)initWithStatsCheckboxInitiallyChecked:(BOOL)checked {
   if ((self = [super init])) {
     _statsCheckboxInitiallyChecked = checked;
-    _defaultBrowserCheckboxVisible = visible;
   }
   return self;
 }
@@ -139,8 +136,6 @@
   [_defaultBrowserCheckbox
       setFrame:NSMakeRect(45, 107, kDialogWidth - 2 * 45, 18)];
   [_defaultBrowserCheckbox setState:NSOnState];
-  if (!_defaultBrowserCheckboxVisible)
-    [_defaultBrowserCheckbox setHidden:YES];
 
   _statsCheckbox = [NSButton
       checkboxWithTitle:NSStringWithProductName(
@@ -215,19 +210,6 @@
   if (base::i18n::IsRTL())
     frame.origin.x = kDialogWidth - NSMaxX(frame);
   [startChromeButton setFrame:frame];
-
-  // Lastly, if the default browser checkbox is actually invisible, move the
-  // views above it downward so that there's not a big open space in the content
-  // view, and resize the content view itself so there isn't extra space.
-  if (!_defaultBrowserCheckboxVisible) {
-    CGFloat delta = NSHeight([_defaultBrowserCheckbox frame]);
-    MoveViewsVertically(@[ topBox, topSeparator ], -delta);
-    NSRect frame = [self.view frame];
-    frame.size.height -= delta;
-    [self.view setAutoresizesSubviews:NO];
-    [self.view setFrame:frame];
-    [self.view setAutoresizesSubviews:YES];
-  }
 }
 
 - (NSString*)windowTitle {
diff --git a/chrome/browser/ui/cocoa/first_run_dialog_controller_unittest.mm b/chrome/browser/ui/cocoa/first_run_dialog_controller_unittest.mm
index b6274ac..b6bc084 100644
--- a/chrome/browser/ui/cocoa/first_run_dialog_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/first_run_dialog_controller_unittest.mm
@@ -14,10 +14,9 @@
 using FirstRunDialogControllerTest = CocoaTest;
 using TestController = base::scoped_nsobject<FirstRunDialogViewController>;
 
-TestController MakeTestController(BOOL stats, BOOL browser) {
+TestController MakeTestController(BOOL stats) {
   return TestController([[FirstRunDialogViewController alloc]
-      initWithStatsCheckboxInitiallyChecked:stats
-              defaultBrowserCheckboxVisible:browser]);
+      initWithStatsCheckboxInitiallyChecked:stats]);
 }
 
 NSView* FindBrowserButton(NSView* view) {
@@ -32,39 +31,33 @@
 }
 
 TEST(FirstRunDialogControllerTest, SetStatsDefault) {
-  TestController controller(MakeTestController(YES, YES));
+  TestController controller(MakeTestController(YES));
   [controller view];  // Make sure view is actually loaded.
   EXPECT_TRUE([controller isStatsReportingEnabled]);
 }
 
 TEST(FirstRunDialogControllerTest, MakeDefaultBrowserDefault) {
-  TestController controller(MakeTestController(YES, YES));
+  TestController controller(MakeTestController(YES));
   [controller view];
   EXPECT_TRUE([controller isMakeDefaultBrowserEnabled]);
 }
 
 TEST(FirstRunDialogControllerTest, ShowBrowser) {
-  TestController controller(MakeTestController(YES, YES));
+  TestController controller(MakeTestController(YES));
   NSView* checkbox = FindBrowserButton([controller view]);
   EXPECT_FALSE(checkbox.hidden);
 }
 
-TEST(FirstRunDialogControllerTest, HideBrowser) {
-  TestController controller(MakeTestController(YES, NO));
-  NSView* checkbox = FindBrowserButton([controller view]);
-  EXPECT_TRUE(checkbox.hidden);
-}
-
 TEST(FirstRunDialogControllerTest, LayoutWithLongStrings) {
   // It's necessary to call |view| on the controller before mangling the
   // strings, since otherwise the controller will lazily construct its view,
   // which might happen after the call to |set_mangle_localized_strings|.
-  TestController defaultController(MakeTestController(YES, YES));
+  TestController defaultController(MakeTestController(YES));
   NSView* defaultView = [defaultController view];
 
   ui::ResourceBundle::GetSharedInstance().set_mangle_localized_strings_for_test(
       true);
-  TestController longController(MakeTestController(YES, YES));
+  TestController longController(MakeTestController(YES));
   NSView* longView = [longController view];
 
   // Ensure that the mangled strings actually do change the height!
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index f0bfa1f..6da7d48 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -253,7 +253,6 @@
   PasswordForm test_local_form_;
   PasswordForm test_federated_form_;
   PasswordForm submitted_form_;
-  std::vector<const PasswordForm*> insecure_credentials_;
   CredentialManagementDialogPromptMock dialog_prompt_;
 };
 
@@ -327,7 +326,7 @@
           Return(base::span<const password_manager::InteractionsStats>()));
   EXPECT_CALL(*form_manager, GetInsecureCredentials())
       .Times(AtMost(1))
-      .WillOnce(ReturnRef(insecure_credentials_));
+      .WillOnce(Return(std::vector<const password_manager::PasswordForm*>()));
   EXPECT_CALL(*form_manager, GetPendingCredentials())
       .WillRepeatedly(ReturnRef(submitted_form_));
   EXPECT_CALL(*form_manager, GetMetricsRecorder())
@@ -1561,11 +1560,10 @@
 
   EXPECT_CALL(*test_form_manager_raw, Save());
   PasswordForm credential = CreateInsecureCredential(test_local_form());
-  std::vector<const PasswordForm*> saved = {&credential};
   // Pretend that the current credential was insecure but with the updated
   // password not anymore.
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(ReturnRef(saved));
+      .WillOnce(Return(std::vector<const PasswordForm*>{&credential}));
   base::WeakPtr<password_manager::PasswordStoreConsumer> post_save_helper;
 
   EXPECT_CALL(*client().GetProfilePasswordStore(), GetAutofillableLogins)
@@ -1604,9 +1602,8 @@
   EXPECT_CALL(*test_form_manager_raw, Save());
   // Pretend that the current credential was insecure.
   PasswordForm credential = CreateInsecureCredential(test_local_form());
-  std::vector<const PasswordForm*> saved = {&credential};
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(ReturnRef(saved));
+      .WillOnce(Return(std::vector<const PasswordForm*>{&credential}));
 
   base::WeakPtr<password_manager::PasswordStoreConsumer> post_save_helper;
 
@@ -1653,10 +1650,9 @@
 
   EXPECT_CALL(*test_form_manager_raw, Save());
   PasswordForm credential = CreateInsecureCredential(test_local_form());
-  std::vector<const PasswordForm*> saved = {&credential};
   // Pretend that the current credential was insecure.
   EXPECT_CALL(*test_form_manager_raw, GetInsecureCredentials())
-      .WillOnce(ReturnRef(saved));
+      .WillOnce(Return(std::vector<const PasswordForm*>{&credential}));
   controller()->SavePassword(submitted_form().username_value,
                              submitted_form().password_value);
   // The sign-in promo bubble stays open, the warning isn't shown.
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
index 5fe4a850..96971e7 100644
--- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
+++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
@@ -140,13 +140,11 @@
   // PostTask because the Throttle needs to be deferred before the status code
   // is set. After setting the status code Resume() can be called synchronous
   // and thereby before the throttle is deferred. This would result in a crash.
-  // Unretained is safe because the NavigationThrottle is deferred and can only
-  // be continued after the callback finished.
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WellKnownChangePasswordState::SetChangePasswordResponseCode,
-          base::Unretained(&well_known_change_password_state_),
+          weak_ptr_factory_.GetWeakPtr(),
           navigation_handle()->GetResponseHeaders()->response_code()));
   return NavigationThrottle::DEFER;
 }
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
index 10817f7e..d5727c6c4 100644
--- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
+++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
@@ -66,6 +66,8 @@
       well_known_change_password_state_{this};
   ukm::SourceId source_id_ = ukm::kInvalidSourceId;
   raw_ptr<password_manager::AffiliationService> affiliation_service_ = nullptr;
+  base::WeakPtrFactory<password_manager::WellKnownChangePasswordState>
+      weak_ptr_factory_{&well_known_change_password_state_};
 };
 
 #endif  // CHROME_BROWSER_UI_PASSWORDS_WELL_KNOWN_CHANGE_PASSWORD_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc b/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
index ca1e732..d9df3a42 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -17,15 +21,31 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/interaction/interaction_test_util_browser.h"
+#include "chrome/test/interaction/webui_interaction_test_util.h"
 #include "content/public/test/browser_test.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+#include "ui/base/interaction/interaction_sequence.h"
+#include "ui/base/page_transition_types.h"
 #include "ui/base/pointer/touch_ui_controller.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/views/controls/webview/webview.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/scoped_observation.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
+#include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/client/drag_drop_client_observer.h"
+#include "ui/aura/window.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kMouseDragCompleteCustomEvent);
+
 class WebUITabStripInteractiveTest : public InProcessBrowserTest {
  public:
   WebUITabStripInteractiveTest() {
@@ -34,9 +54,111 @@
 
   ~WebUITabStripInteractiveTest() override = default;
 
+  // Convenience method to locate and send a custom event of type `event_type`
+  // on the element with identifier `id`.
+  void SendCustomEvent(ui::ElementIdentifier id,
+                       ui::CustomElementEventType event_type) {
+    auto* const target =
+        ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+            id, browser()->window()->GetElementContext());
+    ASSERT_NE(nullptr, target);
+    ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(target,
+                                                                  event_type);
+  };
+
+ protected:
+  using WeakPtr = base::WeakPtr<WebUITabStripInteractiveTest>;
+
+  // Performs a drag by sending mouse events.
+  //
+  // Moves the cursor to `start` and begins a drag to `end` in screen
+  // coordinates (but does not release the mouse button). When the mouse reaches
+  // `end`, an event is sent.
+  //
+  // This can probably be turned into a common utility method for testing things
+  // that happen in the middle of a drag.
+  void PerformDragWithoutRelease(gfx::Point start,
+                                 gfx::Point end,
+                                 ui::ElementIdentifier target_id) {
+    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+        start.x(), start.y(),
+        base::BindOnce(
+            [](WeakPtr test, gfx::Point end, ui::ElementIdentifier target_id) {
+              if (!test)
+                return;
+              ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
+                  ui_controls::LEFT, ui_controls::DOWN,
+                  base::BindOnce(
+                      [](WeakPtr test, gfx::Point end,
+                         ui::ElementIdentifier target_id) {
+                        if (!test)
+                          return;
+                        ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+                            end.x(), end.y(),
+                            base::BindOnce(
+                                &WebUITabStripInteractiveTest::SendCustomEvent,
+                                test, target_id,
+                                kMouseDragCompleteCustomEvent)));
+                      },
+                      test, end, target_id)));
+            },
+            weak_ptr_factory_.GetWeakPtr(), end, target_id)));
+  };
+
+  WeakPtr GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  void CancelPendingDrag() {
+    drag_ender_ = std::make_unique<DragEnder>(aura::client::GetDragDropClient(
+        browser()->window()->GetNativeWindow()->GetRootWindow()));
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
  private:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Ends any drag currently in progress or that starts during this object's
+  // lifetime. Used to prevent test hangs at the end of a test before TearDown()
+  // is run because a spurious drag starts. See discussion on crbug.com/1352602
+  // for discussion.
+  class DragEnder : public aura::client::DragDropClientObserver {
+   public:
+    explicit DragEnder(aura::client::DragDropClient* client) : client_(client) {
+      if (client_->IsDragDropInProgress()) {
+        PostCancel();
+      } else {
+        scoped_observation_.Observe(client_);
+      }
+    }
+
+    ~DragEnder() override = default;
+
+   private:
+    void OnDragStarted() override {
+      scoped_observation_.Reset();
+      PostCancel();
+    }
+
+    void PostCancel() {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&DragEnder::CancelDrag,
+                                    weak_ptr_factory_.GetWeakPtr()));
+    }
+
+    void CancelDrag() { client_->DragCancel(); }
+
+    aura::client::DragDropClient* const client_;
+    base::ScopedObservation<aura::client::DragDropClient,
+                            aura::client::DragDropClientObserver>
+        scoped_observation_{this};
+    base::WeakPtrFactory<DragEnder> weak_ptr_factory_{this};
+  };
+
+  std::unique_ptr<DragEnder> drag_ender_;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   base::test::ScopedFeatureList feature_override_;
   ui::TouchUiController::TouchUiScoperForTesting touch_ui_scoper_{true};
+  base::WeakPtrFactory<WebUITabStripInteractiveTest> weak_ptr_factory_{this};
 };
 
 // Regression test for crbug.com/1027375.
@@ -188,4 +310,213 @@
   EXPECT_TRUE(immersive_mode_controller->IsRevealed());
 }
 
+// Regression test for crbug.com/1286203.
+//
+// The original bug was a UAF that happened when a tab closed itself (e.g. via
+// javascript) during a drag from the WebUI tabstrip; not all references to the
+// tab were properly cleaned up.
+//
+// There is already a proposed regression test for this bug using existing
+// technology; see:
+//   https://chromium-review.googlesource.com/c/chromium/src/+/3588859
+//
+// This is a proof-of-concept for regression testing using InteractionSequence,
+// which demonstrates that:
+//  - tests can be written without arbitrary (and often flaky) delays
+//  - tests can be end-to-end interacting with both native and WebUI code
+//  - tests can be written to reproduce very specific test cases
+//
+// This framework can be used to handle many similar types of bugs, for both
+// WebUI and Views elements. These tests, while more verbose, can be made very
+// specific and are declarative and event-driven. This particular test performs
+// the following steps:
+//  1. opens a second tab in the browser
+//  2. clicks the tab counter button to open the WebUI tabstrip
+//  3. drags the second tab out of the WebUI tabstrip
+//  4. without finishing the drag, closes the tab via script
+//  5. verifies the tab actually closed
+//  6. completes the drag
+//
+// This sequence of events would crash without the associated bugfix. More
+// detail is provided in the actual test sequence.
+IN_PROC_BROWSER_TEST_F(WebUITabStripInteractiveTest, CloseTabDuringDrag) {
+  // Add a second tab and set up an object to instrument that tab.
+  ASSERT_TRUE(AddTabAtIndex(-1, GURL("about:blank"), ui::PAGE_TRANSITION_LINK));
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTabElementId);
+  std::unique_ptr<WebUIInteractionTestUtil> first_tab =
+      WebUIInteractionTestUtil::ForExistingTabInBrowser(browser(),
+                                                        kFirstTabElementId, 0);
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSecondTabElementId);
+  std::unique_ptr<WebUIInteractionTestUtil> second_tab =
+      WebUIInteractionTestUtil::ForExistingTabInBrowser(browser(),
+                                                        kSecondTabElementId, 1);
+
+  // The WebUI for the tabstrip will be instrumented only after it is guaranteed
+  // to have been created.
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebUiTabStripElementId);
+  std::unique_ptr<WebUIInteractionTestUtil> tab_strip;
+
+  // This is the DeepQuery path to the second tab element in the WebUI tabstrip.
+  // If the structure of the WebUI page changes greatly, it may need to be
+  // modified to reflect a new page structure.
+  const WebUIInteractionTestUtil::DeepQuery kSecondTabQuery{
+      "tabstrip-tab-list", "tabstrip-tab + tabstrip-tab"};
+
+  // Some custom events used to advance the test sequence.
+  DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kTabPopulatedCustomEvent);
+
+  // These are needed to determine the sequence didn't fail. They're boilerplate
+  // and will probably be exchanged in the future for a smarter version of
+  // InteractionSequence::RunSynchronouslyForTesting().
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
+
+  // This object contains the sequence of expected stets in the test.
+  auto sequence =
+      ui::InteractionSequence::Builder()
+          .SetContext(browser()->window()->GetElementContext())
+          .SetCompletedCallback(completed.Get())
+          .SetAbortedCallback(aborted.Get())
+
+          // Wait until the second tab has fully loaded. This is advisable since
+          // later the destruction of the tab needs to be observed.
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kShown)
+                       .SetElementID(kSecondTabElementId))
+
+          // Click the tab counter button to display the WebUI tabstrip and
+          // make sure the tabstrip appears.
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetElementID(kTabCounterButtonElementId)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence*,
+                          ui::TrackedElement* element) {
+                        const auto test_util = CreateInteractionTestUtil();
+                        test_util->PressButton(element);
+
+                        // The WebUI tabstrip can be created dynamically, so
+                        // wait until the button is pressed and the browser is
+                        // re-laid-out to bind the associated WebUI.
+                        auto* const browser_view =
+                            BrowserView::GetBrowserViewForBrowser(browser());
+                        browser_view->GetWidget()->LayoutRootViewIfNecessary();
+                        auto* const web_view = browser_view->webui_tab_strip()
+                                                   ->web_view_for_testing();
+                        tab_strip = WebUIInteractionTestUtil::ForNonTabWebView(
+                            web_view, kWebUiTabStripElementId);
+                      })))
+
+          // Wait for the WebUI tabstrip to become fully loaded, and then wait
+          // for the tab data to load and render.
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetElementID(kWebUiTabStripElementId)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence*,
+                          ui::TrackedElement* element) {
+                        // At this point the new tab has been fully loaded and
+                        // its onLoad() called.
+                        EXPECT_EQ(2, browser()->tab_strip_model()->count());
+
+                        // It takes a while for tab data to be filled out in the
+                        // tabstrip. Before it is fully loaded the tabs have
+                        // zero visible size, so wait until they are the
+                        // expected size.
+                        WebUIInteractionTestUtil::StateChange change;
+                        change.event = kTabPopulatedCustomEvent;
+                        change.where = kSecondTabQuery;
+                        change.type = WebUIInteractionTestUtil::StateChange::
+                            Type::kExistsAndConditionTrue;
+                        change.test_function =
+                            "el => (el.getBoundingClientRect().width > 0)";
+                        tab_strip->SendEventOnStateChange(std::move(change));
+                      })))
+
+          // Now that the tab is properly rendered, drag it out of the tabstrip.
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kCustomEvent,
+                                kTabPopulatedCustomEvent)
+                       .SetElementID(kWebUiTabStripElementId)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             // Starting point of drag is the center of the
+                             // second tab in the WebUI tabstrip.
+                             const gfx::Point start =
+                                 tab_strip
+                                     ->GetElementBoundsInScreen(kSecondTabQuery)
+                                     .CenterPoint();
+
+                             // Endpoint is center of the main webcontents, so
+                             // guaranteed to be outside the tabstrip.
+                             const gfx::Point end = browser()
+                                                        ->tab_strip_model()
+                                                        ->GetActiveWebContents()
+                                                        ->GetContainerBounds()
+                                                        .CenterPoint();
+
+                             // Perform but do not complete the drag.
+                             PerformDragWithoutRelease(start, end,
+                                                       kWebUiTabStripElementId);
+                           })))
+
+          // Wait for the drag to finish and close the tab without releasing the
+          // mouse and actually ending the drag.
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kCustomEvent,
+                                kMouseDragCompleteCustomEvent)
+                       .SetElementID(kWebUiTabStripElementId)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             // For WebUI tab drag, the tab isn't actually
+                             // removed from the tabstrip until the drag
+                             // completes.
+                             EXPECT_EQ(2,
+                                       browser()->tab_strip_model()->count());
+
+                             // Close the new tab.
+                             second_tab->Execute("() => window.close()");
+                           })))
+
+          // Wait for the dragged tab to be closed, verify it is closed, and
+          // release the mouse to finish the drag.
+          //
+          // SetTransitionOnlyOnEvent(true) means the test will fail if the tab
+          // goes away before this step is queued; it will only succeed if the
+          // tab disappears specifically in response to the previous step.
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kHidden)
+                       .SetElementID(kSecondTabElementId)
+                       .SetTransitionOnlyOnEvent(true)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             // The tab should now be removed from the tabstrip
+                             // because it was closed; the drag has not yet
+                             // finished.
+                             EXPECT_EQ(1,
+                                       browser()->tab_strip_model()->count());
+                           })))
+          .Build();
+
+  EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
+
+  // Cleanup.
+
+  // First, send a mouse-up to end the drag.
+  ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
+
+  // Second, due to an interaction between the Linux Ash simulator and certain
+  // Chrome builds, intermittently, a drag operation can start spuriously after
+  // this sequence. Unfortunately, this happens between the test body and the
+  // TearDown() method, which soft-locks the test (see crbug.com/1352602 for
+  // discussion). Install an observer to detect if this happens and cancel the
+  // drag.
+  CancelPendingDrag();
+}
+
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc b/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
index 7521eb0..56f0bc7 100644
--- a/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
+++ b/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
@@ -36,8 +36,9 @@
     views::View* anchor_view,
     base::OnceClosure close_callback,
     base::OnceClosure escape_callback)
-    : views::BubbleDialogDelegateView(anchor_view,
-                                      views::BubbleBorder::Arrow::TOP_CENTER) {
+    : views::BubbleDialogDelegateView(
+          anchor_view,
+          views::BubbleBorder::Arrow::BOTTOM_CENTER) {
   // The cancel close_callback is called when VKEY_ESCAPE is hit.
   SetCancelCallback(std::move(escape_callback));
 
@@ -100,9 +101,9 @@
 
 gfx::Rect LensRegionSearchInstructionsView::GetBubbleBounds() {
   gfx::Rect bubble_rect = views::BubbleDialogDelegateView::GetBubbleBounds();
-  // Since we should be centered and positioned under the toolbar, adjust the
-  // bubble position to contain a top margin to the toolbar.
-  bubble_rect.set_y(bubble_rect.y() +
+  // Since we should be centered and positioned on top of the web view, adjust
+  // the bubble position to contain a top margin to the top container view.
+  bubble_rect.set_y(bubble_rect.y() + bubble_rect.height() +
                     ChromeLayoutProvider::Get()->GetDistanceMetric(
                         DISTANCE_RELATED_CONTROL_VERTICAL_SMALL));
   return bubble_rect;
diff --git a/chrome/browser/ui/views/lens/lens_side_panel_helper.cc b/chrome/browser/ui/views/lens/lens_side_panel_helper.cc
index d6dfbb2..a49a66d3 100644
--- a/chrome/browser/ui/views/lens/lens_side_panel_helper.cc
+++ b/chrome/browser/ui/views/lens/lens_side_panel_helper.cc
@@ -60,10 +60,10 @@
     Browser* browser,
     base::OnceClosure close_callback,
     base::OnceClosure escape_callback) {
-  // Our anchor should be the browser view's top container view. This includes
-  // views such as the toolbar, tab strip, etc.
+  // Our anchor should be the browser view's top container view. This makes sure
+  // that we account for side panel width and the top container view.
   views::View* anchor =
-      BrowserView::GetBrowserViewForBrowser(browser)->top_container();
+      BrowserView::GetBrowserViewForBrowser(browser)->contents_web_view();
   return views::BubbleDialogDelegateView::CreateBubble(
       std::make_unique<LensRegionSearchInstructionsView>(
           anchor, std::move(close_callback), std::move(escape_callback)));
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_container_view.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_container_view.h
index 68e83dd..65c9785a 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_container_view.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_container_view.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
 #include "ui/views/view.h"
 
@@ -31,8 +30,5 @@
   ReadAnythingContainerView& operator=(const ReadAnythingContainerView&) =
       delete;
   ~ReadAnythingContainerView() override;
-
- private:
-  base::WeakPtrFactory<ReadAnythingContainerView> weak_pointer_factory_{this};
 };
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_CONTAINER_VIEW_H_
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
index 54fd25f..d2b60c52 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
@@ -5,10 +5,11 @@
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_container_view.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.h"
@@ -25,13 +26,14 @@
     : BrowserUserData<ReadAnythingCoordinator>(*browser) {
   // Create the model and initialize it with user prefs (if present).
   model_ = std::make_unique<ReadAnythingModel>();
-  InitModelWithUserPrefs(browser);
+  InitModelWithUserPrefs();
 
   // Create the controller.
   controller_ = std::make_unique<ReadAnythingController>(model_.get(), browser);
 }
 
-void ReadAnythingCoordinator::InitModelWithUserPrefs(Browser* browser) {
+void ReadAnythingCoordinator::InitModelWithUserPrefs() {
+  Browser* browser = &GetBrowser();
   if (!browser->profile() || !browser->profile()->GetPrefs())
     return;
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
index 6658c8b..97b9284 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
@@ -58,7 +58,7 @@
   friend class ReadAnythingCoordinatorTest;
 
   // Used during construction to initialize the model with saved user prefs.
-  void InitModelWithUserPrefs(Browser* browser);
+  void InitModelWithUserPrefs();
 
   // SidePanelEntryObserver:
   void OnEntryShown(SidePanelEntry* entry) override;
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
index e8da3fd9..fc61f1d 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -369,6 +369,7 @@
   DCHECK(header_combobox_);
   header_combobox_->SetSelectedIndex(
       combobox_model_->GetIndexForId(entry->id()));
+  header_combobox_->SchedulePaint();
 
   auto* content_wrapper =
       GetContentView()->GetViewByID(kSidePanelContentWrapperViewId);
@@ -522,6 +523,7 @@
   if (GetContentView()) {
     header_combobox_->SetSelectedIndex(combobox_model_->GetIndexForId(
         GetLastActiveEntryId().value_or(kDefaultEntry)));
+    header_combobox_->SchedulePaint();
   }
 }
 
@@ -531,6 +533,7 @@
   if (GetContentView()) {
     header_combobox_->SetSelectedIndex(combobox_model_->GetIndexForId(
         GetLastActiveEntryId().value_or(kDefaultEntry)));
+    header_combobox_->SchedulePaint();
   }
 
   // If the active global entry is the entry being deregistered, reset
@@ -580,6 +583,7 @@
   if (GetContentView()) {
     header_combobox_->SetSelectedIndex(combobox_model_->GetIndexForId(
         GetLastActiveEntryId().value_or(kDefaultEntry)));
+    header_combobox_->SchedulePaint();
 
     if ((!new_contextual_registry ||
          !new_contextual_registry->active_entry().has_value()) &&
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc
index da794a9..1a4c164e 100644
--- a/chrome/browser/ui/views/tabs/tab_container_impl.cc
+++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -649,21 +649,6 @@
 }
 
 void TabContainerImpl::Layout() {
-  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
-    // With tab scrolling, the tab container is solely responsible for its own
-    // width.
-    // It should never be larger than its preferred width.
-    const int max_width = CalculatePreferredSize().width();
-    // It should never be smaller than its minimum width.
-    const int min_width = GetMinimumSize().width();
-    // If it can, it should fit within the tab strip region.
-    const int available_width = GetAvailableWidthForTabContainer();
-    // It should be as wide as possible subject to the above constraints.
-    const int width = std::min(max_width, std::max(min_width, available_width));
-    SetBounds(0, 0, width, GetLayoutConstant(TAB_HEIGHT));
-    SetTabSlotVisibility();
-  }
-
   if (IsAnimating()) {
     // Hide tabs that have animated at least partially out of the clip region.
     SetTabSlotVisibility();
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index ed1e5be..f1aee4a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1789,11 +1789,25 @@
 }
 
 void TabStrip::Layout() {
-  views::View::Layout();
   if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
-    // With tab scrolling, our bounds are driven solely by our TabContainer.
-    SetBoundsRect(tab_container_->bounds());
+    // With tab scrolling, the TabStrip is solely responsible for its own width
+    // (It's the contents view of a ScrollView, and with sizing freedom comes
+    // sizing responsibility).
+
+    // We can figure out our width based on the preferences of our TabContainer
+    // and the available width in the tab strip region:
+    // We should never be larger than our container's preferred width.
+    const int max_width = tab_container_->CalculatePreferredSize().width();
+    // We should never be smaller than our container's minimum width.
+    const int min_width = tab_container_->GetMinimumSize().width();
+    // If we can, we should fit within the tab strip region to avoid scrolling.
+    const int available_width =
+        tab_container_->GetAvailableWidthForTabContainer();
+    // Be as wide as possible subject to the above constraints.
+    const int width = std::min(max_width, std::max(min_width, available_width));
+    SetBounds(0, 0, width, GetLayoutConstant(TAB_HEIGHT));
   }
+  views::View::Layout();
 
   // drag_context_ isn't part of normal layout since it overlays the tabstrip.
   drag_context_->Layout();
diff --git a/chrome/browser/ui/views/user_education/browser_feature_promo_controller_unittest.cc b/chrome/browser/ui/views/user_education/browser_feature_promo_controller_unittest.cc
index 8da02899..2abf5f17 100644
--- a/chrome/browser/ui/views/user_education/browser_feature_promo_controller_unittest.cc
+++ b/chrome/browser/ui/views/user_education/browser_feature_promo_controller_unittest.cc
@@ -67,6 +67,8 @@
                                   base::FEATURE_ENABLED_BY_DEFAULT};
 base::Feature kCustomActionIPHFeature{"CustomActionTestIPHFeature",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
+base::Feature kDefaultCustomActionIPHFeature{
+    "DefaultCustomActionTestIPHFeature", base::FEATURE_ENABLED_BY_DEFAULT};
 constexpr char kTestTutorialIdentifier[] = "Test Tutorial";
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOneOffIPHElementId);
 }  // namespace
@@ -136,7 +138,18 @@
             IDS_REOPEN_TAB_PROMO, IDS_REOPEN_TAB_PROMO,
             base::BindRepeating(
                 &BrowserFeaturePromoControllerTest::OnCustomPromoAction,
-                base::Unretained(this))));
+                base::Unretained(this),
+                base::Unretained(&kCustomActionIPHFeature))));
+
+    auto default_custom = FeaturePromoSpecification::CreateForCustomAction(
+        kDefaultCustomActionIPHFeature, kAppMenuButtonElementId,
+        IDS_REOPEN_TAB_PROMO, IDS_REOPEN_TAB_PROMO,
+        base::BindRepeating(
+            &BrowserFeaturePromoControllerTest::OnCustomPromoAction,
+            base::Unretained(this),
+            base::Unretained(&kDefaultCustomActionIPHFeature)));
+    default_custom.SetCustomActionIsDefault(true);
+    registry()->RegisterFeature(std::move(default_custom));
 
     // Make sure the browser view is visible for the tests.
     browser_view()->GetWidget()->Show();
@@ -197,14 +210,15 @@
         &kTestIPHFeature, kAppMenuButtonElementId, IDS_REOPEN_TAB_PROMO);
   }
 
-  void OnCustomPromoAction(ui::ElementContext context,
+  void OnCustomPromoAction(const base::Feature* feature,
+                           ui::ElementContext context,
                            FeaturePromoHandle promo_handle) {
     ++custom_callback_count_;
     EXPECT_TRUE(promo_handle.is_valid());
-    EXPECT_TRUE(controller_->IsPromoActive(kCustomActionIPHFeature, true));
+    EXPECT_TRUE(controller_->IsPromoActive(*feature, true));
     EXPECT_EQ(browser()->window()->GetElementContext(), context);
     promo_handle.Release();
-    EXPECT_FALSE(controller_->IsPromoActive(kCustomActionIPHFeature, true));
+    EXPECT_FALSE(controller_->IsPromoActive(*feature, true));
   }
 
   raw_ptr<BrowserFeaturePromoController> controller_;
@@ -852,6 +866,26 @@
   EXPECT_EQ(1, custom_callback_count_);
 }
 
+// Test that a feature promo can perform a custom action that is the default.
+TEST_F(BrowserFeaturePromoControllerTest, PerformsCustomActionAsDefault) {
+  // Launch a feature promo that has a tutorial.
+  EXPECT_CALL(*mock_tracker_,
+              ShouldTriggerHelpUI(Ref(kDefaultCustomActionIPHFeature)))
+      .Times(1)
+      .WillOnce(Return(true));
+  ASSERT_TRUE(controller_->MaybeShowPromo(kDefaultCustomActionIPHFeature));
+
+  // Simulate clicking the "Show Tutorial" button.
+  auto* const bubble = GetPromoBubble();
+  ASSERT_TRUE(bubble);
+  views::test::WidgetDestroyedWaiter waiter(bubble->GetWidget());
+  views::test::InteractionTestUtilSimulatorViews::PressButton(
+      bubble->GetDefaultButtonForTesting());
+  waiter.Wait();
+
+  EXPECT_EQ(1, custom_callback_count_);
+}
+
 // Test that a feature promo does not perform a custom action when the default
 // "Got it" button is clicked.
 TEST_F(BrowserFeaturePromoControllerTest, DoesNotPerformCustomAction) {
@@ -872,6 +906,27 @@
   EXPECT_EQ(0, custom_callback_count_);
 }
 
+// Test that a feature promo does not perform a custom action when a non-default
+// "Got it" button is clicked.
+TEST_F(BrowserFeaturePromoControllerTest, DoesNotPerformDefaultCustomAction) {
+  // Launch a feature promo that has a tutorial.
+  EXPECT_CALL(*mock_tracker_,
+              ShouldTriggerHelpUI(Ref(kDefaultCustomActionIPHFeature)))
+      .Times(1)
+      .WillOnce(Return(true));
+  ASSERT_TRUE(controller_->MaybeShowPromo(kDefaultCustomActionIPHFeature));
+
+  // Simulate clicking the "Show Tutorial" button.
+  auto* const bubble = GetPromoBubble();
+  ASSERT_TRUE(bubble);
+  views::test::WidgetDestroyedWaiter waiter(bubble->GetWidget());
+  views::test::InteractionTestUtilSimulatorViews::PressButton(
+      bubble->GetNonDefaultButtonForTesting(0));
+  waiter.Wait();
+
+  EXPECT_EQ(0, custom_callback_count_);
+}
+
 TEST_F(BrowserFeaturePromoControllerTest, GetAnchorContext) {
   EXPECT_EQ(browser_view()->GetElementContext(),
             controller_->GetAnchorContext());
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index b836786..e5dc15d2 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -128,7 +128,7 @@
             base::Value(ash::TabletMode::Get()->InTabletMode()));
   dict->Set("isDemoModeEnabled",
             base::Value(DemoSetupController::IsDemoModeAllowed()));
-  if (policy::EnrollmentRequisitionManager::IsRemoraRequisition()) {
+  if (policy::EnrollmentRequisitionManager::IsMeetDevice()) {
     dict->Set("flowType", base::Value("meet"));
   }
   dict->Set("isQuickStartEnabled",
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h
index 8888458..54df1ed 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_model.h"
@@ -65,7 +64,6 @@
 
   mojo::Receiver<read_anything::mojom::PageHandler> receiver_;
   mojo::Remote<read_anything::mojom::Page> page_;
-  base::WeakPtrFactory<ReadAnythingPageHandler> weak_pointer_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_PAGE_HANDLER_H_
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.cc b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
index 28d9ff83..425ad6c 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command.cc
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/commands/web_app_command.h"
 #include "chrome/browser/web_applications/locks/shared_web_contents_with_app_lock.h"
 #include "chrome/browser/web_applications/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
@@ -56,7 +57,7 @@
     WebAppInstallFinalizer& install_finalizer,
     base::OnceCallback<void(InstallIsolatedAppCommandResult)> callback)
     : lock_(std::make_unique<SharedWebContentsWithAppLock>(
-          base::flat_set<AppId>{"some random app id"})),
+          base::flat_set<AppId>{GenerateAppId("/", GURL{url})})),
       url_(url),
       url_loader_(url_loader),
       install_finalizer_(install_finalizer),
@@ -77,9 +78,7 @@
   data_retriever_ = std::move(data_retriever);
 }
 
-InstallIsolatedAppCommand::~InstallIsolatedAppCommand() {
-  DCHECK(callback_.is_null());
-}
+InstallIsolatedAppCommand::~InstallIsolatedAppCommand() = default;
 
 Lock& InstallIsolatedAppCommand::lock() const {
   return *lock_;
@@ -148,13 +147,17 @@
 
   // In other installations the best-effort encoding is fine, but for isolated
   // apps we have the opportunity to report this error.
-  if (absl::optional<std::string> encoded_id = UTF16ToUTF8(*manifest.id);
-      encoded_id.has_value()) {
-    info.manifest_id = *encoded_id;
-  } else {
+  absl::optional<std::string> encoded_id = UTF16ToUTF8(*manifest.id);
+  if (!encoded_id.has_value()) {
     return absl::nullopt;
   }
 
+  if (*encoded_id != "/") {
+    return absl::nullopt;
+  }
+
+  info.manifest_id = *encoded_id;
+
   return info;
 }
 
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.h b/chrome/browser/web_applications/commands/install_isolated_app_command.h
index 54440ec..aef3db21 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command.h
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command.h
@@ -38,6 +38,12 @@
   // TODO(kuragin): Consider to create an instance of |GURL| instead of passing
   // a string and probably introduce factory function in order to handle invalid
   // urls.
+  //
+  // |application_url| is the url for the app to be installed.
+  //
+  // |callback| must be not null.
+  //
+  // The `id` in the application's manifest must equal "/".
   explicit InstallIsolatedAppCommand(
       base::StringPiece application_url,
       WebAppUrlLoader& url_loader,
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
index 5d32976..8bf2883 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_future.h"
+#include "chrome/browser/web_applications/locks/lock.h"
 #include "chrome/browser/web_applications/test/fake_install_finalizer.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/mock_data_retriever.h"
@@ -26,6 +27,7 @@
 #include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
@@ -58,11 +60,12 @@
 using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Pointee;
+using ::testing::Property;
 using ::testing::UnorderedElementsAre;
 
 blink::mojom::ManifestPtr CreateDefaultManifest() {
   auto manifest = blink::mojom::Manifest::New();
-  manifest->id = u"some default test manifest id";
+  manifest->id = u"/";
   manifest->start_url = GURL{"http://default-test.com/"},
   manifest->scope = GURL{"/scope"},
   manifest->display = DisplayMode::kStandalone;
@@ -235,7 +238,10 @@
               IsInstallationOk());
 }
 
-TEST_F(InstallIsolatedAppCommandTest, ReportsErrorWhenURLIsInvalid) {
+// It is impossible to pass invalid url to |GenerateAppId| since it DCHECKs.
+// TODO(kuragin): Replace constructor with factory function to make the
+// validation testable.
+TEST_F(InstallIsolatedAppCommandTest, DISABLED_ReportsErrorWhenURLIsInvalid) {
   SetPrepareForLoadResultLoaded();
 
   EXPECT_THAT(ExecuteCommand("some definetely invalid url"),
@@ -296,6 +302,17 @@
               Not(IsInstallationOk()));
 }
 
+TEST_F(InstallIsolatedAppCommandTest, CommandLocksOnAppIdAndWebContents) {
+  base::test::TestFuture<InstallIsolatedAppCommandResult> test_future;
+  auto command =
+      CreateCommand("http://test-app-id.com/", test_future.GetCallback());
+  EXPECT_THAT(command->lock(),
+              AllOf(Property(&Lock::type, Eq(Lock::Type::kAppAndWebContents)),
+                    Property(&Lock::app_ids,
+                             UnorderedElementsAre(GenerateAppIdFromUnhashed(
+                                 "http://test-app-id.com//")))));
+}
+
 TEST_F(InstallIsolatedAppCommandTest,
        InstallationFailsWhenAppIsInstallableButManifestIsNull) {
   SetPrepareForLoadResultLoaded();
@@ -341,15 +358,25 @@
               Not(IsInstallationOk()));
 }
 
-TEST_F(InstallIsolatedAppCommandManifestTest, PassesManifestIdToFinalizer) {
+TEST_F(InstallIsolatedAppCommandManifestTest,
+       PassesManifestIdToFinalizerWhenManifestIdIsSlash) {
   blink::mojom::ManifestPtr manifest = CreateDefaultManifest();
-  manifest->id = u"test manifest id";
+  manifest->id = u"/";
 
   EXPECT_THAT(ExecuteCommandWithManifest(manifest.Clone()), IsInstallationOk());
 
   EXPECT_THAT(install_finalizer().web_app_info(),
               Pointee(Field(&WebAppInstallInfo::manifest_id,
-                            Optional(std::string{"test manifest id"}))));
+                            Optional(std::string{"/"}))));
+}
+
+TEST_F(InstallIsolatedAppCommandManifestTest, FailsWhenManifestIdIsNotSlash) {
+  blink::mojom::ManifestPtr manifest = CreateDefaultManifest();
+  manifest->id = u"test-manifest-id";
+
+  EXPECT_THAT(ExecuteCommandWithManifest(manifest.Clone()),
+              Not(IsInstallationOk()));
+  EXPECT_THAT(install_finalizer().web_app_info(), IsNull());
 }
 
 TEST_F(InstallIsolatedAppCommandManifestTest, PassesManifestNameAsTitle) {
@@ -417,7 +444,11 @@
               BucketsAre(base::Bucket(true, 1)));
 }
 
-TEST_F(InstallIsolatedAppCommandMetricsTest, ReportFailureWhenURLIsInvalid) {
+// It is impossible to pass invalid url to |GenerateAppId| since it DCHECKs.
+// TODO(kuragin): Replace constructor with factory function to make the
+// validation testable.
+TEST_F(InstallIsolatedAppCommandMetricsTest,
+       DISABLED_ReportFailureWhenURLIsInvalid) {
   SetPrepareForLoadResultLoaded();
 
   base::HistogramTester histogram_tester;
@@ -497,5 +528,18 @@
               BucketsAre(base::Bucket(false, 1)));
 }
 
+TEST_F(InstallIsolatedAppCommandMetricsTest,
+       ReportFailureWhenManifestIdIsNotSlash) {
+  blink::mojom::ManifestPtr manifest = CreateDefaultManifest();
+  manifest->id = u"test manifest id";
+
+  base::HistogramTester histogram_tester;
+
+  EXPECT_THAT(ExecuteCommandWithManifest(manifest.Clone()),
+              Not(IsInstallationOk()));
+  EXPECT_THAT(histogram_tester.GetAllSamples("WebApp.Install.Result"),
+              BucketsAre(base::Bucket(false, 1)));
+}
+
 }  // namespace
 }  // namespace web_app
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index b91ef55..a10e091 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1660650975-0429cc669493803dc7829079efcbdbb7910f7af8.profdata
+chrome-linux-main-1660669594-e1a98eac0424f7882f9f051b0b6d4d8ed43d1852.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 3da6c4c3..516145b 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1660650975-78f2abc50319e2e21df10a82759f3c1451ffd523.profdata
+chrome-mac-main-1660669594-ab66c8ad86c45aa98b2a82524a1be164c1c793cf.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index a35f91e..d2dd38c 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2254,10 +2254,10 @@
 const char kNtlmV2Enabled[] = "auth.ntlm_v2_enabled";
 #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 // Boolean whether Kerberos functionality is enabled.
 const char kKerberosEnabled[] = "kerberos.enabled";
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Boolean that specifies whether to enable revocation checking (best effort)
 // by default.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index e711a04..ffaf5fa 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -77,7 +77,7 @@
 #endif  // BUILDFLAG(ENABLE_RLZ)
 
 // For OS_CHROMEOS we maintain the kApplicationLocale property in both local
-// state and the user's profile.  The global property determines the locale of
+// state and the user's profile. The global property determines the locale of
 // the login screen, while the user's profile determines their personal locale
 // preference.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -816,7 +816,6 @@
 extern const char kKnownUserParentAccessCodeConfig[];
 extern const char kLastRsuDeviceIdUploaded[];
 extern const char kDeviceName[];
-
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 extern const char kClearPluginLSODataEnabled[];
@@ -853,9 +852,9 @@
 extern const char kNtlmV2Enabled[];
 #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 extern const char kKerberosEnabled[];
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 extern const char kCertRevocationCheckingEnabled[];
 extern const char kCertRevocationCheckingRequiredLocalAnchors[];
diff --git a/chrome/common/safe_browsing/binary_feature_extractor.cc b/chrome/common/safe_browsing/binary_feature_extractor.cc
index 49c0cae0..1d58f92 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor.cc
@@ -10,6 +10,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
+#include "base/metrics/histogram_functions.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
@@ -26,8 +27,11 @@
     ClientDownloadRequest_ImageHeaders* image_headers,
     google::protobuf::RepeatedPtrField<std::string>* signed_data) {
   base::MemoryMappedFile mapped_file;
+  base::Time start_time = base::Time::Now();
   if (!mapped_file.Initialize(file_path))
     return false;
+  base::UmaHistogramMediumTimes("SBClientDownload.MemoryMapFileDuration",
+                                base::Time::Now() - start_time);
   return ExtractImageFeaturesFromData(mapped_file.data(), mapped_file.length(),
       options, image_headers, signed_data);
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5d9fcf5..8963d3d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5386,7 +5386,6 @@
     "../browser/resource_coordinator/tab_load_tracker_unittest.cc",
     "../browser/resources_util_unittest.cc",
     "../browser/security_events/security_event_recorder_impl_unittest.cc",
-    "../browser/segmentation_platform/model_provider_factory_impl_unittest.cc",
     "../browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc",
     "../browser/signin/e2e_tests/test_accounts_util_unittest.cc",
 
@@ -6315,7 +6314,6 @@
       "../browser/metrics/chrome_android_metrics_provider_unittest.cc",
       "../browser/metrics/family_link_user_metrics_provider_unittest.cc",
       "../browser/notifications/notification_channels_provider_android_unittest.cc",
-      "../browser/optimization_guide/android/android_push_notification_manager_unittest.cc",
       "../browser/optimization_guide/android/optimization_guide_bridge_unittest.cc",
       "../browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc",
       "../browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
index 375c84f..cfaeb49 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
@@ -7,7 +7,7 @@
 import 'chrome://personalization/strings.m.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {CurrentWallpaper, DailyRefreshType, DisplayableImage, WallpaperFullscreen, WallpaperLayout, WallpaperType} from 'chrome://personalization/js/personalization_app.js';
+import {CurrentWallpaper, DailyRefreshType, DisplayableImage, WallpaperFullscreen, WallpaperLayout, WallpaperObserver, WallpaperType} from 'chrome://personalization/js/personalization_app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
@@ -346,4 +346,39 @@
             ?.getAttribute('aria-label'),
         'exit button aria label is set');
   });
+
+  test('exits fullscreen on popstate', async () => {
+    WallpaperObserver.initWallpaperObserverIfNeeded();
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
+    const {requestFullscreenPromise, exitFullscreenPromise} =
+        mockFullscreenApis();
+    await waitAfterNextRender(wallpaperFullscreenElement);
+
+    // Add a history entry to pop later.
+    window.history.pushState(null, '', window.location.href + '#test');
+
+    personalizationStore.data.wallpaper.fullscreen = true;
+    personalizationStore.notifyObservers();
+
+    await requestFullscreenPromise;
+
+    personalizationStore.setReducersEnabled(true);
+    window.history.back();
+
+    // Triggered by popstate.
+    await wallpaperProvider.whenCalled('cancelPreviewWallpaper');
+
+    // Simulate the response from wallpaper controller.
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
+        wallpaperProvider.currentWallpaper);
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperPreviewEnded();
+    // Second |onWallpaperChanged| is generated by
+    // |personalization_app_wallpaper_provider_impl.cc|.
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
+        wallpaperProvider.currentWallpaper);
+
+    // |exitFullscreenPromise| from wallpaper preview being canceled.
+    await exitFullscreenPromise;
+    WallpaperObserver.shutdown();
+  });
 });
diff --git a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
index ec222b2..bacbfcd0f 100644
--- a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
+++ b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {MiddleSlotPromoElement} from 'chrome://new-tab-page/lazy_load.js';
+import {MiddleSlotPromoElement, PromoDismissAction} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, BrowserCommandProxy, CrAutoImgElement, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
@@ -15,18 +15,20 @@
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 
+import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js';
 import {installMock} from './test_support.js';
 
 suite('NewTabPageMiddleSlotPromoTest', () => {
   let newTabPageHandler: TestBrowserProxy;
   let promoBrowserCommandHandler: TestBrowserProxy;
+  let metrics: MetricsTracker;
 
   setup(() => {
     document.body.innerHTML = '';
+    metrics = fakeMetricsPrivate();
     newTabPageHandler = installMock(
         PageHandlerRemote,
         mock => NewTabPageProxy.setInstance(mock, new PageCallbackRouter()));
-
     promoBrowserCommandHandler = installMock(
         CommandHandlerRemote,
         mock => BrowserCommandProxy.setInstance({handler: mock}));
@@ -208,9 +210,13 @@
       const dismissPromoButton = parts[1] as HTMLElement;
       dismissPromoButton.click();
       assertEquals(true, middleSlotPromo.$.promoAndDismissContainer.hidden);
+      assertEquals(
+          1,
+          metrics.count(
+              'NewTabPage.Promos.DismissAction', PromoDismissAction.DISMISS));
     });
 
-    test(`clicking dismiss button dismisses promo`, async () => {
+    test(`clicking undo button restores promo`, async () => {
       const canShowPromo = true;
       const middleSlotPromo = await createMiddleSlotPromo(canShowPromo);
       assertHasContent(canShowPromo, middleSlotPromo);
@@ -220,9 +226,17 @@
       const dismissPromoButton = parts[1] as HTMLElement;
       dismissPromoButton.click();
       assertEquals(true, middleSlotPromo.$.promoAndDismissContainer.hidden);
+      assertEquals(
+          1,
+          metrics.count(
+              'NewTabPage.Promos.DismissAction', PromoDismissAction.DISMISS));
 
       middleSlotPromo.$.undoDismissPromoButton.click();
       assertEquals(false, middleSlotPromo.$.promoAndDismissContainer.hidden);
+      assertEquals(
+          1,
+          metrics.count(
+              'NewTabPage.Promos.DismissAction', PromoDismissAction.RESTORE));
     });
   });
 });
diff --git a/chromecast/cast_core/grpc/server_reactor_tracker.h b/chromecast/cast_core/grpc/server_reactor_tracker.h
index 10632492..8b7fe99f 100644
--- a/chromecast/cast_core/grpc/server_reactor_tracker.h
+++ b/chromecast/cast_core/grpc/server_reactor_tracker.h
@@ -5,6 +5,8 @@
 #ifndef CHROMECAST_CAST_CORE_GRPC_SERVER_REACTOR_TRACKER_H_
 #define CHROMECAST_CAST_CORE_GRPC_SERVER_REACTOR_TRACKER_H_
 
+#include <string>
+
 #include "base/containers/flat_map.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
diff --git a/chromeos/crosapi/mojom/probe_service.mojom b/chromeos/crosapi/mojom/probe_service.mojom
index ba83946d..5b07be6 100644
--- a/chromeos/crosapi/mojom/probe_service.mojom
+++ b/chromeos/crosapi/mojom/probe_service.mojom
@@ -46,8 +46,7 @@
 
 // Interface for getting device telemetry information. Implemented in
 // ash-chrome.
-[Stable, Uuid="f3dbbe3b-a810-43a9-889e-b130b4f94869",
-RenamedFrom="crosapi.mojom.ProbeService"]
+[Stable, Uuid="f3dbbe3b-a810-43a9-889e-b130b4f94869"]
 interface TelemetryProbeService {
   // Returns telemetry information for the desired categories.
   //
@@ -72,7 +71,7 @@
 // An enumeration of each category of information that cros_healthd can report.
 //
 // Next ID: 12
-[Stable, Extensible, RenamedFrom="ash.health.mojom.ProbeCategoryEnum"]
+[Stable, Extensible]
 enum ProbeCategoryEnum {
   [Default] kUnknown = 11,
   kBattery = 0,
@@ -92,7 +91,7 @@
 // probing telemetry information.
 //
 // Next ID: 5
-[Stable, Extensible, RenamedFrom="ash.health.mojom.ErrorType"]
+[Stable, Extensible]
 enum ProbeErrorType {
   // Default value.
   [Default] kUnknown = 4,
@@ -109,7 +108,7 @@
 // Structure that contains error information for a telemetry probe.
 //
 // Next ID: 2
-[Stable, RenamedFrom="ash.health.mojom.ProbeError"]
+[Stable]
 struct ProbeError {
   // The type of error that occurred.
   ProbeErrorType type@0;
@@ -121,7 +120,7 @@
 // Information related to the main battery.
 //
 // Next ID: 14
-[Stable, RenamedFrom="ash.health.mojom.BatteryInfo"]
+[Stable]
 struct ProbeBatteryInfo {
   // Cycle count.
   Int64Value? cycle_count@0;
@@ -159,7 +158,7 @@
 
 // Battery probe result. Can either be populated with the BatteryInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.BatteryResult"]
+[Stable]
 union ProbeBatteryResult {
   // Valid BatteryInfo. Null value if a battery is not present.
   ProbeBatteryInfo? battery_info;
@@ -170,7 +169,7 @@
 // Information related to a specific non-removable block device.
 //
 // Next ID: 12
-[Stable, RenamedFrom="ash.health.mojom.NonRemovableBlockDeviceInfo"]
+[Stable]
 struct ProbeNonRemovableBlockDeviceInfo {
   // The path of this storage on the system. It is useful if caller needs to
   // correlate with other information.
@@ -205,7 +204,7 @@
 
 // Non-removable block device probe result. Can either be populated with the
 // NonRemovableBlockDeviceInfo or an error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.NonRemovableBlockDeviceResult"]
+[Stable]
 union ProbeNonRemovableBlockDeviceResult {
   // Valid NonRemovableBlockDeviceInfo.
   array<ProbeNonRemovableBlockDeviceInfo> block_device_info;
@@ -217,7 +216,7 @@
 // Cached VPD read from sysfs.
 //
 // Next ID: 4
-[Stable, RenamedFrom="ash.health.mojom.CachedVpdInfo"]
+[Stable]
 struct ProbeCachedVpdInfo {
   // Contents of /sys/firmware/vpd/rw/ActivateDate, if the device supports it.
   string? first_power_date@0;
@@ -231,7 +230,7 @@
 
 // Cached VPD probe result. Can either be populated with the CachedVpdInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.CachedVpdResult"]
+[Stable]
 union ProbeCachedVpdResult {
   // Valid CachedVpdInfo.
   ProbeCachedVpdInfo vpd_info;
@@ -242,7 +241,7 @@
 // Information about a CPU's C-states.
 //
 // Next ID: 2
-[Stable, RenamedFrom="ash.health.mojom.CpuCStateInfo"]
+[Stable]
 struct ProbeCpuCStateInfo {
   // Name of the state.
   string? name@0;
@@ -253,7 +252,7 @@
 // Information related to a particular logical CPU.
 //
 // Next ID: 5
-[Stable, RenamedFrom="ash.health.mojom.LogicalCpuInfo"]
+[Stable]
 struct ProbeLogicalCpuInfo {
   // The max CPU clock speed in kHz.
   UInt32Value? max_clock_speed_khz@0;
@@ -270,7 +269,7 @@
 // Information related to a particular physical CPU.
 //
 // Next ID: 2
-[Stable, RenamedFrom="ash.health.mojom.PhysicalCpuInfo"]
+[Stable]
 struct ProbePhysicalCpuInfo {
   // The CPU model name.
   string? model_name@0;
@@ -281,7 +280,7 @@
 // An enumeration of CPU architectures.
 //
 // Next ID: 4
-[Stable, Extensible, RenamedFrom="ash.health.mojom.CpuArchitectureEnum"]
+[Stable, Extensible]
 enum ProbeCpuArchitectureEnum {
   [Default] kUnknown = 3,
   kX86_64 = 0,
@@ -292,7 +291,7 @@
 // Information about the device's CPUs.
 //
 // Next ID: 3
-[Stable, RenamedFrom="ash.health.mojom.CpuInfo"]
+[Stable]
 struct ProbeCpuInfo {
   // Number of total threads available.
   UInt32Value? num_total_threads@0;
@@ -305,7 +304,7 @@
 
 // CPU probe result. Can either be populated with the CpuInfo or an error
 // retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.CpuResult"]
+[Stable]
 union ProbeCpuResult {
   // Valid CpuInfo.
   ProbeCpuInfo cpu_info;
@@ -316,7 +315,7 @@
 // Timezone information.
 //
 // Next ID: 2
-[Stable, RenamedFrom="ash.health.mojom.TimezoneInfo"]
+[Stable]
 struct ProbeTimezoneInfo {
   // The timezone of the device in POSIX standard.
   string? posix@0;
@@ -326,7 +325,7 @@
 
 // Timezone probe result. Can either be populated with the TimezoneInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.TimezoneResult"]
+[Stable]
 union ProbeTimezoneResult {
   // Valid TimezoneInfo.
   ProbeTimezoneInfo timezone_info;
@@ -337,7 +336,7 @@
 // Memory information.
 //
 // Next ID: 4
-[Stable, RenamedFrom="ash.health.mojom.MemoryInfo"]
+[Stable]
 struct ProbeMemoryInfo {
   // Total memory, in KiB.
   UInt32Value? total_memory_kib@0;
@@ -351,7 +350,7 @@
 
 // Memory probe result. Can either be populated with the MemoryInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.MemoryResult"]
+[Stable]
 union ProbeMemoryResult {
   // Valid MemoryInfo.
   ProbeMemoryInfo memory_info;
@@ -362,7 +361,7 @@
 // Backlight information.
 //
 // Next ID: 3
-[Stable, RenamedFrom="ash.health.mojom.BacklightInfo"]
+[Stable]
 struct ProbeBacklightInfo {
   // Path to this backlight on the system. Useful if the caller needs to
   // correlate with other information.
@@ -375,7 +374,7 @@
 
 // Backlight probe result. Can either be populated with the BacklightInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.BacklightResult"]
+[Stable]
 union ProbeBacklightResult {
   // Valid BacklightInfo.
   array<ProbeBacklightInfo> backlight_info;
@@ -384,7 +383,7 @@
 };
 
 // Fan information.
-[Stable, RenamedFrom="ash.health.mojom.FanInfo"]
+[Stable]
 struct ProbeFanInfo {
   // Fan speed in RPM.
   UInt32Value? speed_rpm@0;
@@ -392,7 +391,7 @@
 
 // Fan probe result. Can either be populated with the FanInfo or an error
 // retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.FanResult"]
+[Stable]
 union ProbeFanResult {
   // A list of valid FanInfo.
   array<ProbeFanInfo> fan_info;
@@ -403,7 +402,7 @@
 // Stateful partition info
 //
 // Next ID: 2
-[Stable, RenamedFrom="ash.health.mojom.StatefulPartitionInfo"]
+[Stable]
 struct ProbeStatefulPartitionInfo {
   // Available space for user data storage in the device in bytes. Rounded down
   // to multiples of 100MiB (100 * 1024 * 1024 bytes).
@@ -414,7 +413,7 @@
 
 // Stateful partition probe result. Can either be populated with a valid
 // StatefulPartitionInfo or an error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.StatefulPartitionResult"]
+[Stable]
 union ProbeStatefulPartitionResult {
   // A valid StatefulPartitionInfo.
   ProbeStatefulPartitionInfo partition_info;
@@ -425,7 +424,7 @@
 // Information related to one of a device's Bluetooth adapters.
 //
 // Next ID: 4
-[Stable, RenamedFrom="ash.health.mojom.BluetoothAdapterInfo"]
+[Stable]
 struct ProbeBluetoothAdapterInfo {
   // The name of the adapter.
   string? name@0;
@@ -439,7 +438,7 @@
 
 // Bluetooth probe result. Can either be populated with the BluetoothAdapterInfo
 // or an error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.BluetoothResult"]
+[Stable]
 union ProbeBluetoothResult {
   // Valid BluetoothAdapterInfo.
   array<ProbeBluetoothAdapterInfo> bluetooth_adapter_info;
@@ -450,7 +449,7 @@
 // OS Version information.
 // This structure decomposes a full version string
 // (e.g. "87.13544.59.0") into its parts.
-[Stable, RenamedFrom="ash.health.mojom.OsVersion"]
+[Stable]
 struct ProbeOsVersion {
   // The OS version release milestone (e.g. "87").
   string? release_milestone@0;
@@ -463,7 +462,7 @@
 };
 
 // The OS information.
-[Stable, RenamedFrom="ash.health.mojom.OsInfo"]
+[Stable]
 struct ProbeOsInfo {
   // Contents of CrosConfig in /branding/oem-name.
   string? oem_name@0;
@@ -472,7 +471,7 @@
 };
 
 // System Information.
-[Stable, RenamedFrom="ash.health.mojom.SystemInfo"]
+[Stable]
 struct ProbeSystemInfo {
   // The info related to the OS.
   ProbeOsInfo os_info@0;
@@ -480,7 +479,7 @@
 
 // System probe result. Can either be populated with the SystemInfo or an
 // error retrieving the information.
-[Stable, RenamedFrom="ash.health.mojom.SystemResult"]
+[Stable]
 union ProbeSystemResult {
   // Valid SystemInfo.
   ProbeSystemInfo system_info;
@@ -497,7 +496,7 @@
 // to fetch that information, but was unable to.
 //
 // Next ID: 11
-[Stable, RenamedFrom="ash.health.mojom.TelemetryInfo"]
+[Stable]
 struct ProbeTelemetryInfo {
   // Information about the device's main battery. Only present when kBattery was
   // included in the categories input to ProbeTelemetryInfo.
@@ -537,7 +536,7 @@
 };
 
 // Result of running /usr/share/cros/oemdata.sh script.
-[Stable, RenamedFrom="ash.health.mojom.OemData"]
+[Stable]
 struct ProbeOemData {
   string? oem_data@0;
 };
diff --git a/chromeos/ui/base/window_state_type.cc b/chromeos/ui/base/window_state_type.cc
index 9ccc4e10..e2aa6e46f 100644
--- a/chromeos/ui/base/window_state_type.cc
+++ b/chromeos/ui/base/window_state_type.cc
@@ -26,8 +26,6 @@
       return stream << "kLeftSnapped";
     case WindowStateType::kSecondarySnapped:
       return stream << "kRightSnapped";
-    case WindowStateType::kAutoPositioned:
-      return stream << "kAutoPositioned";
     case WindowStateType::kPinned:
       return stream << "kPinned";
     case WindowStateType::kTrustedPinned:
@@ -69,7 +67,6 @@
     case WindowStateType::kNormal:
     case WindowStateType::kSecondarySnapped:
     case WindowStateType::kPrimarySnapped:
-    case WindowStateType::kAutoPositioned:
     case WindowStateType::kPip:
     case WindowStateType::kFloated:
       return ui::SHOW_STATE_NORMAL;
diff --git a/chromeos/ui/base/window_state_type.h b/chromeos/ui/base/window_state_type.h
index d5b45343..3bf6c02d 100644
--- a/chromeos/ui/base/window_state_type.h
+++ b/chromeos/ui/base/window_state_type.h
@@ -31,11 +31,6 @@
   kPrimarySnapped,
   kSecondarySnapped,
 
-  // A window is in this state when it is automatically placed and
-  // sized by the window manager. (it's newly opened, or pushed to the side
-  // due to new window, for example).
-  kAutoPositioned,
-
   // A window is pinned on top of other windows with fullscreenized.
   // Corresponding shelf should be hidden, also most of windows other than the
   // pinned one should be hidden.
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 8e39982..8c12f8b 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -115,17 +115,6 @@
   return render_frame_host_->GetRenderViewHost() != nullptr;
 }
 
-webauthn::InternalAuthenticator*
-ContentAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
-  if (!authenticator_impl_ && autofill_manager_ &&
-      autofill_manager_->client()) {
-    authenticator_impl_ =
-        autofill_manager_->client()->CreateCreditCardInternalAuthenticator(
-            render_frame_host_);
-  }
-  return authenticator_impl_.get();
-}
-
 void ContentAutofillDriver::PopupHidden() {
   // If the unmask prompt is shown, keep showing the preview. The preview
   // will be cleared when the prompt closes.
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 127592a..fc6caef 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -18,7 +18,6 @@
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/common/form_data_predictions.h"
 #include "components/autofill_assistant/core/public/autofill_assistant_intent.h"
-#include "components/webauthn/core/browser/internal_authenticator.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
@@ -141,8 +140,6 @@
   ui::AXTreeID GetAxTreeId() const override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
-  webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator()
-      override;
   void HandleParsedForms(const std::vector<FormData>& forms) override {}
   void PopupHidden() override;
   net::IsolationInfo IsolationInfo() override;
@@ -388,9 +385,6 @@
   // code.
   std::unique_ptr<AutofillManager> autofill_manager_ = nullptr;
 
-  // Pointer to an implementation of InternalAuthenticator.
-  std::unique_ptr<webauthn::InternalAuthenticator> authenticator_impl_;
-
   content::RenderWidgetHost::KeyPressEventCallback key_press_handler_;
 
   mojo::AssociatedReceiver<mojom::AutofillDriver> receiver_{this};
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index dfe158d..76e714f 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -112,8 +112,7 @@
 
 #if !BUILDFLAG(IS_IOS)
 std::unique_ptr<webauthn::InternalAuthenticator>
-AutofillClient::CreateCreditCardInternalAuthenticator(
-    content::RenderFrameHost* rfh) {
+AutofillClient::CreateCreditCardInternalAuthenticator(AutofillDriver* driver) {
   return nullptr;
 }
 #endif
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 5edf5cc..5dee1a1 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -36,10 +36,6 @@
 
 class PrefService;
 
-namespace content {
-class RenderFrameHost;
-}
-
 namespace signin {
 class IdentityManager;
 }
@@ -396,7 +392,7 @@
   // null for platforms that don't support this, in which case standard CVC
   // authentication will be used instead.
   virtual std::unique_ptr<webauthn::InternalAuthenticator>
-  CreateCreditCardInternalAuthenticator(content::RenderFrameHost* rfh);
+  CreateCreditCardInternalAuthenticator(AutofillDriver* driver);
 #endif
 
   // Causes the Autofill settings UI to be shown. If |show_credit_card_settings|
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index e048710..7d4b9b6 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -17,10 +17,6 @@
 #include "ui/accessibility/ax_tree_id.h"
 #include "url/origin.h"
 
-#if !BUILDFLAG(IS_IOS)
-#include "components/webauthn/core/browser/internal_authenticator.h"
-#endif
-
 namespace network {
 class SharedURLLoaderFactory;
 }
@@ -90,12 +86,6 @@
   // Returns true iff the renderer is available for communication.
   virtual bool RendererIsAvailable() = 0;
 
-#if !BUILDFLAG(IS_IOS)
-  // Gets or creates a pointer to an implementation of InternalAuthenticator.
-  virtual webauthn::InternalAuthenticator*
-  GetOrCreateCreditCardInternalAuthenticator() = 0;
-#endif
-
   // Forwards |data| to the renderer which shall preview or fill the values of
   // |data|'s fields into the relevant DOM elements.
   //
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index fedb246..c33ab84 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -778,7 +778,6 @@
   friend class AutofillAssistantTest;
   friend class AutofillMetricsCrossFrameFormTest;
   friend class BrowserAutofillManagerTest;
-  friend class AutofillMetricsTest;
   friend class metrics::AutofillMetricsBaseTest;
   friend class FormStructureBrowserTest;
   friend class GetMatchingTypesTest;
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index ab3aa271..a2311a5 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -824,18 +824,16 @@
 }
 
 webauthn::InternalAuthenticator* CreditCardFIDOAuthenticator::authenticator() {
-  if (authenticator_)
-    return authenticator_;
-
-  authenticator_ =
-      autofill_driver_->GetOrCreateCreditCardInternalAuthenticator();
-
-  // |authenticator_| may be null for unsupported platforms.
-  if (authenticator_) {
-    authenticator()->SetEffectiveOrigin(
-        url::Origin::Create(payments::GetBaseSecureUrl()));
+  if (!authenticator_) {
+    authenticator_ = autofill_client_->CreateCreditCardInternalAuthenticator(
+        autofill_driver_.get());
+    // `authenticator_` may be null for unsupported platforms.
+    if (authenticator_) {
+      authenticator_->SetEffectiveOrigin(
+          url::Origin::Create(payments::GetBaseSecureUrl()));
+    }
   }
-
-  return authenticator_;
+  return authenticator_.get();
 }
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index 0bda2fe8..dbf8622 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -263,7 +263,7 @@
   const raw_ptr<payments::PaymentsClient> payments_client_;
 
   // Authenticator pointer to facilitate WebAuthn.
-  raw_ptr<webauthn::InternalAuthenticator> authenticator_ = nullptr;
+  std::unique_ptr<webauthn::InternalAuthenticator> authenticator_;
 
   // Responsible for getting the full card details, including the PAN and the
   // CVC.
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index 3202c80..8310d83 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -128,7 +128,7 @@
 #if !BUILDFLAG(IS_IOS)
 std::unique_ptr<webauthn::InternalAuthenticator>
 TestAutofillClient::CreateCreditCardInternalAuthenticator(
-    content::RenderFrameHost* rfh) {
+    AutofillDriver* driver) {
   return std::make_unique<TestInternalAuthenticator>();
 }
 #endif
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 010c366..b0743bb 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -75,8 +75,7 @@
   std::string GetVariationConfigCountryCode() const override;
 #if !BUILDFLAG(IS_IOS)
   std::unique_ptr<webauthn::InternalAuthenticator>
-  CreateCreditCardInternalAuthenticator(
-      content::RenderFrameHost* driver) override;
+  CreateCreditCardInternalAuthenticator(AutofillDriver* driver) override;
 #endif
 
   void ShowAutofillSettings(bool show_credit_card_settings) override;
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index a5e7116..d6f20469 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -61,13 +61,6 @@
   return true;
 }
 
-#if !BUILDFLAG(IS_IOS)
-webauthn::InternalAuthenticator*
-TestAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
-  return test_authenticator_.get();
-}
-#endif
-
 std::vector<FieldGlobalId> TestAutofillDriver::FillOrPreviewForm(
     int query_id,
     mojom::RendererFormDataAction action,
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index 91722c2..eeee313 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -51,10 +51,6 @@
   ui::AXTreeID GetAxTreeId() const override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
-#if !BUILDFLAG(IS_IOS)
-  webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator()
-      override;
-#endif
   // The return value contains the members (field, type) of `field_type_map` for
   // which `field_type_filter_.Run(triggered_origin, field, type)` is true.
   std::vector<FieldGlobalId> FillOrPreviewForm(
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 3f3843a..124179d 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -12,6 +12,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/check.h"
 #include "base/containers/flat_map.h"
 #include "base/i18n/case_conversion.h"
 #include "base/ranges/algorithm.h"
@@ -252,31 +253,6 @@
   }
 }
 
-bool RequiresPaymentMethod(
-    const CollectUserDataOptions& collect_user_data_options) {
-  return collect_user_data_options.request_payment_method;
-}
-
-bool RequiresContact(const CollectUserDataOptions& collect_user_data_options) {
-  return collect_user_data_options.request_payer_name ||
-         collect_user_data_options.request_payer_email ||
-         collect_user_data_options.request_payer_phone;
-}
-
-bool RequiresShipping(const CollectUserDataOptions& collect_user_data_options) {
-  return collect_user_data_options.request_shipping;
-}
-
-bool RequiresAddress(const CollectUserDataOptions& collect_user_data_options) {
-  return RequiresShipping(collect_user_data_options) ||
-         RequiresPaymentMethod(collect_user_data_options);
-}
-
-bool RequiresPhoneNumberSeparately(
-    const CollectUserDataOptions& collect_user_data_options) {
-  return collect_user_data_options.request_phone_number_separately;
-}
-
 void AddAutofillEntryToDataModel(autofill::ServerFieldType type,
                                  AutofillEntryProto entry,
                                  const std::string& locale,
@@ -299,6 +275,59 @@
   }
 }
 
+bool RequiresPaymentMethod(
+    const CollectUserDataOptions& collect_user_data_options) {
+  return collect_user_data_options.request_payment_method;
+}
+
+bool RequiresContact(const CollectUserDataOptions& collect_user_data_options) {
+  return collect_user_data_options.request_payer_name ||
+         collect_user_data_options.request_payer_email ||
+         collect_user_data_options.request_payer_phone;
+}
+
+bool HasValidContact(const GetUserDataResponseProto& response,
+                     const CollectUserDataOptions& collect_user_data_options) {
+  for (const auto& profile_data : response.available_contacts()) {
+    auto profile = std::make_unique<autofill::AutofillProfile>();
+    AddProtoDataToAutofillDataModel(profile_data.values(), response.locale(),
+                                    profile.get());
+    profile->FinalizeAfterImport();
+    if (user_data::GetContactValidationErrors(profile.get(),
+                                              collect_user_data_options)
+            .empty()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool RequiresShipping(const CollectUserDataOptions& collect_user_data_options) {
+  return collect_user_data_options.request_shipping;
+}
+
+bool RequiresAddress(const CollectUserDataOptions& collect_user_data_options) {
+  return RequiresShipping(collect_user_data_options) ||
+         RequiresPaymentMethod(collect_user_data_options);
+}
+
+bool RequiresPhoneNumberSeparately(
+    const CollectUserDataOptions& collect_user_data_options) {
+  return collect_user_data_options.request_phone_number_separately;
+}
+
+bool HasRequiredData(const GetUserDataResponseProto& response,
+                     const CollectUserDataOptions& collect_user_data_options) {
+  return (!RequiresContact(collect_user_data_options) ||
+          HasValidContact(response, collect_user_data_options)) &&
+         (!RequiresPhoneNumberSeparately(collect_user_data_options) ||
+          response.available_phone_numbers().size() > 0) &&
+         (!RequiresAddress(collect_user_data_options) ||
+          response.available_addresses().size() > 0) &&
+         (!RequiresPaymentMethod(collect_user_data_options) ||
+          response.available_payment_instruments().size() > 0);
+}
+
 void MergePhoneNumberIntoSelectedContact(UserData* user_data,
                                          UserModel* user_model,
                                          const CollectUserDataOptions& options,
@@ -614,15 +643,17 @@
     return;
   }
 
-  UseChromeData(user_data);
+  UseChromeData(user_data, Metrics::UserDataSource::CHROME_AUTOFILL);
 }
 
-void CollectUserDataAction::UseChromeData(UserData* user_data) {
+void CollectUserDataAction::UseChromeData(
+    UserData* user_data,
+    Metrics::UserDataSource user_data_source) {
   DCHECK(delegate_->GetPersonalDataManager());
   delegate_->GetPersonalDataManager()->AddObserver(this);
   UpdatePersonalDataManagerProfiles(user_data);
   UpdatePersonalDataManagerCards(user_data);
-  UpdateMetrics(user_data, Metrics::UserDataSource::CHROME_AUTOFILL);
+  UpdateMetrics(user_data, user_data_source);
   UpdateUi();
 
   action_stopwatch_.StartWaitTime();
@@ -635,8 +666,10 @@
     const GetUserDataResponseProto& response) {
   if (!success) {
     if (is_initial_request && !delegate_->MustUseBackendData() &&
-        proto_.collect_user_data().data_source().allow_fallback()) {
-      FallbackToChromeData(user_data);
+        proto_.collect_user_data().data_source().allow_fallback_on_failure()) {
+      FallbackToChromeData(
+          user_data,
+          Metrics::UserDataSource::FALLBACK_CHROME_AUTOFILL_ON_FAILED_REQUEST);
       return;
     }
 
@@ -644,14 +677,34 @@
               Metrics::CollectUserDataResult::FAILURE);
     return;
   }
+
+  const bool allow_fallback_on_missing_data =
+      proto_.collect_user_data().data_source().allow_fallback_on_missing_data();
+  if (allow_fallback_on_missing_data && !delegate_->MustUseBackendData()) {
+    bool has_required_data =
+        HasRequiredData(response, *collect_user_data_options_);
+    if (!has_required_data) {
+      VLOG(1) << "Falling back to Chrome Autofill data becuase backend "
+                 "has missing data";
+      FallbackToChromeData(
+          user_data,
+          Metrics::UserDataSource::FALLBACK_CHROME_AUTOFILL_ON_MISSING_DATA);
+      return;
+    }
+  }
+
   UpdateUserDataFromProto(response, user_data);
-  UpdateMetrics(user_data, Metrics::UserDataSource::BACKEND);
+  UpdateMetrics(user_data, allow_fallback_on_missing_data
+                               ? Metrics::UserDataSource::FALLBACK_BACKEND
+                               : Metrics::UserDataSource::BACKEND);
   UpdateUi();
 
   action_stopwatch_.StartWaitTime();
 }
 
-void CollectUserDataAction::FallbackToChromeData(UserData* user_data) {
+void CollectUserDataAction::FallbackToChromeData(
+    UserData* user_data,
+    Metrics::UserDataSource user_data_source) {
   if (collect_user_data_options_->request_phone_number_separately) {
     collect_user_data_options_->request_payer_phone = true;
     collect_user_data_options_->request_phone_number_separately = false;
@@ -679,7 +732,7 @@
       !delegate_->GetWebContents()->GetBrowserContext()->IsOffTheRecord();
   collect_user_data_options_->use_alternative_edit_dialogs = false;
 
-  UseChromeData(user_data);
+  UseChromeData(user_data, user_data_source);
 }
 
 void CollectUserDataAction::UpdateUi() {
@@ -1308,6 +1361,7 @@
       user_data.selected_address(options.billing_address_name);
   auto* shipping_address =
       user_data.selected_address(options.shipping_address_name);
+
   // TODO(b/204419253): check for phone number errors
   return user_data::GetContactValidationErrors(selected_profile, options)
              .empty() &&
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.h b/components/autofill_assistant/browser/actions/collect_user_data_action.h
index fde021c2..3b6d3fc9 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.h
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.h
@@ -88,12 +88,14 @@
   void ShowToUser();
   void OnShowToUser(UserData* user_data, UserDataFieldChange* field_change);
   void UpdateUserData(UserData* user_data);
-  void UseChromeData(UserData* user_data);
+  void UseChromeData(UserData* user_data,
+                     Metrics::UserDataSource user_data_source);
   void OnRequestUserData(bool is_initial_request,
                          UserData* user_data,
                          bool success,
                          const GetUserDataResponseProto& response);
-  void FallbackToChromeData(UserData* user_data);
+  void FallbackToChromeData(UserData* user_data,
+                            Metrics::UserDataSource user_data_source);
   void UpdateMetrics(UserData* user_data,
                      Metrics::UserDataSource user_data_source);
   void UpdateUi();
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 8bda727..25ce4fa 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -3827,6 +3827,136 @@
                   static_cast<int64_t>(Metrics::UserDataSource::BACKEND))}));
 }
 
+TEST_F(CollectUserDataActionTest, LogUkmDataFallbackBackendData) {
+  ActionProto action_proto;
+  auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+  collect_user_data_proto->set_privacy_notice_text("privacy");
+  collect_user_data_proto->set_request_terms_and_conditions(true);
+  collect_user_data_proto->set_accept_terms_and_conditions_text(
+      "terms and conditions");
+  collect_user_data_proto->set_show_terms_as_checkbox(false);
+  collect_user_data_proto->set_terms_require_review_text("terms review");
+  collect_user_data_proto->mutable_data_source()
+      ->set_allow_fallback_on_missing_data(true);
+
+  ON_CALL(mock_personal_data_manager_, IsAutofillProfileEnabled)
+      .WillByDefault(Return(true));
+  ON_CALL(mock_action_delegate_, MustUseBackendData)
+      .WillByDefault(Return(false));
+
+  GetUserDataResponseProto user_data_response;
+  user_data_response.set_locale("en-US");
+  auto* profile = user_data_response.add_available_contacts();
+  (*profile->mutable_values())[7] = MakeAutofillEntry("John Doe");
+  (*profile->mutable_values())[14] = MakeAutofillEntry("+1 123-456-7890");
+  *user_data_response.add_available_phone_numbers()->mutable_value() =
+      MakeAutofillEntry("+1 187-654-3210");
+  auto* address_1 = user_data_response.add_available_addresses();
+  AddCompleteAddressEntriesToMap("John Doe", address_1->mutable_values());
+  auto* payment_instrument_1 =
+      user_data_response.add_available_payment_instruments();
+  AddCompleteCardEntriesToMap("John Doe",
+                              payment_instrument_1->mutable_card_values());
+
+  EXPECT_CALL(mock_action_delegate_, RequestUserData)
+      .WillRepeatedly(RunOnceCallback<2>(true, user_data_response));
+
+  ON_CALL(mock_action_delegate_, CollectUserData(_))
+      .WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
+        user_data_.terms_and_conditions_ = ACCEPTED;
+
+        std::move(collect_user_data_options->confirm_callback)
+            .Run(&user_data_, &user_model_);
+      });
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  CollectUserDataAction fallback_action(&mock_action_delegate_, action_proto);
+  fallback_action.ProcessAction(callback_.Get());
+  EXPECT_THAT(
+      GetUkmCollectUserDataResult(ukm_recorder_),
+      ElementsAreArray({ToHumanReadableEntry(
+          source_id_, kResult,
+          static_cast<int64_t>(Metrics::CollectUserDataResult::SUCCESS))}));
+  EXPECT_THAT(
+      GetUkmUserDataSource(ukm_recorder_),
+      ElementsAreArray({ToHumanReadableEntry(
+          source_id_, kUserDataSource,
+          static_cast<int64_t>(Metrics::UserDataSource::FALLBACK_BACKEND))}));
+}
+
+TEST_F(CollectUserDataActionTest, LogUkmFallbackChromeAutofillDataFromBackend) {
+  GetUserDataResponseProto user_data_response;
+  user_data_response.set_locale("en-US");
+  auto* response_profile = user_data_response.add_available_contacts();
+  (*response_profile->mutable_values())[7] = MakeAutofillEntry("John Doe");
+  autofill::CountryNames::SetLocaleString("en-US");
+  EXPECT_CALL(mock_action_delegate_, RequestUserData)
+      .WillRepeatedly(RunOnceCallback<2>(true, user_data_response));
+
+  ON_CALL(mock_personal_data_manager_, IsAutofillProfileEnabled)
+      .WillByDefault(Return(true));
+
+  autofill::AutofillProfile profile;
+  autofill::test::SetProfileInfo(
+      &profile, "Adam", "", "West", "adam.west@gmail.com", "", "Main St. 18",
+      "", "abc", "New York", "NY", "10001", "us", "+1 123-456-7890");
+
+  ON_CALL(mock_personal_data_manager_, GetProfiles)
+      .WillByDefault(
+          Return(std::vector<autofill::AutofillProfile*>({&profile})));
+  ON_CALL(mock_action_delegate_, MustUseBackendData)
+      .WillByDefault(Return(false));
+
+  ActionProto action_proto;
+  auto* collect_user_data = action_proto.mutable_collect_user_data();
+  collect_user_data->set_request_terms_and_conditions(false);
+  collect_user_data->set_request_payment_method(false);
+  collect_user_data->set_billing_address_name("billing");
+  collect_user_data->mutable_data_origin_notice()->set_link_text("Link");
+  collect_user_data->mutable_data_origin_notice()->set_dialog_title("Title");
+  collect_user_data->mutable_data_origin_notice()->set_dialog_text("Text");
+  collect_user_data->mutable_data_origin_notice()->set_dialog_button_text(
+      "Button");
+  collect_user_data->mutable_data_source()->set_allow_fallback_on_missing_data(
+      true);
+
+  auto* contact_details = collect_user_data->mutable_contact_details();
+  contact_details->set_request_payer_name(true);
+  contact_details->set_request_payer_email(true);
+  contact_details->set_contact_details_name("contact");
+  collect_user_data->set_shipping_address_name("shipping-address");
+  collect_user_data->mutable_data_source();
+
+  ON_CALL(mock_action_delegate_, CollectUserData(_))
+      .WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
+        ExpectSelectedProfileMatches("contact", &profile);
+        ExpectSelectedProfileMatches("shipping-address", &profile);
+        user_data_.terms_and_conditions_ = ACCEPTED;
+        std::move(collect_user_data_options->confirm_callback)
+            .Run(&user_data_, &user_model_);
+      });
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  CollectUserDataAction action(&mock_action_delegate_, action_proto);
+  action.ProcessAction(callback_.Get());
+
+  EXPECT_THAT(
+      GetUkmCollectUserDataResult(ukm_recorder_),
+      ElementsAreArray({ToHumanReadableEntry(
+          source_id_, kResult,
+          static_cast<int64_t>(Metrics::CollectUserDataResult::SUCCESS))}));
+  EXPECT_THAT(GetUkmUserDataSource(ukm_recorder_),
+              ElementsAreArray({ToHumanReadableEntry(
+                  source_id_, kUserDataSource,
+                  static_cast<int64_t>(
+                      Metrics::UserDataSource::
+                          FALLBACK_CHROME_AUTOFILL_ON_MISSING_DATA))}));
+}
+
 TEST_F(CollectUserDataActionTest, LogsUkmInitialSelectionFieldBitArray) {
   ON_CALL(mock_personal_data_manager_, IsAutofillProfileEnabled)
       .WillByDefault(Return(true));
@@ -3835,8 +3965,9 @@
   ON_CALL(mock_personal_data_manager_, ShouldSuggestServerCards)
       .WillByDefault(Return(true));
 
-  // We add artificial constraints on the fields in the CUD proto below to make
-  // sure that we get a different profile as default for each kind of entry.
+  // We add artificial constraints on the fields in the CUD proto below to
+  // make sure that we get a different profile as default for each kind of
+  // entry.
   autofill::AutofillProfile default_contact;
   autofill::test::SetProfileInfo(&default_contact, "Adam", "", "",
                                  "adam.west@gmail.com", "", "Baker Street 221b",
@@ -4152,7 +4283,7 @@
   *collect_user_data->mutable_contact_details()
        ->add_phone_number_required_data_piece() =
       MakeRequiredDataPiece(autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER);
-  collect_user_data->mutable_data_source()->set_allow_fallback(true);
+  collect_user_data->mutable_data_source()->set_allow_fallback_on_failure(true);
   collect_user_data->mutable_data_origin_notice()->set_link_text("Link");
   collect_user_data->mutable_data_origin_notice()->set_dialog_title("Title");
   collect_user_data->mutable_data_origin_notice()->set_dialog_text("Text");
@@ -4165,11 +4296,12 @@
   CollectUserDataAction action(&mock_action_delegate_, action_proto);
   action.ProcessAction(callback_.Get());
 
-  EXPECT_THAT(
-      GetUkmUserDataSource(ukm_recorder_),
-      ElementsAreArray({ToHumanReadableEntry(
-          source_id_, kUserDataSource,
-          static_cast<int64_t>(Metrics::UserDataSource::CHROME_AUTOFILL))}));
+  EXPECT_THAT(GetUkmUserDataSource(ukm_recorder_),
+              ElementsAreArray({ToHumanReadableEntry(
+                  source_id_, kUserDataSource,
+                  static_cast<int64_t>(
+                      Metrics::UserDataSource::
+                          FALLBACK_CHROME_AUTOFILL_ON_FAILED_REQUEST))}));
 }
 
 TEST_F(CollectUserDataActionTest, FailActionIfFallbackIsNotPossible) {
@@ -4186,7 +4318,7 @@
   collect_user_data->mutable_contact_details()->set_request_payer_name(true);
   collect_user_data->mutable_contact_details()->set_contact_details_name(
       kMemoryLocation);
-  collect_user_data->mutable_data_source()->set_allow_fallback(true);
+  collect_user_data->mutable_data_source()->set_allow_fallback_on_failure(true);
 
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
                                               USER_DATA_REQUEST_FAILED))));
@@ -4214,7 +4346,7 @@
   collect_user_data->mutable_contact_details()->set_request_payer_name(true);
   collect_user_data->mutable_contact_details()->set_contact_details_name(
       kMemoryLocation);
-  collect_user_data->mutable_data_source()->set_allow_fallback(true);
+  collect_user_data->mutable_data_source()->set_allow_fallback_on_failure(true);
 
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
                                               USER_DATA_REQUEST_FAILED))));
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 77bcf5f..b75a750 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -2805,7 +2805,8 @@
     // ignored (Chrome Autofill data is not available). If this is false, the
     // action will fail if the user data request fails, even if Chrome Autofill
     // data would have been available.
-    optional bool allow_fallback = 1 [default = true];
+    optional bool allow_fallback_on_failure = 1 [default = true];
+    optional bool allow_fallback_on_missing_data = 2;
   }
 
   optional string prompt = 1;
diff --git a/components/desks_storage/core/desk_model.cc b/components/desks_storage/core/desk_model.cc
index fc48adab..cb21534 100644
--- a/components/desks_storage/core/desk_model.cc
+++ b/components/desks_storage/core/desk_model.cc
@@ -39,6 +39,12 @@
     observer.OnDeskModelDestroying();
 }
 
+DeskModel::GetAllEntriesResult::GetAllEntriesResult(
+    GetAllEntriesStatus status,
+    std::vector<const ash::DeskTemplate*> entries)
+    : status(status), entries(std::move(entries)) {}
+DeskModel::GetAllEntriesResult::~GetAllEntriesResult() = default;
+
 void DeskModel::AddObserver(DeskModelObserver* observer) {
   DCHECK(observer);
   observers_.AddObserver(observer);
diff --git a/components/desks_storage/core/desk_model.h b/components/desks_storage/core/desk_model.h
index fb6b190..e911ee7 100644
--- a/components/desks_storage/core/desk_model.h
+++ b/components/desks_storage/core/desk_model.h
@@ -80,13 +80,20 @@
   DeskModel& operator=(const DeskModel&) = delete;
   virtual ~DeskModel();
 
+  // Stores GetAllEntries result.
+  struct GetAllEntriesResult {
+    GetAllEntriesResult(GetAllEntriesStatus status,
+                        std::vector<const ash::DeskTemplate*> entries);
+    ~GetAllEntriesResult();
+
+    GetAllEntriesStatus status;
+    std::vector<const ash::DeskTemplate*> entries;
+  };
+
   // TODO(crbug.com/1320805): Once DeskSyncBridge is set to support saved desk,
   // add methods to support operations on both types of templates.
-  using GetAllEntriesCallback = base::OnceCallback<void(
-      GetAllEntriesStatus status,
-      const std::vector<const ash::DeskTemplate*>& entries)>;
-  // Returns a vector of entries in the model.
-  virtual void GetAllEntries(GetAllEntriesCallback callback) = 0;
+  // Returns all entries in the model.
+  virtual GetAllEntriesResult GetAllEntries() = 0;
 
   using GetEntryByUuidCallback =
       base::OnceCallback<void(GetEntryByUuidStatus status,
diff --git a/components/desks_storage/core/desk_model_wrapper.cc b/components/desks_storage/core/desk_model_wrapper.cc
index c4bf2b4..aa826d86 100644
--- a/components/desks_storage/core/desk_model_wrapper.cc
+++ b/components/desks_storage/core/desk_model_wrapper.cc
@@ -20,8 +20,7 @@
 
 DeskModelWrapper::~DeskModelWrapper() = default;
 
-void DeskModelWrapper::GetAllEntries(
-    DeskModel::GetAllEntriesCallback callback) {
+DeskModel::GetAllEntriesResult DeskModelWrapper::GetAllEntries() {
   auto template_entries = std::vector<const ash::DeskTemplate*>();
   auto desk_template_status =
       GetDeskTemplateModel()->GetAllEntries(template_entries);
@@ -29,14 +28,26 @@
     template_entries.push_back(it.get());
 
   if (desk_template_status != DeskModel::GetAllEntriesStatus::kOk) {
-    std::move(callback).Run(DeskModel::GetAllEntriesStatus::kFailure,
-                            template_entries);
-    return;
+    return DeskModel::GetAllEntriesResult(
+        DeskModel::GetAllEntriesStatus::kFailure, std::move(template_entries));
   }
 
-  save_and_recall_desks_model_->GetAllEntries(base::BindOnce(
-      &DeskModelWrapper::OnGetAllEntries, weak_ptr_factory_.GetWeakPtr(),
-      template_entries, std::move(callback)));
+  auto save_and_recall_entries_result =
+      save_and_recall_desks_model_->GetAllEntries();
+
+  if (save_and_recall_entries_result.status !=
+      DeskModel::GetAllEntriesStatus::kOk) {
+    return DeskModel::GetAllEntriesResult(save_and_recall_entries_result.status,
+                                          std::move(template_entries));
+  }
+
+  auto all_entries = template_entries;
+
+  for (auto* const entry : save_and_recall_entries_result.entries)
+    all_entries.push_back(entry);
+
+  return DeskModel::GetAllEntriesResult(save_and_recall_entries_result.status,
+                                        std::move(all_entries));
 }
 
 void DeskModelWrapper::GetEntryByUUID(
@@ -165,24 +176,6 @@
   return desk_template_model_;
 }
 
-void DeskModelWrapper::OnGetAllEntries(
-    const std::vector<const ash::DeskTemplate*>& template_entries,
-    DeskModel::GetAllEntriesCallback callback,
-    desks_storage::DeskModel::GetAllEntriesStatus status,
-    const std::vector<const ash::DeskTemplate*>& entries) {
-  if (status != DeskModel::GetAllEntriesStatus::kOk) {
-    std::move(callback).Run(status, template_entries);
-    return;
-  }
-
-  auto all_entries = template_entries;
-
-  for (auto* const entry : entries)
-    all_entries.push_back(entry);
-
-  std::move(callback).Run(status, all_entries);
-}
-
 void DeskModelWrapper::OnDeleteAllEntries(
     DeskModel::DeleteEntryCallback callback,
     desks_storage::DeskModel::DeleteEntryStatus status) {
diff --git a/components/desks_storage/core/desk_model_wrapper.h b/components/desks_storage/core/desk_model_wrapper.h
index abc6c26..0c969f5 100644
--- a/components/desks_storage/core/desk_model_wrapper.h
+++ b/components/desks_storage/core/desk_model_wrapper.h
@@ -34,7 +34,7 @@
   ~DeskModelWrapper() override;
 
   // DeskModel:
-  void GetAllEntries(GetAllEntriesCallback callback) override;
+  DeskModel::GetAllEntriesResult GetAllEntries() override;
   void GetEntryByUUID(const std::string& uuid,
                       GetEntryByUuidCallback callback) override;
   void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
@@ -64,14 +64,6 @@
  private:
   desks_storage::DeskSyncBridge* GetDeskTemplateModel() const;
 
-  // Wrapper for GetAllEntriesCallback to consolidate entries from both storage
-  // models into one.
-  void OnGetAllEntries(
-      const std::vector<const ash::DeskTemplate*>& template_entries,
-      DeskModel::GetAllEntriesCallback callback,
-      desks_storage::DeskModel::GetAllEntriesStatus status,
-      const std::vector<const ash::DeskTemplate*>& entries);
-
   // Wrapper for DeleteEntryCallback to consolidate deleting all entries from
   // both storage models.
   void OnDeleteAllEntries(DeskModel::DeleteEntryCallback callback,
diff --git a/components/desks_storage/core/desk_model_wrapper_unittests.cc b/components/desks_storage/core/desk_model_wrapper_unittests.cc
index ee54cc54..ca86bb0aa 100644
--- a/components/desks_storage/core/desk_model_wrapper_unittests.cc
+++ b/components/desks_storage/core/desk_model_wrapper_unittests.cc
@@ -285,17 +285,12 @@
 
   void VerifyAllEntries(size_t expected_size, const std::string& trace_string) {
     SCOPED_TRACE(trace_string);
-    base::RunLoop loop;
 
-    model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-        [&](DeskModel::GetAllEntriesStatus status,
-            const std::vector<const ash::DeskTemplate*>& entries) {
-          EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-          EXPECT_EQ(entries.size(), expected_size);
-          loop.Quit();
-        }));
+    task_environment_.RunUntilIdle();
 
-    loop.Run();
+    auto result = model_wrapper_->GetAllEntries();
+    EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+    EXPECT_EQ(result.entries.size(), expected_size);
   }
 
   base::ScopedTempDir temp_dir_;
@@ -321,20 +316,16 @@
                                    base::BindOnce(&VerifyEntryAddedCorrectly));
 
   VerifyAllEntries(1ul, "Added one desk template");
-  base::RunLoop loop;
+
   // Verify that it's not desk template entry in the save and recall desk
   // storage.
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 1ul);
-        EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kTemplate);
-        loop.Quit();
-      }));
+  auto result = model_wrapper_->GetAllEntries();
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 1ul);
+  EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kTemplate);
+
   EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 1ul);
   EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 0ul);
-  loop.Run();
 }
 
 TEST_F(DeskModelWrapperTest, CanAddSaveAndRecallDeskEntry) {
@@ -345,19 +336,15 @@
       base::BindOnce(&VerifyEntryAddedCorrectly));
 
   VerifyAllEntries(1ul, "Added one save and recall desk");
-  base::RunLoop loop;
   // Verify that it's not SaveAndRecall entry in the desk template storage.
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 1ul);
-        EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
-        loop.Quit();
-      }));
+  auto result = model_wrapper_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 1ul);
+  EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+
   EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
   EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
-  loop.Run();
 }
 
 TEST_F(DeskModelWrapperTest,
@@ -381,20 +368,16 @@
   InitializeBridge();
 
   AddTwoTemplates();
-  base::RunLoop loop;
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 2ul);
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
 
-        // Sanity check for the search function.
-        EXPECT_FALSE(FindUuidInUuidList(kTestUuid3, entries));
-        loop.Quit();
-      }));
-  loop.Run();
+  auto result = model_wrapper_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 2ul);
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, result.entries));
+
+  // Sanity check for the search function.
+  EXPECT_FALSE(FindUuidInUuidList(kTestUuid3, result.entries));
 }
 
 TEST_F(DeskModelWrapperTest, GetAllEntriesIncludesPolicyValues) {
@@ -404,30 +387,23 @@
   AddTwoSaveAndRecallDeskTemplates();
   model_wrapper_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
 
-  base::RunLoop loop;
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 5ul);
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid4, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid5, entries));
-        // One of these templates should be from policy.
-        EXPECT_EQ(
-            base::ranges::count_if(entries,
+  auto result = model_wrapper_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 5ul);
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid4, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid5, result.entries));
+  // One of these templates should be from policy.
+  EXPECT_EQ(base::ranges::count_if(result.entries,
                                    [](const ash::DeskTemplate* entry) {
                                      return entry->source() ==
                                             ash::DeskTemplateSource::kPolicy;
                                    }),
             1l);
 
-        loop.Quit();
-      }));
-  loop.Run();
-
   model_wrapper_->SetPolicyDeskTemplates("");
 }
 
@@ -457,36 +433,29 @@
   auto dupe_second_save_and_recall_desk = MakeTestSaveAndRecallDesk(
       dupe_second_save_and_recall_uuid, "desk_01", base::Time::Now());
   AddSavedDeskToDeskModel(std::move(dupe_second_save_and_recall_desk));
-  base::RunLoop loop;
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 5ul);
 
-        const ash::DeskTemplate* duplicate_desk_template =
-            model_wrapper_->FindOtherEntryWithName(
-                u"desk_01", ash::DeskTemplateType::kTemplate,
-                base::GUID::ParseCaseInsensitive(dupe_template_uuid));
-        EXPECT_TRUE(duplicate_desk_template);
+  auto result = model_wrapper_->GetAllEntries();
 
-        const ash::DeskTemplate* duplicate_save_and_recall =
-            model_wrapper_->FindOtherEntryWithName(
-                u"save_and_recall_desk_01",
-                ash::DeskTemplateType::kSaveAndRecall,
-                base::GUID::ParseCaseInsensitive(dupe_template_uuid));
-        EXPECT_TRUE(duplicate_save_and_recall);
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 5ul);
 
-        const ash::DeskTemplate* duplicate_save_and_recall_not_found =
-            model_wrapper_->FindOtherEntryWithName(
-                u"desk_01", ash::DeskTemplateType::kSaveAndRecall,
-                base::GUID::ParseCaseInsensitive(
-                    dupe_second_save_and_recall_uuid));
-        EXPECT_FALSE(duplicate_save_and_recall_not_found);
+  const ash::DeskTemplate* duplicate_desk_template =
+      model_wrapper_->FindOtherEntryWithName(
+          u"desk_01", ash::DeskTemplateType::kTemplate,
+          base::GUID::ParseCaseInsensitive(dupe_template_uuid));
+  EXPECT_TRUE(duplicate_desk_template);
 
-        loop.Quit();
-      }));
-  loop.Run();
+  const ash::DeskTemplate* duplicate_save_and_recall =
+      model_wrapper_->FindOtherEntryWithName(
+          u"save_and_recall_desk_01", ash::DeskTemplateType::kSaveAndRecall,
+          base::GUID::ParseCaseInsensitive(dupe_template_uuid));
+  EXPECT_TRUE(duplicate_save_and_recall);
+
+  const ash::DeskTemplate* duplicate_save_and_recall_not_found =
+      model_wrapper_->FindOtherEntryWithName(
+          u"desk_01", ash::DeskTemplateType::kSaveAndRecall,
+          base::GUID::ParseCaseInsensitive(dupe_second_save_and_recall_uuid));
+  EXPECT_FALSE(duplicate_save_and_recall_not_found);
 }
 
 TEST_F(DeskModelWrapperTest, CanDetectNoDuplicateEntryNames) {
@@ -510,28 +479,23 @@
       MakeTestSaveAndRecallDesk(second_save_and_recall_uuid,
                                 "save_and_recall_desk_02", base::Time::Now());
   AddSavedDeskToDeskModel(std::move(second_save_and_recall_desk));
-  base::RunLoop loop;
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 4ul);
 
-        const ash::DeskTemplate* duplicate_desk_template =
-            model_wrapper_->FindOtherEntryWithName(
-                u"desk_02", ash::DeskTemplateType::kTemplate,
-                base::GUID::ParseCaseInsensitive(second_template_uuid));
-        EXPECT_FALSE(duplicate_desk_template);
+  auto result = model_wrapper_->GetAllEntries();
 
-        const ash::DeskTemplate* duplicate_save_and_recall =
-            model_wrapper_->FindOtherEntryWithName(
-                u"save_and_recall_desk_02",
-                ash::DeskTemplateType::kSaveAndRecall,
-                base::GUID::ParseCaseInsensitive(second_save_and_recall_uuid));
-        EXPECT_FALSE(duplicate_save_and_recall);
-        loop.Quit();
-      }));
-  loop.Run();
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 4ul);
+
+  const ash::DeskTemplate* duplicate_desk_template =
+      model_wrapper_->FindOtherEntryWithName(
+          u"desk_02", ash::DeskTemplateType::kTemplate,
+          base::GUID::ParseCaseInsensitive(second_template_uuid));
+  EXPECT_FALSE(duplicate_desk_template);
+
+  const ash::DeskTemplate* duplicate_save_and_recall =
+      model_wrapper_->FindOtherEntryWithName(
+          u"save_and_recall_desk_02", ash::DeskTemplateType::kSaveAndRecall,
+          base::GUID::ParseCaseInsensitive(second_save_and_recall_uuid));
+  EXPECT_FALSE(duplicate_save_and_recall);
 }
 
 TEST_F(DeskModelWrapperTest, CanGetEntryByUuid) {
@@ -750,15 +714,9 @@
   EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 2ul);
   EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 2ul);
 
-  base::RunLoop loop;
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        loop.Quit();
-      }));
+  auto result = model_wrapper_->GetAllEntries();
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
 
-  loop.Run();
   VerifyAllEntries(4ul,
                    "Add two desks templates and two saved and recall desks");
 }
@@ -771,19 +729,16 @@
       base::BindOnce(&VerifyEntryAddedCorrectly));
 
   VerifyAllEntries(1ul, "Added one save and recall desk");
-  base::RunLoop loop;
+
   // Verify that it's not SaveAndRecall entry in the desk template cache.
-  model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 1ul);
-        EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
-        loop.Quit();
-      }));
+  auto result = model_wrapper_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 1ul);
+  EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+
   EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
   EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
-  loop.Run();
 }
 
 TEST_F(DeskModelWrapperTest, CanAddMaxEntriesForBothTypes) {
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index c5f7697..f282150 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -369,7 +369,6 @@
     case chromeos::WindowStateType::kDefault:
     case chromeos::WindowStateType::kNormal:
     case chromeos::WindowStateType::kInactive:
-    case chromeos::WindowStateType::kAutoPositioned:
     case chromeos::WindowStateType::kPinned:
     case chromeos::WindowStateType::kTrustedPinned:
     case chromeos::WindowStateType::kPip:
@@ -1053,12 +1052,10 @@
   return entity_data.specifics.workspace_desk().uuid();
 }
 
-void DeskSyncBridge::GetAllEntries(GetAllEntriesCallback callback) {
+DeskModel::GetAllEntriesResult DeskSyncBridge::GetAllEntries() {
   std::vector<const DeskTemplate*> entries;
-
   GetAllEntriesStatus status = GetAllEntries(entries);
-
-  std::move(callback).Run(status, std::move(entries));
+  return GetAllEntriesResult(status, std::move(entries));
 }
 
 DeskModel::GetAllEntriesStatus DeskSyncBridge::GetAllEntries(
diff --git a/components/desks_storage/core/desk_sync_bridge.h b/components/desks_storage/core/desk_sync_bridge.h
index fba8b306..7efcbdd 100644
--- a/components/desks_storage/core/desk_sync_bridge.h
+++ b/components/desks_storage/core/desk_sync_bridge.h
@@ -64,7 +64,7 @@
   std::string GetStorageKey(const syncer::EntityData& entity_data) override;
 
   // DeskModel overrides.
-  void GetAllEntries(GetAllEntriesCallback callback) override;
+  DeskModel::GetAllEntriesResult GetAllEntries() override;
   void GetEntryByUUID(const std::string& uuid,
                       GetEntryByUuidCallback callback) override;
   void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
diff --git a/components/desks_storage/core/desk_sync_bridge_unittest.cc b/components/desks_storage/core/desk_sync_bridge_unittest.cc
index 036770b..055ae44f 100644
--- a/components/desks_storage/core/desk_sync_bridge_unittest.cc
+++ b/components/desks_storage/core/desk_sync_bridge_unittest.cc
@@ -1137,26 +1137,18 @@
 
   EXPECT_EQ(4ul, bridge()->GetAllEntryUuids().size());
 
-  base::RunLoop loop;
-  bridge()->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 4ul);
+  auto result = bridge()->GetAllEntries();
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 4ul);
 
-        // Two of these templates should be from policy.
-        EXPECT_EQ(
-            base::ranges::count_if(entries,
+  // Two of these templates should be from policy.
+  EXPECT_EQ(base::ranges::count_if(result.entries,
                                    [](const ash::DeskTemplate* entry) {
                                      return entry->source() ==
                                             ash::DeskTemplateSource::kPolicy;
                                    }),
             2l);
 
-        loop.Quit();
-      }));
-  loop.Run();
-
   bridge()->SetPolicyDeskTemplates("");
 }
 
diff --git a/components/desks_storage/core/local_desk_data_manager.cc b/components/desks_storage/core/local_desk_data_manager.cc
index b1d372f..fe45d60f 100644
--- a/components/desks_storage/core/local_desk_data_manager.cc
+++ b/components/desks_storage/core/local_desk_data_manager.cc
@@ -162,33 +162,26 @@
 
 LocalDeskDataManager::~LocalDeskDataManager() = default;
 
-void LocalDeskDataManager::GetAllEntries(
-    DeskModel::GetAllEntriesCallback callback) {
-  auto status = std::make_unique<DeskModel::GetAllEntriesStatus>();
-  auto entries = std::make_unique<std::vector<const ash::DeskTemplate*>>();
+DeskModel::GetAllEntriesResult LocalDeskDataManager::GetAllEntries() {
+  DeskModel::GetAllEntriesStatus status = DeskModel::GetAllEntriesStatus::kOk;
+  auto entries = std::vector<const ash::DeskTemplate*>();
+
   if (cache_status_ != CacheStatus::kOk) {
-    *status = DeskModel::GetAllEntriesStatus::kFailure;
-    std::move(callback).Run(*status, *entries);
-    return;
+    status = DeskModel::GetAllEntriesStatus::kFailure;
+    return DeskModel::GetAllEntriesResult(status, std::move(entries));
   }
+
   for (const auto& it : policy_entries_)
-    entries->push_back(it.get());
+    entries.push_back(it.get());
 
   for (auto& saved_desk : saved_desks_list_) {
     for (auto& [uuid, template_entry] : saved_desk.second) {
       DCHECK_EQ(uuid, template_entry->uuid());
-      entries->push_back(template_entry.get());
+      entries.push_back(template_entry.get());
     }
   }
-  // It's safe to pass base::Unretained(this) since the LocalDeskDataManager is
-  // a long-lived object that should persist during user session.
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(&LocalDeskDataManager::GetAllEntriesTask,
-                     base::Unretained(this), status.get()),
-      base::BindOnce(&LocalDeskDataManager::OnGetAllEntries,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(status),
-                     std::move(entries), std::move(callback)));
+
+  return DeskModel::GetAllEntriesResult(status, std::move(entries));
 }
 
 void LocalDeskDataManager::GetEntryByUUID(
@@ -476,25 +469,6 @@
   return;
 }
 
-void LocalDeskDataManager::GetAllEntriesTask(
-    DeskModel::GetAllEntriesStatus* out_status_ptr) {
-  if (cache_status_ == CacheStatus::kInvalidPath) {
-    *out_status_ptr = DeskModel::GetAllEntriesStatus::kFailure;
-  }
-  if (cache_status_ == CacheStatus::kOk) {
-    *out_status_ptr = DeskModel::GetAllEntriesStatus::kOk;
-  } else {
-    *out_status_ptr = DeskModel::GetAllEntriesStatus::kPartialFailure;
-  }
-}
-
-void LocalDeskDataManager::OnGetAllEntries(
-    std::unique_ptr<DeskModel::GetAllEntriesStatus> status_ptr,
-    std::unique_ptr<std::vector<const ash::DeskTemplate*>> entries_ptr,
-    DeskModel::GetAllEntriesCallback callback) {
-  std::move(callback).Run(*status_ptr, *entries_ptr);
-}
-
 void LocalDeskDataManager::GetEntryByUuidTask(
     const std::string& uuid_str,
     DeskModel::GetEntryByUuidStatus* out_status_ptr) {
diff --git a/components/desks_storage/core/local_desk_data_manager.h b/components/desks_storage/core/local_desk_data_manager.h
index 07c5d31f..9230aeb 100644
--- a/components/desks_storage/core/local_desk_data_manager.h
+++ b/components/desks_storage/core/local_desk_data_manager.h
@@ -55,7 +55,7 @@
   ~LocalDeskDataManager() override;
 
   // DeskModel:
-  void GetAllEntries(GetAllEntriesCallback callback) override;
+  DeskModel::GetAllEntriesResult GetAllEntries() override;
   void GetEntryByUUID(const std::string& uuid_str,
                       GetEntryByUuidCallback callback) override;
   void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
@@ -92,9 +92,6 @@
       CacheStatus* cache_status_ptr,
       std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>* entries_ptr);
 
-  // Gets all entries from user's `local_saved_desk_path_`.
-  void GetAllEntriesTask(DeskModel::GetAllEntriesStatus* status_ptr);
-
   // Get a specific entry by `uuid_str`.
   void GetEntryByUuidTask(const std::string& uuid_str,
                           DeskModel::GetEntryByUuidStatus* status_ptr);
@@ -106,12 +103,6 @@
       std::unique_ptr<ash::DeskTemplate*> entry_ptr_ptr,
       DeskModel::GetEntryByUuidCallback callback);
 
-  // Wrapper method to call GetAllEntriesCallback.
-  void OnGetAllEntries(
-      std::unique_ptr<DeskModel::GetAllEntriesStatus> status_ptr,
-      std::unique_ptr<std::vector<const ash::DeskTemplate*>> entries_ptr,
-      DeskModel::GetAllEntriesCallback callback);
-
   // Add or update an entry by `new_entry`'s UUID.
   void AddOrUpdateEntryTask(const base::GUID uuid,
                             DeskModel::AddOrUpdateEntryStatus* status_ptr,
diff --git a/components/desks_storage/core/local_desk_data_manager_unittests.cc b/components/desks_storage/core/local_desk_data_manager_unittests.cc
index 03afc873..7fe09f4 100644
--- a/components/desks_storage/core/local_desk_data_manager_unittests.cc
+++ b/components/desks_storage/core/local_desk_data_manager_unittests.cc
@@ -288,17 +288,13 @@
 
   void VerifyAllEntries(size_t expected_size, const std::string& trace_string) {
     SCOPED_TRACE(trace_string);
-    base::RunLoop loop;
 
-    data_manager_->GetAllEntries(base::BindLambdaForTesting(
-        [&](DeskModel::GetAllEntriesStatus status,
-            const std::vector<const ash::DeskTemplate*>& entries) {
-          EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-          EXPECT_EQ(entries.size(), expected_size);
-          loop.Quit();
-        }));
+    task_environment_.RunUntilIdle();
 
-    loop.Run();
+    auto result = data_manager_->GetAllEntries();
+
+    EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+    EXPECT_EQ(result.entries.size(), expected_size);
   }
 
   base::ScopedTempDir temp_dir_;
@@ -373,21 +369,18 @@
   data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_three_),
                                   base::BindOnce(&VerifyEntryAddedCorrectly));
 
-  base::RunLoop loop;
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 3ul);
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, entries));
+  task_environment_.RunUntilIdle();
 
-        // Sanity check for the search function.
-        EXPECT_FALSE(FindUuidInUuidList(kTestUuid4, entries));
-        loop.Quit();
-      }));
-  loop.Run();
+  auto result = data_manager_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 3ul);
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, result.entries));
+
+  // Sanity check for the search function.
+  EXPECT_FALSE(FindUuidInUuidList(kTestUuid4, result.entries));
 }
 
 TEST_F(LocalDeskDataManagerTest, GetAllEntriesIncludesPolicyValues) {
@@ -402,31 +395,27 @@
 
   data_manager_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
 
-  base::RunLoop loop;
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 4ul);
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, entries));
-        EXPECT_TRUE(FindUuidInUuidList(kTestUuid9, entries));
+  task_environment_.RunUntilIdle();
 
-        // One of these templates should be from policy.
-        EXPECT_EQ(
-            base::ranges::count_if(entries,
+  auto result = data_manager_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 4ul);
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, result.entries));
+  EXPECT_TRUE(FindUuidInUuidList(kTestUuid9, result.entries));
+
+  // One of these templates should be from policy.
+  EXPECT_EQ(base::ranges::count_if(result.entries,
                                    [](const ash::DeskTemplate* entry) {
                                      return entry->source() ==
                                             ash::DeskTemplateSource::kPolicy;
                                    }),
             1l);
 
-        // Sanity check for the search function.
-        EXPECT_FALSE(FindUuidInUuidList(kTestUuid4, entries));
-        loop.Quit();
-      }));
-  loop.Run();
+  // Sanity check for the search function.
+  EXPECT_FALSE(FindUuidInUuidList(kTestUuid4, result.entries));
 
   data_manager_->SetPolicyDeskTemplates("");
 }
@@ -580,15 +569,8 @@
       base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
         EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
       }));
-  base::RunLoop loop;
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 0ul);
-        loop.Quit();
-      }));
-  loop.Run();
+
+  VerifyAllEntries(0ul, "Delete one entry");
 }
 
 TEST_F(LocalDeskDataManagerTest, CanDeleteAllEntries) {
@@ -600,23 +582,12 @@
 
   data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_three_),
                                   base::BindOnce(&VerifyEntryAddedCorrectly));
-
-  base::RunLoop loop;
-
   data_manager_->DeleteAllEntries(
       base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
         EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
       }));
-  task_environment_.RunUntilIdle();
 
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 0ul);
-        loop.Quit();
-      }));
-  loop.Run();
+  VerifyAllEntries(0ul, "Delete all entries");
 }
 
 TEST_F(LocalDeskDataManagerTest,
@@ -657,15 +628,10 @@
   EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 2ul);
   EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 2ul);
 
-  base::RunLoop loop;
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        loop.Quit();
-      }));
+  auto result = data_manager_->GetAllEntries();
 
-  loop.Run();
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+
   VerifyAllEntries(4ul,
                    "Add two desks templates and two saved and recall desks");
 }
@@ -676,19 +642,16 @@
       base::BindOnce(&VerifyEntryAddedCorrectly));
 
   VerifyAllEntries(1ul, "Added one save and recall desk");
-  base::RunLoop loop;
+
   // Verify that it's not SaveAndRecall entry in the desk template cache.
-  data_manager_->GetAllEntries(base::BindLambdaForTesting(
-      [&](DeskModel::GetAllEntriesStatus status,
-          const std::vector<const ash::DeskTemplate*>& entries) {
-        EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
-        EXPECT_EQ(entries.size(), 1ul);
-        EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
-        loop.Quit();
-      }));
+  auto result = data_manager_->GetAllEntries();
+
+  EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
+  EXPECT_EQ(result.entries.size(), 1ul);
+  EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+
   EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 0ul);
   EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 1ul);
-  loop.Run();
 }
 
 TEST_F(LocalDeskDataManagerTest, CanGetSaveAndRecallDeskEntryByUuid) {
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index 3906137a..69632b1 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -185,9 +185,7 @@
 }
 
 bool TopSitesImpl::HasBlockedUrls() const {
-  const base::Value* blocked_urls =
-      pref_service_->GetDictionary(kBlockedUrlsPrefsKey);
-  return blocked_urls && !blocked_urls->DictEmpty();
+  return !pref_service_->GetValueDict(kBlockedUrlsPrefsKey).empty();
 }
 
 void TopSitesImpl::AddBlockedUrl(const GURL& url) {
@@ -216,9 +214,8 @@
 
 bool TopSitesImpl::IsBlocked(const GURL& url) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  const base::Value* blocked_urls =
-      pref_service_->GetDictionary(kBlockedUrlsPrefsKey);
-  return blocked_urls && blocked_urls->FindKey(GetURLHash(url));
+  return pref_service_->GetValueDict(kBlockedUrlsPrefsKey)
+      .contains(GetURLHash(url));
 }
 
 void TopSitesImpl::ClearBlockedUrls() {
@@ -517,9 +514,8 @@
 int TopSitesImpl::num_results_to_request_from_history() const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  const base::Value* blocked_urls =
-      pref_service_->GetDictionary(kBlockedUrlsPrefsKey);
-  return kTopSitesNumber + (blocked_urls ? blocked_urls->DictSize() : 0);
+  return kTopSitesNumber +
+         pref_service_->GetValueDict(kBlockedUrlsPrefsKey).size();
 }
 
 void TopSitesImpl::MoveStateToLoaded() {
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index a8ba5ca4..c88e620 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -169,7 +169,7 @@
     SYNC_ERROR_INFOBAR_DELEGATE_ANDROID = 97,
     MIXED_CONTENT_DOWNLOAD_INFOBAR_DELEGATE_ANDROID = 98,
     // Removed: CONDITIONAL_TAB_STRIP_INFOBAR_ANDROID = 99,
-    LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR_ANDROID = 100,
+    // Removed: LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR_ANDROID = 100,
     // Removed: SYSTEM_INFOBAR_DELEGATE_MAC = 101,
     EXPERIMENTAL_INFOBAR_DELEGATE_LACROS = 102,
     // Removed: ROSETTA_REQUIRED_INFOBAR_DELEGATE = 103,
diff --git a/components/metrics/structured/BUILD.gn b/components/metrics/structured/BUILD.gn
index 08ab7c73..e76475c 100644
--- a/components/metrics/structured/BUILD.gn
+++ b/components/metrics/structured/BUILD.gn
@@ -39,9 +39,12 @@
 
 static_library("events") {
   sources = [
+    "delegating_events_processor.cc",
+    "delegating_events_processor.h",
     "enums.h",
     "event.cc",
     "event.h",
+    "events_processor_interface.h",
     "structured_metrics_client.cc",
     "structured_metrics_client.h",
   ]
diff --git a/components/metrics/structured/delegating_events_processor.cc b/components/metrics/structured/delegating_events_processor.cc
new file mode 100644
index 0000000..513f112
--- /dev/null
+++ b/components/metrics/structured/delegating_events_processor.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/structured/delegating_events_processor.h"
+
+namespace metrics::structured {
+
+DelegatingEventsProcessor::DelegatingEventsProcessor() = default;
+DelegatingEventsProcessor::~DelegatingEventsProcessor() = default;
+
+// Each individual events_processor could be checked, but this will need to be
+// checked during OnEventsRecord().
+bool DelegatingEventsProcessor::ShouldProcessOnEventRecord(const Event& event) {
+  return true;
+}
+
+void DelegatingEventsProcessor::OnEventsRecord(Event* event) {
+  DCHECK(event);
+
+  for (auto& events_processor : events_processors_) {
+    if (events_processor->ShouldProcessOnEventRecord(*event)) {
+      // Note that every |events_processor| is operating on the same |event|.
+      // Race conditions should be mangaged by the client.
+      events_processor->OnEventsRecord(event);
+    }
+  }
+}
+
+void DelegatingEventsProcessor::AddEventsProcessor(
+    std::unique_ptr<EventsProcessorInterface> events_processor) {
+  DCHECK(events_processor);
+
+  events_processors_.push_back(std::move(events_processor));
+}
+
+}  // namespace metrics::structured
diff --git a/components/metrics/structured/delegating_events_processor.h b/components/metrics/structured/delegating_events_processor.h
new file mode 100644
index 0000000..9a674a34
--- /dev/null
+++ b/components/metrics/structured/delegating_events_processor.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_STRUCTURED_DELEGATING_EVENTS_PROCESSOR_H_
+#define COMPONENTS_METRICS_STRUCTURED_DELEGATING_EVENTS_PROCESSOR_H_
+
+#include <memory>
+
+#include "components/metrics/structured/event.h"
+#include "components/metrics/structured/events_processor_interface.h"
+
+namespace metrics::structured {
+
+// DelegatingEventsProcessor manages a set of other EventsProcessorInterfaces.
+// Calls to this events processor are forwarded to all of the registered events
+// processors.
+class DelegatingEventsProcessor final : public EventsProcessorInterface {
+ public:
+  DelegatingEventsProcessor();
+  ~DelegatingEventsProcessor() override;
+
+  // Adds a |events_processor| to forward calls to.
+  void AddEventsProcessor(
+      std::unique_ptr<EventsProcessorInterface> events_processor);
+
+  // EventsProcessor:
+  bool ShouldProcessOnEventRecord(const Event& event) override;
+  void OnEventsRecord(Event* event) override;
+
+ private:
+  std::vector<std::unique_ptr<EventsProcessorInterface>> events_processors_;
+};
+
+}  // namespace metrics::structured
+
+#endif  // COMPONENTS_METRICS_STRUCTURED_DELEGATING_EVENTS_PROCESSOR_H_
diff --git a/components/metrics/structured/events_processor_interface.h b/components/metrics/structured/events_processor_interface.h
new file mode 100644
index 0000000..d68e234
--- /dev/null
+++ b/components/metrics/structured/events_processor_interface.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_STRUCTURED_EVENTS_PROCESSOR_INTERFACE_H_
+#define COMPONENTS_METRICS_STRUCTURED_EVENTS_PROCESSOR_INTERFACE_H_
+
+#include "components/metrics/structured/event.h"
+
+namespace metrics::structured {
+
+// An interface allowing different classes to add fields to events after the
+// events are recorded by a client.
+class EventsProcessorInterface {
+ public:
+  EventsProcessorInterface() = default;
+
+  EventsProcessorInterface(const EventsProcessorInterface& events_processor) =
+      delete;
+  EventsProcessorInterface& operator=(
+      const EventsProcessorInterface& events_processor) = delete;
+
+  virtual ~EventsProcessorInterface() = default;
+
+  // Returns true if |event| should be processed by |this|.
+  virtual bool ShouldProcessOnEventRecord(const Event& event) = 0;
+
+  // Processes |event|. Note that this function may mutate |event|.
+  virtual void OnEventsRecord(Event* event) = 0;
+};
+
+}  // namespace metrics::structured
+
+#endif  // COMPONENTS_METRICS_STRUCTURED_EVENTS_PROCESSOR_INTERFACE_H_
diff --git a/components/metrics/structured/structured_metrics_client.cc b/components/metrics/structured/structured_metrics_client.cc
index cb7cb37e..331093442 100644
--- a/components/metrics/structured/structured_metrics_client.cc
+++ b/components/metrics/structured/structured_metrics_client.cc
@@ -22,8 +22,15 @@
 }
 
 void StructuredMetricsClient::Record(Event&& event) {
-  if (delegate_ && delegate_->IsReadyToRecord())
+  if (delegate_ && delegate_->IsReadyToRecord()) {
+    delegating_events_processor_.OnEventsRecord(&event);
     delegate_->RecordEvent(std::move(event));
+  }
+}
+
+void StructuredMetricsClient::AddEventsProcessor(
+    std::unique_ptr<EventsProcessorInterface> events_processor) {
+  delegating_events_processor_.AddEventsProcessor(std::move(events_processor));
 }
 
 void StructuredMetricsClient::SetDelegate(RecordingDelegate* delegate) {
diff --git a/components/metrics/structured/structured_metrics_client.h b/components/metrics/structured/structured_metrics_client.h
index 11ec898..551d71b 100644
--- a/components/metrics/structured/structured_metrics_client.h
+++ b/components/metrics/structured/structured_metrics_client.h
@@ -8,7 +8,9 @@
 #include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
 
+#include "components/metrics/structured/delegating_events_processor.h"
 #include "components/metrics/structured/event.h"
+#include "components/metrics/structured/events_processor_interface.h"
 
 namespace metrics {
 namespace structured {
@@ -41,6 +43,11 @@
   // Forwards to |delegate_|. If no delegate has been set, then no-op.
   void Record(Event&& event);
 
+  // Adds |events_processor| to further add metadata to recorded events or
+  // listen to recorded events.
+  void AddEventsProcessor(
+      std::unique_ptr<EventsProcessorInterface> events_processor);
+
   // Sets the delegate for the client's recording logic. Should be called before
   // anything else. |this| does not take ownership of |delegate| and assumes
   // that the caller will properly manage the lifetime of delegate and call
@@ -54,6 +61,8 @@
   StructuredMetricsClient();
   ~StructuredMetricsClient();
 
+  DelegatingEventsProcessor delegating_events_processor_;
+
   // Not owned. Assumes that the delegate's lifetime will exceed |this|.
   raw_ptr<RecordingDelegate> delegate_ = nullptr;
 };
diff --git a/components/optimization_guide/core/hints_manager.cc b/components/optimization_guide/core/hints_manager.cc
index aad1241..9b1baf86 100644
--- a/components/optimization_guide/core/hints_manager.cc
+++ b/components/optimization_guide/core/hints_manager.cc
@@ -1753,10 +1753,4 @@
   hint_cache_->RemoveHintsForHosts(std::move(on_success), hint_keys);
 }
 
-void HintsManager::PurgeFetchedEntries(base::OnceClosure on_success) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  ClearFetchedHints();
-  std::move(on_success).Run();
-}
-
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/hints_manager.h b/components/optimization_guide/core/hints_manager.h
index b68eca3..acd6705 100644
--- a/components/optimization_guide/core/hints_manager.h
+++ b/components/optimization_guide/core/hints_manager.h
@@ -175,7 +175,6 @@
       base::OnceClosure on_success,
       proto::KeyRepresentation key_representation,
       const base::flat_set<std::string>& hint_keys) override;
-  void PurgeFetchedEntries(base::OnceClosure on_success) override;
 
   // Returns true if |this| is allowed to fetch hints at the navigation time for
   // |url|.
diff --git a/components/optimization_guide/core/hints_manager_unittest.cc b/components/optimization_guide/core/hints_manager_unittest.cc
index e6e898e..d7652b3 100644
--- a/components/optimization_guide/core/hints_manager_unittest.cc
+++ b/components/optimization_guide/core/hints_manager_unittest.cc
@@ -1697,43 +1697,6 @@
   EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
 }
 
-TEST_F(HintsManagerTest, PurgeFetchedEntries) {
-  int cache_duration_in_secs = 60;
-  GURL url("https://host.com/r/cats");
-
-  std::unique_ptr<proto::GetHintsResponse> get_hints_response =
-      std::make_unique<proto::GetHintsResponse>();
-
-  proto::Hint* hint = get_hints_response->add_hints();
-  hint->set_key(url.spec());
-  hint->set_key_representation(proto::FULL_URL);
-  hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
-  proto::PageHint* page_hint = hint->add_page_hints();
-  page_hint->add_allowlisted_optimizations()->set_optimization_type(
-      proto::PERFORMANCE_HINTS);
-  page_hint->set_page_pattern("whatever/*");
-
-  hint = get_hints_response->add_hints();
-  hint->set_key_representation(proto::HOST);
-  hint->set_key(url.host());
-  page_hint = hint->add_page_hints();
-  page_hint->set_page_pattern("anything/*");
-
-  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->hint_cache()->UpdateFetchedHints(
-      std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
-      run_loop->QuitClosure());
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-
-  run_loop = std::make_unique<base::RunLoop>();
-  hints_manager()->PurgeFetchedEntries(run_loop->QuitClosure());
-  run_loop->Run();
-
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
-  EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
-}
-
 TEST_F(HintsManagerTest, HintFetcherPrefUpdated_URL) {
   base::Time expiry = base::Time::Now() + base::Hours(1);
   HintsFetcher::AddFetchedHostForTesting(pref_service(), "host-key.com",
diff --git a/components/optimization_guide/core/push_notification_manager.h b/components/optimization_guide/core/push_notification_manager.h
index 9443d759..05a83e7 100644
--- a/components/optimization_guide/core/push_notification_manager.h
+++ b/components/optimization_guide/core/push_notification_manager.h
@@ -31,12 +31,6 @@
         base::OnceClosure on_success,
         proto::KeyRepresentation key_representation,
         const base::flat_set<std::string>& hint_keys) = 0;
-
-    // If a cache of notifications overflowed and the set of hints to invalidate
-    // were lost, this asks the delegate to purge the whole database.
-    // TODO(crbug.com/1347657) can be removed when Android uses
-    // PushNotificationManager and not AndroidPushNotificationManager.
-    virtual void PurgeFetchedEntries(base::OnceClosure on_success) = 0;
   };
 
   // Observer interface to process HintNotificationPayload.payload. Subclasses
diff --git a/components/optimization_guide/core/push_notification_manager_unittest.cc b/components/optimization_guide/core/push_notification_manager_unittest.cc
index 7453396f..677c690f 100644
--- a/components/optimization_guide/core/push_notification_manager_unittest.cc
+++ b/components/optimization_guide/core/push_notification_manager_unittest.cc
@@ -32,10 +32,6 @@
     std::move(on_success).Run();
   }
 
-  void PurgeFetchedEntries(base::OnceClosure on_success) override {
-    // unused - will be deprecated as part of crbug.com/1347657
-  }
-
  private:
   std::vector<RemoveMultiplePair> removed_entries_;
 };
diff --git a/components/password_manager/content/browser/DEPS b/components/password_manager/content/browser/DEPS
index 225871a..3268664 100644
--- a/components/password_manager/content/browser/DEPS
+++ b/components/password_manager/content/browser/DEPS
@@ -8,6 +8,7 @@
   "+components/ukm",
   "+components/user_prefs",
   "+content/public/browser",
+  "+content/test",
   "+net",
   "+services/metrics/public/cpp",
   "+services/service_manager/public/cpp",
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 2f14765..9ae986c2 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -88,10 +88,11 @@
   // call ContentPasswordManagerDriver::SendLoggingAvailability() on |this| to
   // do it actually.
   if (client_->GetLogManager()) {
-    // Do not call the virtual method SendLoggingAvailability from a constructor
-    // here, inline its steps instead.
-    GetPasswordAutofillAgent()->SetLoggingState(
-        client_->GetLogManager()->IsLoggingActive());
+    if (const auto& agent = GetPasswordAutofillAgent()) {
+      // Do not call the virtual method SendLoggingAvailability from a
+      // constructor here, inline its steps instead.
+      agent->SetLoggingState(client_->GetLogManager()->IsLoggingActive());
+    }
   }
 }
 
@@ -126,15 +127,17 @@
 void ContentPasswordManagerDriver::FillPasswordForm(
     const autofill::PasswordFormFillData& form_data) {
   password_autofill_manager_.OnAddPasswordFillData(form_data);
-  GetPasswordAutofillAgent()->FillPasswordForm(
-      autofill::MaybeClearPasswordValues(form_data));
+  if (const auto& agent = GetPasswordAutofillAgent()) {
+    agent->FillPasswordForm(autofill::MaybeClearPasswordValues(form_data));
+  }
 }
 
 void ContentPasswordManagerDriver::InformNoSavedCredentials(
     bool should_show_popup_without_passwords) {
   GetPasswordAutofillManager()->OnNoCredentialsFound();
-  GetPasswordAutofillAgent()->InformNoSavedCredentials(
-      should_show_popup_without_passwords);
+  if (const auto& agent = GetPasswordAutofillAgent()) {
+    agent->InformNoSavedCredentials(should_show_popup_without_passwords);
+  }
 }
 
 void ContentPasswordManagerDriver::FormEligibleForGenerationFound(
@@ -175,7 +178,9 @@
 void ContentPasswordManagerDriver::FillIntoFocusedField(
     bool is_password,
     const std::u16string& credential) {
-  GetPasswordAutofillAgent()->FillIntoFocusedField(is_password, credential);
+  if (const auto& agent = GetPasswordAutofillAgent()) {
+    agent->FillIntoFocusedField(is_password, credential);
+  }
 }
 
 #if BUILDFLAG(IS_ANDROID)
@@ -214,8 +219,9 @@
 }
 
 void ContentPasswordManagerDriver::SendLoggingAvailability() {
-  GetPasswordAutofillAgent()->SetLoggingState(
-      client_->GetLogManager()->IsLoggingActive());
+  if (const auto& agent = GetPasswordAutofillAgent()) {
+    agent->SetLoggingState(client_->GetLogManager()->IsLoggingActive());
+  }
 }
 
 bool ContentPasswordManagerDriver::IsInPrimaryMainFrame() const {
@@ -237,7 +243,9 @@
 
 void ContentPasswordManagerDriver::AnnotateFieldsWithParsingResult(
     const autofill::ParsingResult& parsing_result) {
-  GetPasswordAutofillAgent()->AnnotateFieldsWithParsingResult(parsing_result);
+  if (const auto& agent = GetPasswordAutofillAgent()) {
+    agent->AnnotateFieldsWithParsingResult(parsing_result);
+  }
 }
 
 void ContentPasswordManagerDriver::GeneratePassword(
@@ -476,9 +484,15 @@
 
 const mojo::AssociatedRemote<autofill::mojom::PasswordAutofillAgent>&
 ContentPasswordManagerDriver::GetPasswordAutofillAgent() {
+  if (render_frame_host_->IsAnonymous()) {
+    password_autofill_agent_.reset();
+    return password_autofill_agent_;  // Unbound remote.
+  }
+
   DCHECK(!password_autofill_agent_ ||
          (content::RenderFrameHost::LifecycleState::kPrerendering !=
           render_frame_host_->GetLifecycleState()));
+
   if (!password_autofill_agent_) {
     // Some test environments may have no remote interface support.
     if (render_frame_host_->GetRemoteAssociatedInterfaces()) {
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index e0c08a1..ab7519a 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
+#include "content/test/test_render_frame_host.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -433,4 +434,42 @@
             url::Origin::CreateFromNormalizedTuple("https", "hostname", 443));
 }
 
+TEST_F(ContentPasswordManagerDriverTest,
+       PasswordAutofillDisabledOnAnonymousIframe) {
+  NavigateAndCommit(GURL("https://test.org"));
+
+  content::RenderFrameHost* anonymous_iframe_root =
+      content::RenderFrameHostTester::For(main_rfh())
+          ->AppendAnonymousChild("anonymous_iframe");
+
+  // Navigate an anonymous iframe.
+  GURL anonymous_iframe_url = GURL("https://hostname/path?query#hash");
+  std::unique_ptr<content::NavigationSimulator> navigation_simulator =
+      content::NavigationSimulator::CreateRendererInitiated(
+          anonymous_iframe_url, anonymous_iframe_root);
+  navigation_simulator->Commit();
+  content::RenderFrameHost* anonymous_iframe_1 =
+      navigation_simulator->GetFinalRenderFrameHost();
+
+  // Install a the PasswordAutofillAgent mock. Verify it do not receive commands
+  // from the browser side.
+  FakePasswordAutofillAgent anonymous_fake_agent_;
+  EXPECT_CALL(anonymous_fake_agent_, FillPasswordForm(_)).Times(0);
+  anonymous_iframe_1->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
+      autofill::mojom::PasswordAutofillAgent::Name_,
+      base::BindRepeating(&FakePasswordAutofillAgent::BindPendingReceiver,
+                          base::Unretained(&anonymous_fake_agent_)));
+
+  autofill::FormData initial_form;
+  autofill::FormData form_in_anonymous_iframe =
+      GetFormWithFrameAndFormMetaData(anonymous_iframe_1, initial_form);
+
+  // Verify autofill can not be triggered by browser side.
+  std::unique_ptr<ContentPasswordManagerDriver> driver(
+      std::make_unique<ContentPasswordManagerDriver>(
+          anonymous_iframe_1, &password_manager_client_, &autofill_client_));
+  driver->FillPasswordForm(GetTestPasswordFormFillData());
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/fake_form_fetcher.cc b/components/password_manager/core/browser/fake_form_fetcher.cc
index 7c43006..0a8a643 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.cc
+++ b/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -38,8 +38,8 @@
   return stats_;
 }
 
-const std::vector<const PasswordForm*>&
-FakeFormFetcher::GetInsecureCredentials() const {
+std::vector<const PasswordForm*> FakeFormFetcher::GetInsecureCredentials()
+    const {
   return insecure_credentials_;
 }
 
diff --git a/components/password_manager/core/browser/fake_form_fetcher.h b/components/password_manager/core/browser/fake_form_fetcher.h
index 40e5444..4482bf27 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.h
+++ b/components/password_manager/core/browser/fake_form_fetcher.h
@@ -42,8 +42,7 @@
   State GetState() const override;
 
   const std::vector<InteractionsStats>& GetInteractionsStats() const override;
-  const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const override;
+  std::vector<const PasswordForm*> GetInsecureCredentials() const override;
   std::vector<const PasswordForm*> GetNonFederatedMatches() const override;
   std::vector<const PasswordForm*> GetFederatedMatches() const override;
   bool IsBlocklisted() const override;
diff --git a/components/password_manager/core/browser/form_fetcher.h b/components/password_manager/core/browser/form_fetcher.h
index a7101192..efb3174 100644
--- a/components/password_manager/core/browser/form_fetcher.h
+++ b/components/password_manager/core/browser/form_fetcher.h
@@ -67,15 +67,18 @@
       const = 0;
 
   // Returns all PasswordForm entries that have insecure features.
-  virtual const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const = 0;
+  // Do not store the result of this call. The pointers become invalid if `this`
+  // receives new results from a password store.
+  virtual std::vector<const PasswordForm*> GetInsecureCredentials() const = 0;
 
-  // Non-federated matches obtained from the backend. Valid only if GetState()
-  // returns NOT_WAITING.
+  // Non-federated matches obtained from the backend.
+  // Do not store the result of this call. The pointers become invalid if `this`
+  // receives new results from a password store.
   virtual std::vector<const PasswordForm*> GetNonFederatedMatches() const = 0;
 
-  // Federated matches obtained from the backend. Valid only if GetState()
-  // returns NOT_WAITING.
+  // Federated matches obtained from the backend.
+  // Do not store the result of this call. The pointers become invalid if `this`
+  // receives new results from a password store.
   virtual std::vector<const PasswordForm*> GetFederatedMatches() const = 0;
 
   // Whether there are blocklisted matches in the backend. Valid only if
diff --git a/components/password_manager/core/browser/form_fetcher_impl.cc b/components/password_manager/core/browser/form_fetcher_impl.cc
index 2a5e507..de98d78 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -148,9 +148,9 @@
   return interactions_stats_;
 }
 
-const std::vector<const PasswordForm*>&
-FormFetcherImpl::GetInsecureCredentials() const {
-  return insecure_credentials_;
+std::vector<const PasswordForm*> FormFetcherImpl::GetInsecureCredentials()
+    const {
+  return MakeWeakCopies(insecure_credentials_);
 }
 
 std::vector<const PasswordForm*> FormFetcherImpl::GetNonFederatedMatches()
@@ -230,7 +230,7 @@
       &result->preferred_match_);
 
   result->interactions_stats_ = interactions_stats_;
-  result->insecure_credentials_ = insecure_credentials_;
+  result->insecure_credentials_ = MakeCopies(insecure_credentials_);
   result->state_ = state_;
   result->need_to_refetch_ = need_to_refetch_;
 
@@ -271,7 +271,7 @@
       }
     } else {
       if (!form->password_issues.empty())
-        insecure_credentials_.push_back(form.get());
+        insecure_credentials_.push_back(std::make_unique<PasswordForm>(*form));
       if (form->IsFederatedCredential()) {
         federated_.push_back(std::move(form));
       } else {
diff --git a/components/password_manager/core/browser/form_fetcher_impl.h b/components/password_manager/core/browser/form_fetcher_impl.h
index 7784712..ec6aefa 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/components/password_manager/core/browser/form_fetcher_impl.h
@@ -46,8 +46,7 @@
   void Fetch() override;
   State GetState() const override;
   const std::vector<InteractionsStats>& GetInteractionsStats() const override;
-  const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const override;
+  std::vector<const PasswordForm*> GetInsecureCredentials() const override;
   std::vector<const PasswordForm*> GetNonFederatedMatches() const override;
   std::vector<const PasswordForm*> GetFederatedMatches() const override;
   bool IsBlocklisted() const override;
@@ -91,7 +90,7 @@
   std::vector<std::unique_ptr<PasswordForm>> federated_;
 
   // List of insecure credentials for the current domain.
-  std::vector<const PasswordForm*> insecure_credentials_;
+  std::vector<std::unique_ptr<PasswordForm>> insecure_credentials_;
 
   // Indicates whether HTTP passwords should be migrated to HTTPS. This is
   // always false for non HTML forms.
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 8d6e9b9..475705dc 100644
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -896,6 +896,8 @@
   Fetch();
   PasswordForm non_federated = CreateNonFederated();
   PasswordForm federated = CreateFederated();
+  federated.password_issues.insert(
+      {InsecureType::kLeaked, InsecurityMetadata()});
   PasswordForm android_federated = CreateAndroidFederated();
   std::vector<std::unique_ptr<PasswordForm>> results;
   results.push_back(std::make_unique<PasswordForm>(non_federated));
@@ -909,6 +911,8 @@
   EXPECT_THAT(
       form_fetcher_->GetFederatedMatches(),
       UnorderedElementsAre(Pointee(federated), Pointee(android_federated)));
+  EXPECT_THAT(form_fetcher_->GetInsecureCredentials(),
+              UnorderedElementsAre(Pointee(federated)));
   EXPECT_FALSE(form_fetcher_->IsBlocklisted());
 
   ASSERT_TRUE(
@@ -929,6 +933,8 @@
   EXPECT_THAT(
       clone->GetFederatedMatches(),
       UnorderedElementsAre(Pointee(federated), Pointee(android_federated)));
+  EXPECT_THAT(clone->GetInsecureCredentials(),
+              UnorderedElementsAre(Pointee(federated)));
   MockConsumer consumer;
   EXPECT_CALL(consumer, OnFetchCompleted);
   clone->AddConsumer(&consumer);
diff --git a/components/password_manager/core/browser/import/csv_password.cc b/components/password_manager/core/browser/import/csv_password.cc
index db3dc3a..f4bf05c1 100644
--- a/components/password_manager/core/browser/import/csv_password.cc
+++ b/components/password_manager/core/browser/import/csv_password.cc
@@ -39,14 +39,13 @@
       status_(Status::kOK) {}
 
 CSVPassword::CSVPassword(const ColumnMap& map, base::StringPiece row) {
-  if (map.size() != kLabelCount) {
+  if (row.empty() || map.size() != kLabelCount) {
     status_ = Status::kSemanticError;
     return;
   }
 
   size_t field_idx = 0;
   CSVFieldParser parser(row);
-  bool username_set = false;
   status_ = Status::kOK;
 
   while (parser.HasMoreFields()) {
@@ -60,27 +59,16 @@
       continue;
     switch (meaning_it->second) {
       case Label::kOrigin:
-        if (!base::IsStringASCII(field)) {
-          status_ = Status::kSyntaxError;
-          return;
-        }
         url_ = GURL(field);
         break;
       case Label::kUsername:
         username_ = ConvertUTF8(field);
-        username_set = true;
         break;
       case Label::kPassword:
         password_ = ConvertUTF8(field);
         break;
     }
   }
-  // While all of origin, username and password must be set in the CSV data
-  // row, username is permitted to be an empty string, while password and
-  // origin are not.
-  if (!url_.is_valid() || !username_set || password_.empty()) {
-    status_ = Status::kSemanticError;
-  }
 }
 
 CSVPassword::CSVPassword(const CSVPassword&) = default;
diff --git a/components/password_manager/core/browser/import/csv_password_iterator.cc b/components/password_manager/core/browser/import/csv_password_iterator.cc
index 30394365..8f11fce2 100644
--- a/components/password_manager/core/browser/import/csv_password_iterator.cc
+++ b/components/password_manager/core/browser/import/csv_password_iterator.cc
@@ -77,14 +77,12 @@
 void CSVPasswordIterator::SeekToNextValidRow() {
   DCHECK(map_);
   do {
-    csv_row_ = ExtractFirstRow(&csv_rest_);
-    password_.emplace(*map_, csv_row_);
+    csv_row_ = base::TrimString(ExtractFirstRow(&csv_rest_), "\r \t",
+                                base::TRIM_LEADING);
   } while (
-      // Skip over empty lines, and
-      (csv_row_.empty() && !csv_rest_.empty()) ||
-      // lines which are not correctly encoded passwords.
-      (!csv_row_.empty() &&
-       password_->GetParseStatus() != CSVPassword::Status::kOK));
+      // Skip over empty lines.
+      csv_row_.empty() && !csv_rest_.empty());
+  password_.emplace(*map_, csv_row_);
 }
 
 base::StringPiece ConsumeCSVLine(base::StringPiece* input) {
diff --git a/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc b/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
index 622ec40..f69ced9 100644
--- a/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
@@ -32,13 +32,6 @@
   // Because kCSV is just one row, it can be used to create a CSVPassword
   // directly.
 
-  // Mock time so that date_created matches.
-  {
-    base::test::SingleThreadTaskEnvironment env(
-        base::test::TaskEnvironment::TimeSource::MOCK_TIME);
-    EXPECT_EQ(*iter, CSVPassword(kColMap, kCSV));
-  }
-
   // Copy.
   CSVPasswordIterator copy = iter;
   EXPECT_EQ(copy, iter);
@@ -63,57 +56,45 @@
   EXPECT_EQ(++old, iter);
 }
 
-TEST(CSVPasswordIteratorTest, MostRowsCorrect) {
-  const CSVPassword::ColumnMap kColMap = {
-      {1, Label::kOrigin},
-      {4, Label::kUsername},
-      {2, Label::kPassword},
-  };
-  constexpr base::StringPiece kCSVBlob =
-      ",http://example.com,p1,.,u1\n"
-      "something,http://example.com,p2,T,u2\r\n"
-      // The empty line below is completely ignored.
-      "\r\n"
-      "other,http://example.com,p3,>,u\r\r\n"
-      "***,http://example.com,\"p\n4\",\"\n\",\"u\n4\"\n"
-      // The row below is also ignored, because it contains no valid URL.
-      ",:,p,,u\n"
-      "\",\",http://example.com,p5,;,u5\n"
-      // The last row is ignored, because it is invalid.
-      ",http://example.com,p,,u,\"\n";
-  constexpr base::StringPiece kExpectedUsernames[] = {"u1", "u2", "u\r", "u\n4",
-                                                      "u5"};
-
-  CSVPasswordIterator iter(kColMap, kCSVBlob);
-
-  CSVPasswordIterator check = iter;
-  for (size_t i = 0; i < std::size(kExpectedUsernames); ++i) {
-    EXPECT_EQ(CSVPassword::Status::kOK, (check++)->GetParseStatus())
-        << "on line " << i;
-  }
-  EXPECT_NE(CSVPassword::Status::kOK, check->GetParseStatus());
-
-  for (const base::StringPiece& expected_username : kExpectedUsernames) {
-    CSVPassword result = *(iter++);
-    // Detailed checks of the parsed result are made in the test for
-    // CSVPassword. Here only the last field (username) is checked to (1) ensure
-    // that lines are processed in the expected sequence, and (2) line breaks
-    // are handled as expected (in particular, '\r' alone is not a line break).
-    EXPECT_EQ(expected_username, result.GetUsername());
-  }
-}
-
-TEST(CSVPasswordIteratorTest, LastRowCorrect) {
+TEST(CSVPasswordIteratorTest, MostRowsAreValid) {
   const CSVPassword::ColumnMap kColMap = {
       {0, Label::kOrigin},
       {1, Label::kUsername},
       {2, Label::kPassword},
   };
   constexpr base::StringPiece kCSVBlob =
-      "too few fields\n"
-      "http://example.com,\"\"trailing,p\n"
-      "http://notascii.ž.com,u,p\n"
-      "http://example.com,empty-password,\n"
+      "\r\n"
+      "\t\t\t\n"
+      "http://no-failure.example.com,user_1,pwd]\n"
+      "\n"
+      "http://no-failure.example.com,user_2,pwd]\n"
+      "http://no-failure.example.com,user_3,pwd]\n"
+      "http://no-failure.example.com,user_4,pwd]\n";
+
+  CSVPasswordIterator iter(kColMap, kCSVBlob);
+
+  // The iterator should skip all the empty rows and land on the first valid
+  // one.
+
+  for (size_t i = 0; i < 4; i++) {
+    EXPECT_EQ(CSVPassword::Status::kOK, iter->GetParseStatus());
+    iter++;
+  }
+
+  // After iterating over all lines, there is no more data to parse.
+  EXPECT_NE(CSVPassword::Status::kOK, iter->GetParseStatus());
+}
+
+TEST(CSVPasswordIteratorTest, LastRowNonEmpty) {
+  const CSVPassword::ColumnMap kColMap = {
+      {0, Label::kOrigin},
+      {1, Label::kUsername},
+      {2, Label::kPassword},
+  };
+  constexpr base::StringPiece kCSVBlob =
+      "        \n"
+      " \n"
+      "\n"
       "http://no-failure.example.com,to check that,operator++ worked";
 
   CSVPasswordIterator iter(kColMap, kCSVBlob);
@@ -127,21 +108,21 @@
   EXPECT_NE(CSVPassword::Status::kOK, iter->GetParseStatus());
 }
 
-TEST(CSVPasswordIteratorTest, NoRowCorrect) {
+TEST(CSVPasswordIteratorTest, AllRowsAreEmpty) {
   const CSVPassword::ColumnMap kColMap = {
       {0, Label::kOrigin},
       {1, Label::kUsername},
       {2, Label::kPassword},
   };
   constexpr base::StringPiece kCSVBlob =
-      "too few fields\n"
-      "http://example.com,\"\"trailing,p\n"
-      "http://notascii.ž.com,u,p\n"
-      "http://example.com,empty-password,";
+      "     \t   \r\n"
+      " \n"
+      "\n"
+      "         ";
 
   CSVPasswordIterator iter(kColMap, kCSVBlob);
   CSVPasswordIterator end(kColMap, base::StringPiece());
-
+  EXPECT_EQ(0, std::distance(iter, end));
   EXPECT_EQ(iter, end);
 }
 
diff --git a/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc b/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
index 7c082737..7dd507b 100644
--- a/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
@@ -76,22 +76,18 @@
   EXPECT_EQ(seq.begin(), seq.end());
 }
 
-TEST(CSVPasswordSequenceTest, InvalidCSV) {
-  static constexpr char kQuotes[] =
-      "Display Name,Login,Secret Question,Password,URL,Timestamp\n"
-      ":),Bob,ABCD!,odd,https://example.org,\"\n";
-  CSVPasswordSequence seq(kQuotes);
-  EXPECT_EQ(CSVPassword::Status::kOK, seq.result());
-  EXPECT_EQ(seq.begin(), seq.end());
-}
-
-TEST(CSVPasswordSequenceTest, MissingData) {
+TEST(CSVPasswordSequenceTest, SkipsEmptyLines) {
   static constexpr char kNoUrl[] =
       "Display Name,Login,Secret Question,Password,URL,Timestamp\n"
-      ":),Bob,ABCD!,odd\n";
+      "\n"
+      "\t\t\t\r\n"
+      "            \n"
+      "non_empty,pwd\n"
+      "non_empty,pwd\n"
+      "    ";
   CSVPasswordSequence seq(kNoUrl);
   EXPECT_EQ(CSVPassword::Status::kOK, seq.result());
-  EXPECT_EQ(seq.begin(), seq.end());
+  ASSERT_EQ(2, std::distance(seq.begin(), seq.end()));
 }
 
 TEST(CSVPasswordSequenceTest, Iteration) {
diff --git a/components/password_manager/core/browser/import/csv_password_unittest.cc b/components/password_manager/core/browser/import/csv_password_unittest.cc
index 5a161bf..1562d6d 100644
--- a/components/password_manager/core/browser/import/csv_password_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_password_unittest.cc
@@ -258,90 +258,37 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     CSVPasswordTestFailure,
-    ::testing::Values(
-        TestCaseBuilder("no columns specified")
-            .Map({})
-            .CSV("http://example.com,user,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("not ASCII")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV("http://example.com/ř,user,password")
-            .Status(Status::kSyntaxError)
-            .Build(),
-        TestCaseBuilder("no origin in map")
-            .Map({{1, Label::kUsername}, {2, Label::kPassword}})
-            .CSV("http://example.com,user,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("no username in map")
-            .Map({{0, Label::kOrigin}, {2, Label::kPassword}})
-            .CSV("http://example.com,user,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("no password in map")
-            .Map({{0, Label::kOrigin}, {1, Label::kUsername}})
-            .CSV("http://example.com,user,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("no origin in CSV")
-            .Map({{0, Label::kUsername},
-                  {1, Label::kPassword},
-                  {2, Label::kOrigin}})
-            .CSV("user,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("no username in CSV")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kPassword},
-                  {2, Label::kUsername}})
-            .CSV("http://example.com,password")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("no password in CSV")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV("http://example.com,user")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("malformed CSV")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV("\"")
-            .Status(Status::kSyntaxError)
-            .Build(),
-        TestCaseBuilder("another malformed CSV")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV("Url,Username,\"Password\n")
-            .Status(Status::kSyntaxError)
-            .Build(),
-        TestCaseBuilder("no ASCII")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV("https://aččountš.googľe.čom/,test@gmail.com,test1\n")
-            .Status(Status::kSyntaxError)
-            .Build(),
-        TestCaseBuilder("invalid URI")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword}})
-            .CSV(":,test@gmail.com,test1\n")
-            .Status(Status::kSemanticError)
-            .Build(),
-        TestCaseBuilder("map not injective")
-            .Map({{0, Label::kOrigin},
-                  {1, Label::kUsername},
-                  {2, Label::kPassword},
-                  {3, Label::kUsername}})
-            .CSV("http://example.com,user,pwd,user2")
-            .Status(Status::kSemanticError)
-            .Build()));
+    ::testing::Values(TestCaseBuilder("no columns specified")
+                          .Map({})
+                          .CSV("http://example.com,user,password")
+                          .Status(Status::kSemanticError)
+                          .Build(),
+                      TestCaseBuilder("empty line")
+                          .Map({})
+                          .CSV("")
+                          .Status(Status::kSemanticError)
+                          .Build(),
+                      TestCaseBuilder("malformed CSV")
+                          .Map({{0, Label::kOrigin},
+                                {1, Label::kUsername},
+                                {2, Label::kPassword}})
+                          .CSV("\"")
+                          .Status(Status::kSyntaxError)
+                          .Build(),
+                      TestCaseBuilder("another malformed CSV")
+                          .Map({{0, Label::kOrigin},
+                                {1, Label::kUsername},
+                                {2, Label::kPassword}})
+                          .CSV("Url,Username,\"Password\n")
+                          .Status(Status::kSyntaxError)
+                          .Build(),
+                      TestCaseBuilder("map not injective")
+                          .Map({{0, Label::kOrigin},
+                                {1, Label::kUsername},
+                                {2, Label::kPassword},
+                                {3, Label::kUsername}})
+                          .CSV("http://example.com,user,pwd,user2")
+                          .Status(Status::kSemanticError)
+                          .Build()));
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
index 3059e38..a468977 100644
--- a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
@@ -40,7 +40,7 @@
               GetInteractionsStats,
               (),
               (const override));
-  MOCK_METHOD(const std::vector<const PasswordForm*>&,
+  MOCK_METHOD(std::vector<const PasswordForm*>,
               GetInsecureCredentials,
               (),
               (const override));
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index e3025cb..b826c3e 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -246,8 +246,8 @@
   return base::make_span(form_fetcher_->GetInteractionsStats());
 }
 
-const std::vector<const PasswordForm*>&
-PasswordFormManager::GetInsecureCredentials() const {
+std::vector<const PasswordForm*> PasswordFormManager::GetInsecureCredentials()
+    const {
   return form_fetcher_->GetInsecureCredentials();
 }
 
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index dbeffa93..30ccfab 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -153,8 +153,7 @@
   metrics_util::CredentialSourceType GetCredentialSource() const override;
   PasswordFormMetricsRecorder* GetMetricsRecorder() override;
   base::span<const InteractionsStats> GetInteractionsStats() const override;
-  const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const override;
+  std::vector<const PasswordForm*> GetInsecureCredentials() const override;
   bool IsBlocklisted() const override;
   bool WasUnblocklisted() const override;
   bool IsMovableToAccountStore() const override;
diff --git a/components/password_manager/core/browser/password_form_manager_for_ui.h b/components/password_manager/core/browser/password_form_manager_for_ui.h
index 158369f..95223c5f 100644
--- a/components/password_manager/core/browser/password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/password_form_manager_for_ui.h
@@ -52,8 +52,7 @@
   virtual base::span<const InteractionsStats> GetInteractionsStats() const = 0;
 
   // List of insecure passwords for the current site.
-  virtual const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const = 0;
+  virtual std::vector<const PasswordForm*> GetInsecureCredentials() const = 0;
 
   // Determines if the user opted to 'never remember' passwords for this form.
   virtual bool IsBlocklisted() const = 0;
diff --git a/components/password_manager/core/browser/password_generation_manager.cc b/components/password_manager/core/browser/password_generation_manager.cc
index ddb39cbee..3fa816e 100644
--- a/components/password_manager/core/browser/password_generation_manager.cc
+++ b/components/password_manager/core/browser/password_generation_manager.cc
@@ -47,8 +47,7 @@
   metrics_util::CredentialSourceType GetCredentialSource() const override;
   PasswordFormMetricsRecorder* GetMetricsRecorder() override;
   base::span<const InteractionsStats> GetInteractionsStats() const override;
-  const std::vector<const PasswordForm*>& GetInsecureCredentials()
-      const override;
+  std::vector<const PasswordForm*> GetInsecureCredentials() const override;
   bool IsBlocklisted() const override;
   bool WasUnblocklisted() const override;
   bool IsMovableToAccountStore() const override;
@@ -67,7 +66,6 @@
  private:
   PasswordForm pending_form_;
   std::vector<const PasswordForm*> matches_;
-  std::vector<const PasswordForm*> insecure_credentials_;
   const std::vector<PasswordForm> federated_matches_;
   const std::vector<PasswordForm> non_federated_matches_;
 
@@ -127,9 +125,9 @@
   return {};
 }
 
-const std::vector<const PasswordForm*>&
-PasswordDataForUI::GetInsecureCredentials() const {
-  return insecure_credentials_;
+std::vector<const PasswordForm*> PasswordDataForUI::GetInsecureCredentials()
+    const {
+  return {};
 }
 
 bool PasswordDataForUI::IsBlocklisted() const {
diff --git a/components/password_manager/services/csv_password/csv_password_parser_impl_unittest.cc b/components/password_manager/services/csv_password/csv_password_parser_impl_unittest.cc
index 2868aa9..6afd263 100644
--- a/components/password_manager/services/csv_password/csv_password_parser_impl_unittest.cc
+++ b/components/password_manager/services/csv_password/csv_password_parser_impl_unittest.cc
@@ -101,19 +101,29 @@
   ParseCSV(raw_csv, base::BindLambdaForTesting(
                         [&](mojom::CSVPasswordSequencePtr sequence) {
                           EXPECT_TRUE(sequence);
-                          EXPECT_THAT(sequence->csv_passwords, IsEmpty());
+                          EXPECT_EQ(1u, sequence->csv_passwords.size());
+                          EXPECT_EQ("Bob",
+                                    sequence->csv_passwords[0].GetUsername());
+                          EXPECT_EQ(GURL("https://example.org"),
+                                    sequence->csv_passwords[0].GetURL());
                         }));
 }
 
 TEST_F(CSVPasswordParserImplTest, ParseFileMissingFields) {
   const std::string raw_csv =
       R"(Display Name,   ,Login,Secret Question,Password,URL,Timestamp
-        :)            Bob,ABCD!,blabla,https://example.org,132)";
+        :)        Bob,ABCD!,blabla,https://example.org,132)";
 
   ParseCSV(raw_csv, base::BindLambdaForTesting(
                         [&](mojom::CSVPasswordSequencePtr sequence) {
                           EXPECT_TRUE(sequence);
-                          EXPECT_THAT(sequence->csv_passwords, IsEmpty());
+                          EXPECT_EQ(1u, sequence->csv_passwords.size());
+                          EXPECT_EQ("blabla",
+                                    sequence->csv_passwords[0].GetUsername());
+                          EXPECT_EQ("132",
+                                    sequence->csv_passwords[0].GetPassword());
+                          EXPECT_EQ(GURL(""),
+                                    sequence->csv_passwords[0].GetURL());
                         }));
 }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 747a026..3dc92731 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -18000,7 +18000,7 @@
         {
           'name': 'SAML_INTERSTITIAL',
           'value': 1,
-          'caption': '''Redirect to SAML IdP after user confirmation''',
+          'caption': '''Redirect to SAML IdP by default (prior to <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> 99 user confirmation is needed)''',
         },
       ],
       'supported_on': ['chrome_os:51-'],
@@ -18017,7 +18017,9 @@
 
       If set to GAIA, login will be done via the normal GAIA authentication flow.
 
-      If set to SAML_INTERSTITIAL, login will show an interstitial screen offering the user to go forward with authentication via the SAML IdP of the device's enrollment domain, or go back to the normal GAIA login flow.'''
+      If set to SAML_INTERSTITIAL, login will automatically redirect to SAML IdP by default. The user is still allowed to go back to the normal GAIA login flow.
+
+      Note: the additional user confirmation screen, which was shown on <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> until version 99, isn't displayed anymore. If SAML IdP isn't configured and this policy is set to SAML_INTERSTITIAL, redirect will fail with the 400 error.'''
     },
     {
       'name': 'UsbDetachableWhitelist',
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
index 6687cf6..243d066 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -837,6 +837,10 @@
   base::Value::Dict dict;
   if (cdr.has_url())
     dict.Set("url", cdr.url());
+  if (cdr.digests().has_sha256()) {
+    const std::string& sha256 = cdr.digests().sha256();
+    dict.Set("digests.sha256", base::HexEncode(sha256.c_str(), sha256.size()));
+  }
   if (cdr.has_download_type())
     dict.Set("download_type", cdr.download_type());
   if (cdr.has_length())
diff --git a/components/segmentation_platform/embedder/BUILD.gn b/components/segmentation_platform/embedder/BUILD.gn
index 10655dc..385ab7e 100644
--- a/components/segmentation_platform/embedder/BUILD.gn
+++ b/components/segmentation_platform/embedder/BUILD.gn
@@ -11,12 +11,17 @@
   sources = [
     "input_delegate/price_tracking_input_delegate.cc",
     "input_delegate/price_tracking_input_delegate.h",
+    "model_provider_factory_impl.cc",
+    "model_provider_factory_impl.h",
   ]
 
   deps = [
     "//base",
     "//components/commerce/core:shopping_service",
+    "//components/optimization_guide/core",
+    "//components/optimization_guide/core:features",
     "//components/segmentation_platform/internal",
+    "//components/segmentation_platform/internal:optimization_guide_segmentation_handler",
     "//components/segmentation_platform/internal/proto",
     "//components/segmentation_platform/public",
     "//url",
@@ -28,7 +33,10 @@
 
   # IMPORTANT NOTE: When adding new tests, also remember to update the list of
   # tests in //components/segmentation_platform/components_unittests.filter
-  sources = [ "input_delegate/price_tracking_input_delegate_unittest.cc" ]
+  sources = [
+    "input_delegate/price_tracking_input_delegate_unittest.cc",
+    "model_provider_factory_impl_unittest.cc",
+  ]
 
   deps = [
     ":embedder",
@@ -37,6 +45,8 @@
     "//components/commerce/core:feature_list",
     "//components/commerce/core:shopping_service",
     "//components/commerce/core:shopping_service_test_support",
+    "//components/optimization_guide/core",
+    "//components/optimization_guide/core:test_support",
     "//components/segmentation_platform/internal",
     "//components/segmentation_platform/internal/proto",
     "//components/segmentation_platform/public",
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc b/components/segmentation_platform/embedder/model_provider_factory_impl.cc
similarity index 95%
rename from chrome/browser/segmentation_platform/model_provider_factory_impl.cc
rename to components/segmentation_platform/embedder/model_provider_factory_impl.cc
index 60e5a131..fd6b03a 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
+++ b/components/segmentation_platform/embedder/model_provider_factory_impl.cc
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
+#include "components/segmentation_platform/embedder/model_provider_factory_impl.h"
 
-#include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
 #include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
 #include "components/segmentation_platform/public/config.h"
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.h b/components/segmentation_platform/embedder/model_provider_factory_impl.h
similarity index 90%
rename from chrome/browser/segmentation_platform/model_provider_factory_impl.h
rename to components/segmentation_platform/embedder/model_provider_factory_impl.h
index 8f39897e..82912c5f 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl.h
+++ b/components/segmentation_platform/embedder/model_provider_factory_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
-#define CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_EMBEDDER_MODEL_PROVIDER_FACTORY_IMPL_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_EMBEDDER_MODEL_PROVIDER_FACTORY_IMPL_H_
 
 #include <memory>
 
@@ -74,4 +74,4 @@
 
 }  // namespace segmentation_platform
 
-#endif  // CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_EMBEDDER_MODEL_PROVIDER_FACTORY_IMPL_H_
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc b/components/segmentation_platform/embedder/model_provider_factory_impl_unittest.cc
similarity index 96%
rename from chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
rename to components/segmentation_platform/embedder/model_provider_factory_impl_unittest.cc
index 705268f..a64f937 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
+++ b/components/segmentation_platform/embedder/model_provider_factory_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
+#include "components/segmentation_platform/embedder/model_provider_factory_impl.h"
 
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
diff --git a/components/signin/internal/identity_manager/account_tracker_service.cc b/components/signin/internal/identity_manager/account_tracker_service.cc
index 2e27788..ecad90b 100644
--- a/components/signin/internal/identity_manager/account_tracker_service.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service.cc
@@ -44,23 +44,23 @@
 #endif
 
 namespace {
-const char kAccountKeyPath[] = "account_id";
-const char kAccountEmailPath[] = "email";
-const char kAccountGaiaPath[] = "gaia";
-const char kAccountHostedDomainPath[] = "hd";
-const char kAccountFullNamePath[] = "full_name";
-const char kAccountGivenNamePath[] = "given_name";
-const char kAccountLocalePath[] = "locale";
-const char kAccountPictureURLPath[] = "picture_url";
-const char kLastDownloadedImageURLWithSizePath[] =
+const char kAccountKeyKey[] = "account_id";
+const char kAccountEmailKey[] = "email";
+const char kAccountGaiaKey[] = "gaia";
+const char kAccountHostedDomainKey[] = "hd";
+const char kAccountFullNameKey[] = "full_name";
+const char kAccountGivenNameKey[] = "given_name";
+const char kAccountLocaleKey[] = "locale";
+const char kAccountPictureURLKey[] = "picture_url";
+const char kLastDownloadedImageURLWithSizeKey[] =
     "last_downloaded_image_url_with_size";
-const char kAccountChildAttributePath[] = "is_supervised_child";
-const char kAdvancedProtectionAccountStatusPath[] =
+const char kAccountChildAttributeKey[] = "is_supervised_child";
+const char kAdvancedProtectionAccountStatusKey[] =
     "is_under_advanced_protection";
 
 // This key is deprecated since 2021/07 and should be removed after migration.
-// It was replaced by kAccountChildAttributePath.
-const char kDeprecatedChildStatusPath[] = "is_child_account";
+// It was replaced by kAccountChildAttributeKey.
+const char kDeprecatedChildStatusKey[] = "is_child_account";
 
 // This key is deprecated since 2022/02 and should be removed after migration.
 // It was replaced by GetCapabilityPrefPath(capability_name) method that derives
@@ -121,11 +121,11 @@
   return base::StrCat({"accountcapabilities.", capability_name});
 }
 
-void SetAccountCapabilityState(base::Value* value,
+void SetAccountCapabilityState(base::Value::Dict& value,
                                base::StringPiece capability_name,
                                signin::Tribool state) {
-  value->SetIntPath(GetCapabilityPrefPath(capability_name),
-                    static_cast<int>(state));
+  value.SetByDottedPath(GetCapabilityPrefPath(capability_name),
+                        static_cast<int>(state));
 }
 
 signin::Tribool ParseTribool(absl::optional<int> int_value) {
@@ -144,17 +144,17 @@
   }
 }
 
-signin::Tribool FindAccountCapabilityState(const base::Value& value,
+signin::Tribool FindAccountCapabilityState(const base::Value::Dict& dict,
                                            base::StringPiece name) {
   absl::optional<int> capability =
-      value.FindIntPath(GetCapabilityPrefPath(name));
+      dict.FindIntByDottedPath(GetCapabilityPrefPath(name));
   return ParseTribool(capability);
 }
 
-void GetString(const base::Value& dict,
+void GetString(const base::Value::Dict& dict,
                base::StringPiece key,
                std::string& result) {
-  if (const std::string* value = dict.FindStringKey(key)) {
+  if (const std::string* value = dict.FindString(key)) {
     result = *value;
   }
 }
@@ -555,15 +555,15 @@
   if (!success || !pref_service_)
     return;
 
-  base::DictionaryValue* dict = nullptr;
+  base::Value::Dict* dict = nullptr;
   ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
-  for (size_t i = 0; i < update->GetListDeprecated().size();
-       ++i, dict = nullptr) {
-    base::Value& dict_value = update->GetListDeprecated()[i];
-    if (dict_value.is_dict()) {
-      dict = static_cast<base::DictionaryValue*>(&dict_value);
-      const std::string* account_key = dict->FindStringKey(kAccountKeyPath);
+  base::Value::List& list = update->GetList();
+  for (base::Value& value : list) {
+    base::Value::Dict* maybe_dict = value.GetIfDict();
+    if (maybe_dict) {
+      const std::string* account_key = maybe_dict->FindString(kAccountKeyKey);
       if (account_key && *account_key == account_id.ToString()) {
+        dict = maybe_dict;
         break;
       }
     }
@@ -572,7 +572,7 @@
   if (!dict) {
     return;
   }
-  dict->SetString(kLastDownloadedImageURLWithSizePath, image_url_with_size);
+  dict->Set(kLastDownloadedImageURLWithSizeKey, image_url_with_size);
 }
 
 void AccountTrackerService::RemoveAccountImageFromDisk(
@@ -588,12 +588,9 @@
       pref_service_->GetValueList(prefs::kAccountInfo);
   std::set<CoreAccountId> to_remove;
   for (size_t i = 0; i < list.size(); ++i) {
-    const base::Value& dict_value = list[i];
-    if (dict_value.is_dict()) {
-      const base::DictionaryValue& dict =
-          base::Value::AsDictionaryValue(dict_value);
-      if (const std::string* account_key =
-              dict.FindStringKey(kAccountKeyPath)) {
+    const base::Value::Dict* dict = list[i].GetIfDict();
+    if (dict) {
+      if (const std::string* account_key = dict->FindString(kAccountKeyKey)) {
         // Ignore incorrectly persisted non-canonical account ids.
         if (account_key->find('@') != std::string::npos &&
             *account_key != gaia::CanonicalizeEmail(*account_key)) {
@@ -605,58 +602,55 @@
         StartTrackingAccount(account_id);
         AccountInfo& account_info = accounts_[account_id];
 
-        GetString(dict, kAccountGaiaPath, account_info.gaia);
-        GetString(dict, kAccountEmailPath, account_info.email);
-        GetString(dict, kAccountHostedDomainPath, account_info.hosted_domain);
-        GetString(dict, kAccountFullNamePath, account_info.full_name);
-        GetString(dict, kAccountGivenNamePath, account_info.given_name);
-        GetString(dict, kAccountLocalePath, account_info.locale);
-        GetString(dict, kAccountPictureURLPath, account_info.picture_url);
-        GetString(dict, kLastDownloadedImageURLWithSizePath,
+        GetString(*dict, kAccountGaiaKey, account_info.gaia);
+        GetString(*dict, kAccountEmailKey, account_info.email);
+        GetString(*dict, kAccountHostedDomainKey, account_info.hosted_domain);
+        GetString(*dict, kAccountFullNameKey, account_info.full_name);
+        GetString(*dict, kAccountGivenNameKey, account_info.given_name);
+        GetString(*dict, kAccountLocaleKey, account_info.locale);
+        GetString(*dict, kAccountPictureURLKey, account_info.picture_url);
+        GetString(*dict, kLastDownloadedImageURLWithSizeKey,
                   account_info.last_downloaded_image_url_with_size);
 
         if (absl::optional<bool> is_child_status =
-                dict.FindBoolKey(kDeprecatedChildStatusPath)) {
+                dict->FindBool(kDeprecatedChildStatusKey)) {
           account_info.is_child_account = is_child_status.value()
                                               ? signin::Tribool::kTrue
                                               : signin::Tribool::kFalse;
-          // Migrate to kAccountChildAttributePath.
+          // Migrate to kAccountChildAttributeKey.
           ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
-          base::Value* update_dict = &update->GetListDeprecated()[i];
-          DCHECK(update_dict->is_dict());
-          update_dict->SetIntPath(
-              kAccountChildAttributePath,
-              static_cast<int>(account_info.is_child_account));
-          update_dict->RemoveKey(kDeprecatedChildStatusPath);
+          base::Value::Dict& update_dict = update->GetList()[i].GetDict();
+          update_dict.Set(kAccountChildAttributeKey,
+                          static_cast<int>(account_info.is_child_account));
+          update_dict.Remove(kDeprecatedChildStatusKey);
         } else {
           account_info.is_child_account =
-              ParseTribool(dict.FindIntPath(kAccountChildAttributePath));
+              ParseTribool(dict->FindInt(kAccountChildAttributeKey));
         }
 
         absl::optional<bool> is_under_advanced_protection =
-            dict.FindBoolKey(kAdvancedProtectionAccountStatusPath);
+            dict->FindBool(kAdvancedProtectionAccountStatusKey);
         if (is_under_advanced_protection.has_value()) {
           account_info.is_under_advanced_protection =
               is_under_advanced_protection.value();
         }
 
         if (absl::optional<int> can_offer_extended_chrome_sync_promos =
-                dict.FindIntPath(
+                dict->FindIntByDottedPath(
                     kDeprecatedCanOfferExtendedChromeSyncPromosPrefPath)) {
           // Migrate to Capability names based pref paths.
           ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
-          base::Value* update_dict = &update->GetListDeprecated()[i];
-          DCHECK(update_dict->is_dict());
+          base::Value::Dict& update_dict = update->GetList()[i].GetDict();
           SetAccountCapabilityState(
               update_dict, kCanOfferExtendedChromeSyncPromosCapabilityName,
               ParseTribool(can_offer_extended_chrome_sync_promos));
-          update_dict->RemovePath(
+          update_dict.RemoveByDottedPath(
               kDeprecatedCanOfferExtendedChromeSyncPromosPrefPath);
         }
 
         for (const std::string& name :
              AccountCapabilities::GetSupportedAccountCapabilityNames()) {
-          switch (FindAccountCapabilityState(dict, name)) {
+          switch (FindAccountCapabilityState(*dict, name)) {
             case signin::Tribool::kUnknown:
               account_info.capabilities.capabilities_map_.erase(name);
               break;
@@ -708,47 +702,45 @@
   if (!pref_service_)
     return;
 
-  base::DictionaryValue* dict = nullptr;
+  base::Value::Dict* dict = nullptr;
   ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
-  for (size_t i = 0; i < update->GetListDeprecated().size();
-       ++i, dict = nullptr) {
-    base::Value& dict_value = update->GetListDeprecated()[i];
-    if (dict_value.is_dict()) {
-      dict = static_cast<base::DictionaryValue*>(&dict_value);
-      const std::string* account_key = dict->FindStringKey(kAccountKeyPath);
+  base::Value::List& list = update->GetList();
+  for (base::Value& value : list) {
+    base::Value::Dict* maybe_dict = value.GetIfDict();
+    if (maybe_dict) {
+      const std::string* account_key = maybe_dict->FindString(kAccountKeyKey);
       if (account_key && *account_key == account_info.account_id.ToString()) {
+        dict = maybe_dict;
         break;
       }
     }
   }
 
   if (!dict) {
-    update->Append(base::Value(base::Value::Type::DICTIONARY));
-    base::Value& dict_value = update->GetListDeprecated().back();
-    DCHECK(dict_value.is_dict());
-    dict = static_cast<base::DictionaryValue*>(&dict_value);
-    dict->SetString(kAccountKeyPath, account_info.account_id.ToString());
+    list.Append(base::Value::Dict());
+    dict = &list.back().GetDict();
+    dict->Set(kAccountKeyKey, account_info.account_id.ToString());
   }
 
-  dict->SetString(kAccountEmailPath, account_info.email);
-  dict->SetString(kAccountGaiaPath, account_info.gaia);
-  dict->SetString(kAccountHostedDomainPath, account_info.hosted_domain);
-  dict->SetString(kAccountFullNamePath, account_info.full_name);
-  dict->SetString(kAccountGivenNamePath, account_info.given_name);
-  dict->SetString(kAccountLocalePath, account_info.locale);
-  dict->SetString(kAccountPictureURLPath, account_info.picture_url);
-  dict->SetIntPath(kAccountChildAttributePath,
-                   static_cast<int>(account_info.is_child_account));
-  dict->SetBoolean(kAdvancedProtectionAccountStatusPath,
-                   account_info.is_under_advanced_protection);
-  // |kLastDownloadedImageURLWithSizePath| should only be set after the GAIA
+  dict->Set(kAccountEmailKey, account_info.email);
+  dict->Set(kAccountGaiaKey, account_info.gaia);
+  dict->Set(kAccountHostedDomainKey, account_info.hosted_domain);
+  dict->Set(kAccountFullNameKey, account_info.full_name);
+  dict->Set(kAccountGivenNameKey, account_info.given_name);
+  dict->Set(kAccountLocaleKey, account_info.locale);
+  dict->Set(kAccountPictureURLKey, account_info.picture_url);
+  dict->Set(kAccountChildAttributeKey,
+            static_cast<int>(account_info.is_child_account));
+  dict->Set(kAdvancedProtectionAccountStatusKey,
+            account_info.is_under_advanced_protection);
+  // |kLastDownloadedImageURLWithSizeKey| should only be set after the GAIA
   // picture is successufly saved to disk. Otherwise, there is no guarantee that
-  // |kLastDownloadedImageURLWithSizePath| matches the picture on disk.
+  // |kLastDownloadedImageURLWithSizeKey| matches the picture on disk.
   for (const std::string& name :
        AccountCapabilities::GetSupportedAccountCapabilityNames()) {
     signin::Tribool capability_state =
         account_info.capabilities.GetCapabilityByName(name);
-    SetAccountCapabilityState(dict, name, capability_state);
+    SetAccountCapabilityState(*dict, name, capability_state);
   }
 }
 
@@ -758,10 +750,10 @@
 
   ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
   const std::string account_id = account_info.account_id.ToString();
-  update->EraseListValueIf([&account_id](const base::Value& value) {
+  update->GetList().EraseIf([&account_id](const base::Value& value) {
     if (!value.is_dict())
       return false;
-    const std::string* account_key = value.FindStringKey(kAccountKeyPath);
+    const std::string* account_key = value.GetDict().FindString(kAccountKeyKey);
     return account_key && *account_key == account_id;
   });
 }
diff --git a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
index 9f5e14a..e45958b1 100644
--- a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
@@ -1206,18 +1206,19 @@
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", email_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_beta);
-  dict.SetStringKey("email", email_beta);
-  dict.SetStringKey("gaia", gaia_beta);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", email_beta);
+  dict.Set("email", email_beta);
+  dict.Set("gaia", gaia_beta);
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1252,18 +1253,19 @@
   const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", email_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_beta);
-  dict.SetStringKey("email", email_beta);
-  dict.SetStringKey("gaia", "");
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", email_beta);
+  dict.Set("email", email_beta);
+  dict.Set("gaia", "");
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1299,25 +1301,26 @@
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", email_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_beta);
-  dict.SetStringKey("email", email_beta);
-  dict.SetStringKey("gaia", gaia_beta);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", email_beta);
+  dict.Set("email", email_beta);
+  dict.Set("gaia", gaia_beta);
+  update_list.Append(std::move(dict));
 
   // Succeed miggrated account.
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", gaia_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", gaia_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1653,18 +1656,19 @@
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", gaia_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", gaia_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", gaia_beta);
-  dict.SetStringKey("email", email_beta);
-  dict.SetStringKey("gaia", gaia_beta);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", gaia_beta);
+  dict.Set("email", email_beta);
+  dict.Set("gaia", gaia_beta);
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1682,18 +1686,19 @@
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", email_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_beta);
-  dict.SetStringKey("email", email_beta);
-  dict.SetStringKey("gaia", gaia_beta);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", email_beta);
+  dict.Set("email", email_beta);
+  dict.Set("gaia", gaia_beta);
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1711,20 +1716,21 @@
   const std::string gaia_foobar = AccountKeyToGaiaId(kAccountKeyFooDotBar);
 
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  base::Value::List& update_list = update->GetList();
 
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_alpha);
-  dict.SetStringKey("email", email_alpha);
-  dict.SetStringKey("gaia", gaia_alpha);
-  update->Append(std::move(dict));
+  base::Value::Dict dict;
+  dict.Set("account_id", email_alpha);
+  dict.Set("email", email_alpha);
+  dict.Set("gaia", gaia_alpha);
+  update_list.Append(std::move(dict));
 
   // This account is invalid because the account_id is a non-canonicalized
   // version of the email.
-  dict = base::Value(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("account_id", email_foobar);
-  dict.SetStringKey("email", email_foobar);
-  dict.SetStringKey("gaia", gaia_foobar);
-  update->Append(std::move(dict));
+  dict = base::Value::Dict();
+  dict.Set("account_id", email_foobar);
+  dict.Set("email", email_foobar);
+  dict.Set("gaia", gaia_foobar);
+  update_list.Append(std::move(dict));
 
   base::HistogramTester tester;
   ResetAccountTracker();
@@ -1756,20 +1762,21 @@
                 ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha))
                 .capabilities.can_offer_extended_chrome_sync_promos());
   ListPrefUpdate update(prefs(), prefs::kAccountInfo);
-  ASSERT_FALSE(update->GetListDeprecated().empty());
-  base::Value& dict = update->GetListDeprecated()[0];
-  ASSERT_TRUE(dict.is_dict());
+  base::Value::List& update_list = update->GetList();
+  ASSERT_FALSE(update_list.empty());
+  base::Value::Dict* dict = update_list[0].GetIfDict();
+  ASSERT_TRUE(dict);
   const char kDeprecatedCapabilityKey[] =
       "accountcapabilities.can_offer_extended_chrome_sync_promos";
   const char kNewCapabilityKey[] =
       "accountcapabilities.accountcapabilities/gi2tklldmfya";
   // The deprecated key is not set.
-  EXPECT_FALSE(dict.FindIntPath(kDeprecatedCapabilityKey));
-  EXPECT_TRUE(dict.FindIntPath(kNewCapabilityKey));
+  EXPECT_FALSE(dict->FindIntByDottedPath(kDeprecatedCapabilityKey));
+  EXPECT_TRUE(dict->FindIntByDottedPath(kNewCapabilityKey));
 
   // Set the capability using the deprecated key, and reload the account.
-  dict.SetIntPath(kDeprecatedCapabilityKey, 1);
-  dict.RemovePath(kNewCapabilityKey);
+  dict->SetByDottedPath(kDeprecatedCapabilityKey, 1);
+  dict->RemoveByDottedPath(kNewCapabilityKey);
   ClearAccountTrackerEvents();
   ResetAccountTrackerWithPersistence(scoped_user_data_dir.GetPath());
   EXPECT_TRUE(CheckAccountTrackerEvents(
@@ -1785,9 +1792,9 @@
   EXPECT_EQ(signin::Tribool::kTrue,
             infos[0].capabilities.can_offer_extended_chrome_sync_promos());
   // The deprecated key has been removed.
-  EXPECT_FALSE(dict.FindIntPath(kDeprecatedCapabilityKey));
+  EXPECT_FALSE(dict->FindIntByDottedPath(kDeprecatedCapabilityKey));
   // The new key has been written.
-  absl::optional<int> new_key = dict.FindIntPath(kNewCapabilityKey);
+  absl::optional<int> new_key = dict->FindIntByDottedPath(kNewCapabilityKey);
   ASSERT_TRUE(new_key.has_value());
   EXPECT_EQ(static_cast<int>(signin::Tribool::kTrue), new_key.value());
 }
diff --git a/components/user_education/common/feature_promo_controller.cc b/components/user_education/common/feature_promo_controller.cc
index 43ab3fe..7cfd984 100644
--- a/components/user_education/common/feature_promo_controller.cc
+++ b/components/user_education/common/feature_promo_controller.cc
@@ -329,7 +329,7 @@
       CHECK(spec.feature());
       create_params.buttons = CreateCustomActionButtons(
           *spec.feature(), spec.custom_action_caption(),
-          spec.custom_action_callback());
+          spec.custom_action_callback(), spec.custom_action_is_default());
       break;
     case FeaturePromoSpecification::PromoType::kUnspecifiied:
     case FeaturePromoSpecification::PromoType::kToast:
@@ -499,13 +499,14 @@
 FeaturePromoControllerCommon::CreateCustomActionButtons(
     const base::Feature& feature,
     const std::u16string& custom_action_caption,
-    FeaturePromoSpecification::CustomActionCallback custom_action_callback) {
+    FeaturePromoSpecification::CustomActionCallback custom_action_callback,
+    bool custom_action_is_default) {
   std::vector<HelpBubbleButtonParams> buttons;
   CHECK(!custom_action_callback.is_null());
 
   HelpBubbleButtonParams action_button;
   action_button.text = custom_action_caption;
-  action_button.is_default = false;
+  action_button.is_default = custom_action_is_default;
   action_button.callback =
       base::BindOnce(&FeaturePromoControllerCommon::OnCustomAction,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature),
@@ -514,7 +515,7 @@
 
   HelpBubbleButtonParams dismiss_button;
   dismiss_button.text = l10n_util::GetStringUTF16(IDS_PROMO_DISMISS_BUTTON);
-  dismiss_button.is_default = true;
+  dismiss_button.is_default = !custom_action_is_default;
   dismiss_button.callback = base::BindOnce(
       &FeaturePromoControllerCommon::OnHelpBubbleDismissed,
       weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature));
diff --git a/components/user_education/common/feature_promo_controller.h b/components/user_education/common/feature_promo_controller.h
index 24b4463..24ffa2a 100644
--- a/components/user_education/common/feature_promo_controller.h
+++ b/components/user_education/common/feature_promo_controller.h
@@ -319,7 +319,8 @@
   std::vector<HelpBubbleButtonParams> CreateCustomActionButtons(
       const base::Feature& feature,
       const std::u16string& custom_action_caption,
-      FeaturePromoSpecification::CustomActionCallback custom_action_callback);
+      FeaturePromoSpecification::CustomActionCallback custom_action_callback,
+      bool custom_action_is_default);
 
   // The feature promo registry to use.
   const raw_ptr<FeaturePromoRegistry> registry_;
diff --git a/components/user_education/common/feature_promo_specification.cc b/components/user_education/common/feature_promo_specification.cc
index 3f972363..9911132a 100644
--- a/components/user_education/common/feature_promo_specification.cc
+++ b/components/user_education/common/feature_promo_specification.cc
@@ -191,6 +191,13 @@
   return *this;
 }
 
+FeaturePromoSpecification& FeaturePromoSpecification::SetCustomActionIsDefault(
+    bool custom_action_is_default) {
+  DCHECK(!custom_action_callback_.is_null());
+  custom_action_is_default_ = custom_action_is_default;
+  return *this;
+}
+
 ui::TrackedElement* FeaturePromoSpecification::GetAnchorElement(
     ui::ElementContext context) const {
   auto* const element_tracker = ui::ElementTracker::GetElementTracker();
diff --git a/components/user_education/common/feature_promo_specification.h b/components/user_education/common/feature_promo_specification.h
index 08bddd5..3cebd37 100644
--- a/components/user_education/common/feature_promo_specification.h
+++ b/components/user_education/common/feature_promo_specification.h
@@ -70,7 +70,7 @@
     kSnooze,
     // A tutorial promo.
     kTutorial,
-    // A promo where the non-default button is replaced by a custom action.
+    // A promo where one button is replaced by a custom action.
     kCustomAction,
     // A simple promo that acts like a toast but without the required
     // accessibility data.
@@ -212,6 +212,13 @@
     return custom_action_caption_;
   }
 
+  // Sets whether the custom action button is the default button on the help
+  // bubble (default is false). It is an error to call this method for a promo
+  // not created with CreateForCustomAction().
+  FeaturePromoSpecification& SetCustomActionIsDefault(
+      bool custom_action_is_default);
+  bool custom_action_is_default() const { return custom_action_is_default_; }
+
   // Used to claim the callback when creating the bubble.
   CustomActionCallback custom_action_callback() const {
     return custom_action_callback_;
@@ -276,6 +283,9 @@
 
   // Custom action button action.
   CustomActionCallback custom_action_callback_;
+
+  // Whether the custom action is the default button.
+  bool custom_action_is_default_ = false;
 };
 
 }  // namespace user_education
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index f0605a4f..9d5c630 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -493,7 +493,7 @@
     ResourceId resource_id;
     resource_list_.push_back(CreateTestTexture(
         kTextureSize,
-        /*texel_color=*/SkColor4f{0.5f, 0.0f, 1.0f, 0.0f},
+        /*texel_color=*/SkColor4f{0.0f, 1.0f, 0.0f, 0.5f},
         /*premultiplied_alpha=*/false, child_resource_provider_.get(),
         child_context_provider_));
     resource_id = resource_list_.back().id;
@@ -538,7 +538,7 @@
       // A single tiled resource referenced by each TileDrawQuad
       resource_list_.push_back(CreateTestTexture(
           kTextureSize,
-          /*texel_color=*/SkColor4f{0.5f, 0.0f, 1.0f, 0.0f},
+          /*texel_color=*/SkColor4f{0.0f, 1.0f, 0.0f, 0.5f},
           /*premultiplied_alpha=*/false, child_resource_provider_.get(),
           child_context_provider_));
     } else {
@@ -546,7 +546,7 @@
       for (int i = 0; i < tile_count; ++i) {
         resource_list_.push_back(CreateTestTexture(
             kTextureSize,
-            /*texel_color=*/SkColor4f{0.5f, 0.0f, 1.0f, 0.0f},
+            /*texel_color=*/SkColor4f{0.0f, 1.0f, 0.0f, 0.5f},
             /*premultiplied_alpha=*/false, child_resource_provider_.get(),
             child_context_provider_));
       }
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 51d9222..efe4bd6 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -231,14 +231,14 @@
       premultiplied_alpha
           ? SkPreMultiplyColor(texel_color_one.toSkColor())
           : SkPackARGB32NoCheck(
-                255 * texel_color_one.fR, 255 * texel_color_one.fG,
-                255 * texel_color_one.fB, 255 * texel_color_one.fA);
+                255 * texel_color_one.fA, 255 * texel_color_one.fR,
+                255 * texel_color_one.fG, 255 * texel_color_one.fB);
   SkPMColor pixel_color_two =
       premultiplied_alpha
           ? SkPreMultiplyColor(texel_color_two.toSkColor())
           : SkPackARGB32NoCheck(
-                255 * texel_color_two.fR, 255 * texel_color_two.fG,
-                255 * texel_color_two.fB, 255 * texel_color_two.fA);
+                255 * texel_color_two.fA, 255 * texel_color_two.fR,
+                255 * texel_color_two.fG, 255 * texel_color_two.fB);
   // The default color is texel_color_one
   std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color_one);
   if (half_and_half) {
@@ -977,9 +977,9 @@
 
   CreateTestTextureDrawQuad(
       !is_software_renderer(), gfx::Rect(this->device_viewport_size_),
-      SkColor4f::FromColor(SkColorSetARGB(128, 0, 255, 0)),  // Texel color.
-      SkColors::kTransparent,  // Background color.
-      true,                    // Premultiplied alpha.
+      {0.0f, 1.0f, 0.0f, 0.5f},  // Texel color.
+      SkColors::kTransparent,    // Background color.
+      true,                      // Premultiplied alpha.
       shared_state, this->resource_provider_.get(),
       this->child_resource_provider_.get(), this->shared_bitmap_manager_.get(),
       this->child_context_provider_, pass.get());
@@ -1417,19 +1417,15 @@
 TEST_P(IntersectingQuadPixelTest, TexturedQuads) {
   this->SetupQuadStateAndRenderPass();
   CreateTestTwoColoredTextureDrawQuad(
-      !is_software_renderer(), this->quad_rect_,
-      SkColor4f::FromColor(SkColorSetARGB(255, 0, 0, 0)),
-      SkColor4f::FromColor(SkColorSetARGB(255, 0, 0, 255)),
-      SkColors::kTransparent, true /* premultiplied_alpha */,
+      !is_software_renderer(), this->quad_rect_, SkColors::kBlack,
+      SkColors::kBlue, SkColors::kTransparent, true /* premultiplied_alpha */,
       false /* flipped_texture_quad */, false /* half_and_half */,
       this->front_quad_state_, this->resource_provider_.get(),
       this->child_resource_provider_.get(), this->shared_bitmap_manager_.get(),
       this->child_context_provider_, this->render_pass_.get());
   CreateTestTwoColoredTextureDrawQuad(
-      !is_software_renderer(), this->quad_rect_,
-      SkColor4f::FromColor(SkColorSetARGB(255, 0, 255, 0)),
-      SkColor4f::FromColor(SkColorSetARGB(255, 0, 0, 0)),
-      SkColors::kTransparent, true /* premultiplied_alpha */,
+      !is_software_renderer(), this->quad_rect_, SkColors::kGreen,
+      SkColors::kBlack, SkColors::kTransparent, true /* premultiplied_alpha */,
       false /* flipped_texture_quad */, false /* half_and_half */,
       this->back_quad_state_, this->resource_provider_.get(),
       this->child_resource_provider_.get(), this->shared_bitmap_manager_.get(),
@@ -1659,9 +1655,9 @@
 
   CreateTestTextureDrawQuad(
       !is_software_renderer(), gfx::Rect(this->device_viewport_size_),
-      SkColor4f::FromColor(SkColorSetARGB(128, 0, 255, 0)),  // Texel color.
-      SkColors::kTransparent,  // Background color.
-      false,                   // Premultiplied alpha.
+      {0.0f, 1.0f, 0.0f, 0.5f},  // Texel color.
+      SkColors::kTransparent,    // Background color.
+      false,                     // Premultiplied alpha.
       shared_state, this->resource_provider_.get(),
       this->child_resource_provider_.get(), this->shared_bitmap_manager_.get(),
       this->child_context_provider_, pass.get());
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.cc b/content/browser/accessibility/accessibility_tree_formatter_android.cc
index 6735df17..b89fad72 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -105,9 +105,9 @@
       BrowserAccessibility::FromAXPlatformNodeDelegate(root);
 
   // XXX: Android formatter should walk native Android tree (not internal one).
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   RecursiveBuildTree(*root_internal, &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 base::Value AccessibilityTreeFormatterAndroid::BuildTreeForSelector(
@@ -119,9 +119,9 @@
 base::Value AccessibilityTreeFormatterAndroid::BuildNode(
     ui::AXPlatformNodeDelegate* node) const {
   CHECK(node);
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   AddProperties(*BrowserAccessibility::FromAXPlatformNodeDelegate(node), &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 void AccessibilityTreeFormatterAndroid::AddDefaultFilters(
@@ -135,7 +135,7 @@
 
 void AccessibilityTreeFormatterAndroid::RecursiveBuildTree(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ShouldDumpNode(node))
     return;
 
@@ -147,89 +147,86 @@
 
   for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
     BrowserAccessibility* child_node = node.PlatformGetChild(i);
-    std::unique_ptr<base::DictionaryValue> child_dict(
-        new base::DictionaryValue);
-    RecursiveBuildTree(*child_node, child_dict.get());
-    children.Append(base::Value::FromUniquePtrValue(std::move(child_dict)));
+    base::Value::Dict child_dict;
+    RecursiveBuildTree(*child_node, &child_dict);
+    children.Append(std::move(child_dict));
   }
-  dict->GetDict().Set(kChildrenDictAttr, std::move(children));
+  dict->Set(kChildrenDictAttr, std::move(children));
 }
 
 void AccessibilityTreeFormatterAndroid::AddProperties(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
-  dict->SetIntKey("id", node.GetId());
+    base::Value::Dict* dict) const {
+  dict->Set("id", node.GetId());
 
   const BrowserAccessibilityAndroid* android_node =
       static_cast<const BrowserAccessibilityAndroid*>(&node);
 
   // Class name.
-  dict->SetStringKey("class", android_node->GetClassName());
+  dict->Set("class", android_node->GetClassName());
 
   // Bool attributes.
-  dict->SetBoolKey("checkable", android_node->IsCheckable());
-  dict->SetBoolKey("checked", android_node->IsChecked());
-  dict->SetBoolKey("clickable", android_node->IsClickable());
-  dict->SetBoolKey("collapsed", android_node->IsCollapsed());
-  dict->SetBoolKey("collection", android_node->IsCollection());
-  dict->SetBoolKey("collection_item", android_node->IsCollectionItem());
-  dict->SetBoolKey("content_invalid", android_node->IsContentInvalid());
-  dict->SetBoolKey("disabled", !android_node->IsEnabled());
-  dict->SetBoolKey("editable_text", android_node->IsTextField());
-  dict->SetBoolKey("expanded", android_node->IsExpanded());
-  dict->SetBoolKey("focusable", android_node->IsFocusable());
-  dict->SetBoolKey("focused", android_node->IsFocused());
-  dict->SetBoolKey("has_character_locations",
-                   android_node->HasCharacterLocations());
-  dict->SetBoolKey("has_image", android_node->HasImage());
-  dict->SetBoolKey("has_non_empty_value", android_node->HasNonEmptyValue());
-  dict->SetBoolKey("heading", android_node->IsHeading());
-  dict->SetBoolKey("hierarchical", android_node->IsHierarchical());
-  dict->SetBoolKey("invisible", !android_node->IsVisibleToUser());
-  dict->SetBoolKey("link", ui::IsLink(android_node->GetRole()));
-  dict->SetBoolKey("multiline", android_node->IsMultiLine());
-  dict->SetBoolKey("multiselectable", android_node->IsMultiselectable());
-  dict->SetBoolKey("range", android_node->GetData().IsRangeValueSupported());
-  dict->SetBoolKey("password", android_node->IsPasswordField());
-  dict->SetBoolKey("scrollable", android_node->IsScrollable());
-  dict->SetBoolKey("selected", android_node->IsSelected());
-  dict->SetBoolKey("interesting", android_node->IsInterestingOnAndroid());
+  dict->Set("checkable", android_node->IsCheckable());
+  dict->Set("checked", android_node->IsChecked());
+  dict->Set("clickable", android_node->IsClickable());
+  dict->Set("collapsed", android_node->IsCollapsed());
+  dict->Set("collection", android_node->IsCollection());
+  dict->Set("collection_item", android_node->IsCollectionItem());
+  dict->Set("content_invalid", android_node->IsContentInvalid());
+  dict->Set("disabled", !android_node->IsEnabled());
+  dict->Set("editable_text", android_node->IsTextField());
+  dict->Set("expanded", android_node->IsExpanded());
+  dict->Set("focusable", android_node->IsFocusable());
+  dict->Set("focused", android_node->IsFocused());
+  dict->Set("has_character_locations", android_node->HasCharacterLocations());
+  dict->Set("has_image", android_node->HasImage());
+  dict->Set("has_non_empty_value", android_node->HasNonEmptyValue());
+  dict->Set("heading", android_node->IsHeading());
+  dict->Set("hierarchical", android_node->IsHierarchical());
+  dict->Set("invisible", !android_node->IsVisibleToUser());
+  dict->Set("link", ui::IsLink(android_node->GetRole()));
+  dict->Set("multiline", android_node->IsMultiLine());
+  dict->Set("multiselectable", android_node->IsMultiselectable());
+  dict->Set("range", android_node->GetData().IsRangeValueSupported());
+  dict->Set("password", android_node->IsPasswordField());
+  dict->Set("scrollable", android_node->IsScrollable());
+  dict->Set("selected", android_node->IsSelected());
+  dict->Set("interesting", android_node->IsInterestingOnAndroid());
 
   // String attributes.
-  dict->SetStringKey("name", android_node->GetTextContentUTF16());
-  dict->SetStringKey("hint", android_node->GetHint());
-  dict->SetStringKey("role_description", android_node->GetRoleDescription());
-  dict->SetStringKey("state_description", android_node->GetStateDescription());
+  dict->Set("name", android_node->GetTextContentUTF16());
+  dict->Set("hint", android_node->GetHint());
+  dict->Set("role_description", android_node->GetRoleDescription());
+  dict->Set("state_description", android_node->GetStateDescription());
 
   // Int attributes.
-  dict->SetIntKey("item_index", android_node->GetItemIndex());
-  dict->SetIntKey("item_count", android_node->GetItemCount());
-  dict->SetIntKey("row_count", android_node->RowCount());
-  dict->SetIntKey("column_count", android_node->ColumnCount());
-  dict->SetIntKey("row_index", android_node->RowIndex());
-  dict->SetIntKey("row_span", android_node->RowSpan());
-  dict->SetIntKey("column_index", android_node->ColumnIndex());
-  dict->SetIntKey("column_span", android_node->ColumnSpan());
-  dict->SetIntKey("input_type", android_node->AndroidInputType());
-  dict->SetIntKey("live_region_type", android_node->AndroidLiveRegionType());
-  dict->SetIntKey("range_min", static_cast<int>(android_node->RangeMin()));
-  dict->SetIntKey("range_max", static_cast<int>(android_node->RangeMax()));
-  dict->SetIntKey("range_current_value",
-                  static_cast<int>(android_node->RangeCurrentValue()));
-  dict->SetIntKey("text_change_added_count",
-                  android_node->GetTextChangeAddedCount());
-  dict->SetIntKey("text_change_removed_count",
-                  android_node->GetTextChangeRemovedCount());
+  dict->Set("item_index", android_node->GetItemIndex());
+  dict->Set("item_count", android_node->GetItemCount());
+  dict->Set("row_count", android_node->RowCount());
+  dict->Set("column_count", android_node->ColumnCount());
+  dict->Set("row_index", android_node->RowIndex());
+  dict->Set("row_span", android_node->RowSpan());
+  dict->Set("column_index", android_node->ColumnIndex());
+  dict->Set("column_span", android_node->ColumnSpan());
+  dict->Set("input_type", android_node->AndroidInputType());
+  dict->Set("live_region_type", android_node->AndroidLiveRegionType());
+  dict->Set("range_min", static_cast<int>(android_node->RangeMin()));
+  dict->Set("range_max", static_cast<int>(android_node->RangeMax()));
+  dict->Set("range_current_value",
+            static_cast<int>(android_node->RangeCurrentValue()));
+  dict->Set("text_change_added_count", android_node->GetTextChangeAddedCount());
+  dict->Set("text_change_removed_count",
+            android_node->GetTextChangeRemovedCount());
 
   // Actions.
-  dict->SetBoolKey("action_scroll_forward", android_node->CanScrollForward());
-  dict->SetBoolKey("action_scroll_backward", android_node->CanScrollBackward());
-  dict->SetBoolKey("action_scroll_up", android_node->CanScrollUp());
-  dict->SetBoolKey("action_scroll_down", android_node->CanScrollDown());
-  dict->SetBoolKey("action_scroll_left", android_node->CanScrollLeft());
-  dict->SetBoolKey("action_scroll_right", android_node->CanScrollRight());
-  dict->SetBoolKey("action_expand", android_node->IsCollapsed());
-  dict->SetBoolKey("action_collapse", android_node->IsExpanded());
+  dict->Set("action_scroll_forward", android_node->CanScrollForward());
+  dict->Set("action_scroll_backward", android_node->CanScrollBackward());
+  dict->Set("action_scroll_up", android_node->CanScrollUp());
+  dict->Set("action_scroll_down", android_node->CanScrollDown());
+  dict->Set("action_scroll_left", android_node->CanScrollLeft());
+  dict->Set("action_scroll_right", android_node->CanScrollRight());
+  dict->Set("action_expand", android_node->IsCollapsed());
+  dict->Set("action_collapse", android_node->IsExpanded());
 }
 
 std::string AccessibilityTreeFormatterAndroid::ProcessTreeForOutput(
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.h b/content/browser/accessibility/accessibility_tree_formatter_android.h
index 074b8c8..cc19e3c 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.h
@@ -30,10 +30,10 @@
 
  private:
   void RecursiveBuildTree(const BrowserAccessibility& node,
-                          base::DictionaryValue* dict) const;
+                          base::Value::Dict* dict) const;
 
   void AddProperties(const BrowserAccessibility& node,
-                     base::DictionaryValue* dict) const;
+                     base::Value::Dict* dict) const;
 
   std::string ProcessTreeForOutput(
       const base::DictionaryValue& node) const override;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android_external.cc b/content/browser/accessibility/accessibility_tree_formatter_android_external.cc
index dc4bfc51..7f67e36 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android_external.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android_external.cc
@@ -28,14 +28,14 @@
   BrowserAccessibility* root_internal =
       BrowserAccessibility::FromAXPlatformNodeDelegate(root);
 
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   RecursiveBuildTree(*root_internal, &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 void AccessibilityTreeFormatterAndroidExternal::RecursiveBuildTree(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   const BrowserAccessibilityAndroid* android_node =
       static_cast<const BrowserAccessibilityAndroid*>(&node);
 
@@ -44,22 +44,21 @@
   // TODO: It would be interesting to allow filtering here in the future.
   std::u16string str = android_node->GenerateAccessibilityNodeInfoString();
   if (str.empty()) {
-    dict->SetStringKey(kStringKey, kErrorMessage);
+    dict->Set(kStringKey, kErrorMessage);
     return;
   }
 
-  dict->SetStringKey(kStringKey, str);
+  dict->Set(kStringKey, str);
 
   base::Value::List children;
 
   for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
     BrowserAccessibility* child_node = node.PlatformGetChild(i);
-    std::unique_ptr<base::DictionaryValue> child_dict(
-        new base::DictionaryValue);
-    RecursiveBuildTree(*child_node, child_dict.get());
-    children.Append(base::Value::FromUniquePtrValue(std::move(child_dict)));
+    base::Value::Dict child_dict;
+    RecursiveBuildTree(*child_node, &child_dict);
+    children.Append(std::move(child_dict));
   }
-  dict->GetDict().Set(kChildrenDictAttr, std::move(children));
+  dict->Set(kChildrenDictAttr, std::move(children));
 }
 
 std::string AccessibilityTreeFormatterAndroidExternal::ProcessTreeForOutput(
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android_external.h b/content/browser/accessibility/accessibility_tree_formatter_android_external.h
index 671a6f0..e362d60 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android_external.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_android_external.h
@@ -22,7 +22,7 @@
 
  private:
   void RecursiveBuildTree(const BrowserAccessibility& node,
-                          base::DictionaryValue* dict) const;
+                          base::Value::Dict* dict) const;
 
   std::string ProcessTreeForOutput(
       const base::DictionaryValue& node) const override;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index 9a9d089..63197a7 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -61,9 +61,9 @@
     return base::Value(base::Value::Type::DICTIONARY);
   }
 
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   RecursiveBuildTree(node, &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 std::string AccessibilityTreeFormatterAuraLinux::EvaluateScript(
@@ -128,21 +128,21 @@
 
 base::Value AccessibilityTreeFormatterAuraLinux::BuildTree(
     ui::AXPlatformNodeDelegate* root) const {
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   RecursiveBuildTree(GetAtkObject(root), &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 base::Value AccessibilityTreeFormatterAuraLinux::BuildNode(
     ui::AXPlatformNodeDelegate* node) const {
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   AddProperties(GetAtkObject(node), &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 void AccessibilityTreeFormatterAuraLinux::RecursiveBuildTree(
     AtkObject* atk_node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   ui::AXPlatformNodeAuraLinux* platform_node =
       ui::AXPlatformNodeAuraLinux::FromAtkObject(atk_node);
   DCHECK(platform_node);
@@ -162,18 +162,17 @@
   if (child_count <= 0)
     return;
 
-  auto children = std::make_unique<base::ListValue>();
+  base::Value::List children;
   for (auto i = 0; i < child_count; i++) {
-    std::unique_ptr<base::DictionaryValue> child_dict(
-        new base::DictionaryValue);
+    base::Value::Dict child_dict;
 
     AtkObject* atk_child = atk_object_ref_accessible_child(atk_node, i);
     CHECK(atk_child);
 
-    RecursiveBuildTree(atk_child, child_dict.get());
+    RecursiveBuildTree(atk_child, &child_dict);
     g_object_unref(atk_child);
 
-    children->Append(base::Value::FromUniquePtrValue(std::move(child_dict)));
+    children.Append(std::move(child_dict));
   }
 
   dict->Set(kChildrenDictAttr, std::move(children));
@@ -181,7 +180,7 @@
 
 void AccessibilityTreeFormatterAuraLinux::RecursiveBuildTree(
     AtspiAccessible* node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   AddProperties(node, dict);
 
   GError* error = nullptr;
@@ -194,22 +193,21 @@
   if (child_count <= 0)
     return;
 
-  auto children = std::make_unique<base::ListValue>();
+  base::Value::List children;
   for (int i = 0; i < child_count; i++) {
-    std::unique_ptr<base::DictionaryValue> child_dict(
-        new base::DictionaryValue);
+    base::Value::Dict child_dict;
 
     AtspiAccessible* child =
         atspi_accessible_get_child_at_index(node, i, &error);
     if (error) {
-      child_dict->SetStringKey("error", "[Error retrieving child]");
+      child_dict.Set("error", "[Error retrieving child]");
       g_clear_error(&error);
       continue;
     }
 
     CHECK(child);
-    RecursiveBuildTree(child, child_dict.get());
-    children->Append(base::Value::FromUniquePtrValue(std::move(child_dict)));
+    RecursiveBuildTree(child, &child_dict);
+    children.Append(std::move(child_dict));
   }
 
   dict->Set(kChildrenDictAttr, std::move(children));
@@ -217,7 +215,7 @@
 
 void AccessibilityTreeFormatterAuraLinux::AddHypertextProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ATK_IS_TEXT(atk_object) || !ATK_IS_HYPERTEXT(atk_object))
     return;
 
@@ -226,7 +224,7 @@
   if (!character_text)
     return;
 
-  auto values = std::make_unique<base::ListValue>();
+  base::Value::List values;
 
   // Each link in the atk_text is represented by the multibyte unicode character
   // U+FFFC, which in UTF-8 is 0xEF 0xBF 0xBC. We will replace each instance of
@@ -261,7 +259,7 @@
     }
   }
 
-  values->Append(base::StringPrintf("hypertext='%s'", text.c_str()));
+  values.Append(base::StringPrintf("hypertext='%s'", text.c_str()));
   dict->Set("hypertext", std::move(values));
 
   g_free(character_text);
@@ -269,34 +267,33 @@
 
 void AccessibilityTreeFormatterAuraLinux::AddTextProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ATK_IS_TEXT(atk_object))
     return;
 
   AtkText* atk_text = ATK_TEXT(atk_object);
 
-  auto text_values = std::make_unique<base::ListValue>();
+  base::Value::List text_values;
   int character_count = atk_text_get_character_count(atk_text);
-  text_values->Append(
-      base::StringPrintf("character_count=%i", character_count));
+  text_values.Append(base::StringPrintf("character_count=%i", character_count));
 
   int caret_offset = atk_text_get_caret_offset(atk_text);
   if (caret_offset != -1)
-    text_values->Append(base::StringPrintf("caret_offset=%i", caret_offset));
+    text_values.Append(base::StringPrintf("caret_offset=%i", caret_offset));
 
   int selection_start, selection_end;
   char* selection_text =
       atk_text_get_selection(atk_text, 0, &selection_start, &selection_end);
   if (selection_text) {
     g_free(selection_text);
-    text_values->Append(
+    text_values.Append(
         base::StringPrintf("selection_start=%i", selection_start));
-    text_values->Append(base::StringPrintf("selection_end=%i", selection_end));
+    text_values.Append(base::StringPrintf("selection_end=%i", selection_end));
   }
 
   auto add_attribute_set_values = [](gpointer value, gpointer list) {
     const AtkAttribute* attribute = static_cast<const AtkAttribute*>(value);
-    static_cast<base::ListValue*>(list)->Append(
+    static_cast<base::Value::List*>(list)->Append(
         base::StringPrintf("%s=%s", attribute->name, attribute->value));
   };
 
@@ -304,9 +301,8 @@
   while (current_offset < character_count) {
     AtkAttributeSet* text_attributes = atk_text_get_run_attributes(
         atk_text, current_offset, &start_offset, &end_offset);
-    text_values->Append(base::StringPrintf("offset=%i", start_offset));
-    g_slist_foreach(text_attributes, add_attribute_set_values,
-                    text_values.get());
+    text_values.Append(base::StringPrintf("offset=%i", start_offset));
+    g_slist_foreach(text_attributes, add_attribute_set_values, &text_values);
     atk_attribute_set_free(text_attributes);
 
     current_offset = end_offset;
@@ -315,7 +311,7 @@
   gchar* character_text = atk_text_get_text(atk_text, 0, -1);
   if (character_text) {
     std::string text(character_text);
-    text_values->Append(base::StringPrintf("text='%s'", text.c_str()));
+    text_values.Append(base::StringPrintf("text='%s'", text.c_str()));
     g_free(character_text);
   }
 
@@ -324,7 +320,7 @@
 
 void AccessibilityTreeFormatterAuraLinux::AddActionProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ATK_IS_ACTION(atk_object))
     return;
 
@@ -333,53 +329,53 @@
   if (!action_count)
     return;
 
-  auto actions = std::make_unique<base::ListValue>();
+  base::Value::List actions;
   for (int i = 0; i < action_count; i++) {
     const char* name = atk_action_get_name(action, i);
-    actions->Append(name ? name : "");
+    actions.Append(name ? name : "");
   }
   dict->Set("actions", std::move(actions));
 }
 
 void AccessibilityTreeFormatterAuraLinux::AddValueProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ATK_IS_VALUE(atk_object))
     return;
 
-  auto value_properties = std::make_unique<base::ListValue>();
+  base::Value::List value_properties;
   AtkValue* value = ATK_VALUE(atk_object);
   GValue current = G_VALUE_INIT;
   g_value_init(&current, G_TYPE_FLOAT);
   atk_value_get_current_value(value, &current);
-  value_properties->Append(
+  value_properties.Append(
       base::StringPrintf("current=%f", g_value_get_float(&current)));
 
   GValue minimum = G_VALUE_INIT;
   g_value_init(&minimum, G_TYPE_FLOAT);
   atk_value_get_minimum_value(value, &minimum);
-  value_properties->Append(
+  value_properties.Append(
       base::StringPrintf("minimum=%f", g_value_get_float(&minimum)));
 
   GValue maximum = G_VALUE_INIT;
   g_value_init(&maximum, G_TYPE_FLOAT);
   atk_value_get_maximum_value(value, &maximum);
-  value_properties->Append(
+  value_properties.Append(
       base::StringPrintf("maximum=%f", g_value_get_float(&maximum)));
   dict->Set("value", std::move(value_properties));
 }
 
 void AccessibilityTreeFormatterAuraLinux::AddTableProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ATK_IS_TABLE(atk_object))
     return;
 
   // Column details.
   AtkTable* table = ATK_TABLE(atk_object);
   int n_cols = atk_table_get_n_columns(table);
-  auto table_properties = std::make_unique<base::ListValue>();
-  table_properties->Append(base::StringPrintf("cols=%i", n_cols));
+  base::Value::List table_properties;
+  table_properties.Append(base::StringPrintf("cols=%i", n_cols));
 
   std::vector<std::string> col_headers;
   for (int i = 0; i < n_cols; i++) {
@@ -391,12 +387,12 @@
   if (!col_headers.size())
     col_headers.push_back("NONE");
 
-  table_properties->Append(base::StringPrintf(
+  table_properties.Append(base::StringPrintf(
       "headers=(%s);", base::JoinString(col_headers, ", ").c_str()));
 
   // Row details.
   int n_rows = atk_table_get_n_rows(table);
-  table_properties->Append(base::StringPrintf("rows=%i", n_rows));
+  table_properties.Append(base::StringPrintf("rows=%i", n_rows));
 
   std::vector<std::string> row_headers;
   for (int i = 0; i < n_rows; i++) {
@@ -408,12 +404,12 @@
   if (!row_headers.size())
     row_headers.push_back("NONE");
 
-  table_properties->Append(base::StringPrintf(
+  table_properties.Append(base::StringPrintf(
       "headers=(%s);", base::JoinString(row_headers, ", ").c_str()));
 
   // Caption details.
   AtkObject* caption = atk_table_get_caption(table);
-  table_properties->Append(
+  table_properties.Append(
       base::StringPrintf("caption=%s;", caption ? "true" : "false"));
 
   // Summarize information about the cells from the table's perspective here.
@@ -431,7 +427,7 @@
   if (!span_info.size())
     span_info.push_back("all: 1x1");
 
-  table_properties->Append(base::StringPrintf(
+  table_properties.Append(base::StringPrintf(
       "spans=(%s)", base::JoinString(span_info, ", ").c_str()));
   dict->Set("table", std::move(table_properties));
 }
@@ -439,7 +435,7 @@
 void AccessibilityTreeFormatterAuraLinux::AddTableCellProperties(
     const ui::AXPlatformNodeAuraLinux* node,
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   AtkRole role = atk_object_get_role(atk_object);
   if (role != ATK_ROLE_TABLE_CELL && role != ATK_ROLE_COLUMN_HEADER &&
       role != ATK_ROLE_ROW_HEADER) {
@@ -486,15 +482,15 @@
   cell_info.push_back(base::StringPrintf("n_row_headers=%i", n_row_headers));
   cell_info.push_back(base::StringPrintf("n_col_headers=%i", n_column_headers));
 
-  auto cell_properties = std::make_unique<base::ListValue>();
-  cell_properties->Append(
+  base::Value::List cell_properties;
+  cell_properties.Append(
       base::StringPrintf("(%s)", base::JoinString(cell_info, ", ").c_str()));
   dict->Set("cell", std::move(cell_properties));
 }
 
 void AccessibilityTreeFormatterAuraLinux::AddProperties(
     AtkObject* atk_object,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   ui::AXPlatformNodeAuraLinux* platform_node =
       ui::AXPlatformNodeAuraLinux::FromAtkObject(atk_object);
   DCHECK(platform_node);
@@ -503,36 +499,36 @@
       platform_node->GetDelegate());
   DCHECK(node);
 
-  dict->SetIntKey("id", node->GetId());
+  dict->Set("id", node->GetId());
 
   AtkRole role = atk_object_get_role(atk_object);
   if (role != ATK_ROLE_UNKNOWN) {
-    dict->SetStringKey("role", AtkRoleToString(role));
+    dict->Set("role", AtkRoleToString(role));
   }
 
   const gchar* name = atk_object_get_name(atk_object);
   if (name)
-    dict->SetStringKey("name", std::string(name));
+    dict->Set("name", std::string(name));
   const gchar* description = atk_object_get_description(atk_object);
   if (description)
-    dict->SetStringKey("description", std::string(description));
+    dict->Set("description", std::string(description));
 
   AtkStateSet* state_set = atk_object_ref_state_set(atk_object);
-  auto states = std::make_unique<base::ListValue>();
+  base::Value::List states;
   for (int i = ATK_STATE_INVALID; i < ATK_STATE_LAST_DEFINED; i++) {
     AtkStateType state_type = static_cast<AtkStateType>(i);
     if (atk_state_set_contains_state(state_set, state_type))
-      states->Append(atk_state_type_get_name(state_type));
+      states.Append(atk_state_type_get_name(state_type));
   }
   dict->Set("states", std::move(states));
   g_object_unref(state_set);
 
   AtkRelationSet* relation_set = atk_object_ref_relation_set(atk_object);
-  auto relations = std::make_unique<base::ListValue>();
+  base::Value::List relations;
   for (int i = ATK_RELATION_NULL; i < ATK_RELATION_LAST_DEFINED; i++) {
     AtkRelationType relation_type = static_cast<AtkRelationType>(i);
     if (atk_relation_set_contains(relation_set, relation_type))
-      relations->Append(atk_relation_type_get_name(relation_type));
+      relations.Append(atk_relation_type_get_name(relation_type));
   }
   dict->Set("relations", std::move(relations));
   g_object_unref(relation_set);
@@ -540,8 +536,8 @@
   AtkAttributeSet* attributes = atk_object_get_attributes(atk_object);
   for (AtkAttributeSet* attr = attributes; attr; attr = attr->next) {
     AtkAttribute* attribute = static_cast<AtkAttribute*>(attr->data);
-    dict->SetStringPath(std::string(kObjectAttributePrefix) + attribute->name,
-                        attribute->value);
+    dict->SetByDottedPath(std::string(kObjectAttributePrefix) + attribute->name,
+                          attribute->value);
   }
   atk_attribute_set_free(attributes);
 
@@ -555,24 +551,24 @@
 
 void AccessibilityTreeFormatterAuraLinux::AddProperties(
     AtspiAccessible* node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   GError* error = nullptr;
   char* role_name = atspi_accessible_get_role_name(node, &error);
   if (!error)
-    dict->SetStringKey("role", role_name);
+    dict->Set("role", role_name);
   g_clear_error(&error);
   free(role_name);
 
   char* name = atspi_accessible_get_name(node, &error);
   if (!error)
-    dict->SetStringKey("name", name);
+    dict->Set("name", name);
   g_clear_error(&error);
   free(name);
 
   error = nullptr;
   char* description = atspi_accessible_get_description(node, &error);
   if (!error)
-    dict->SetStringKey("description", description);
+    dict->Set("description", description);
   g_clear_error(&error);
   free(description);
 
@@ -585,7 +581,7 @@
 
     g_hash_table_iter_init(&i, attributes);
     while (g_hash_table_iter_next(&i, &key, &value)) {
-      dict->SetStringPath(static_cast<char*>(key), static_cast<char*>(value));
+      dict->SetByDottedPath(static_cast<char*>(key), static_cast<char*>(value));
     }
   }
   g_clear_error(&error);
@@ -593,10 +589,10 @@
 
   AtspiStateSet* atspi_states = atspi_accessible_get_state_set(node);
   GArray* state_array = atspi_state_set_get_states(atspi_states);
-  auto states = std::make_unique<base::ListValue>();
+  base::Value::List states;
   for (unsigned i = 0; i < state_array->len; i++) {
     AtspiStateType state_type = g_array_index(state_array, AtspiStateType, i);
-    states->Append(ATSPIStateToString(state_type));
+    states.Append(ATSPIStateToString(state_type));
   }
   dict->Set("states", std::move(states));
   g_array_free(state_array, TRUE);
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.h b/content/browser/accessibility/accessibility_tree_formatter_auralinux.h
index 74463cb..8384285 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.h
@@ -37,26 +37,22 @@
       const AXTreeSelector& selector,
       const ui::AXInspectScenario& scenario) const override;
 
-  void RecursiveBuildTree(AtspiAccessible* node,
-                          base::DictionaryValue* dict) const;
-  void RecursiveBuildTree(AtkObject*, base::DictionaryValue*) const;
+  void RecursiveBuildTree(AtspiAccessible* node, base::Value::Dict* dict) const;
+  void RecursiveBuildTree(AtkObject*, base::Value::Dict*) const;
 
-  void AddProperties(AtkObject*, base::DictionaryValue*) const;
-  void AddProperties(AtspiAccessible*, base::DictionaryValue*) const;
+  void AddProperties(AtkObject*, base::Value::Dict*) const;
+  void AddProperties(AtspiAccessible*, base::Value::Dict*) const;
 
-  void AddTextProperties(AtkObject* atk_object,
-                         base::DictionaryValue* dict) const;
+  void AddTextProperties(AtkObject* atk_object, base::Value::Dict* dict) const;
   void AddHypertextProperties(AtkObject* atk_object,
-                              base::DictionaryValue* dict) const;
+                              base::Value::Dict* dict) const;
   void AddActionProperties(AtkObject* atk_object,
-                           base::DictionaryValue* dict) const;
-  void AddValueProperties(AtkObject* atk_object,
-                          base::DictionaryValue* dict) const;
-  void AddTableProperties(AtkObject* atk_object,
-                          base::DictionaryValue* dict) const;
+                           base::Value::Dict* dict) const;
+  void AddValueProperties(AtkObject* atk_object, base::Value::Dict* dict) const;
+  void AddTableProperties(AtkObject* atk_object, base::Value::Dict* dict) const;
   void AddTableCellProperties(const ui::AXPlatformNodeAuraLinux* node,
                               AtkObject* atk_object,
-                              base::DictionaryValue* dict) const;
+                              base::Value::Dict* dict) const;
 };
 
 }  // namespace content
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 1b7f1f4..f4b797cb 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -240,9 +240,9 @@
 
   BrowserAccessibility* root_internal =
       BrowserAccessibility::FromAXPlatformNodeDelegate(root);
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   RecursiveBuildTree(*root_internal, &dict);
-  return dict;
+  return base::Value(std::move(dict));
 }
 
 base::Value AccessibilityTreeFormatterBlink::BuildTreeForSelector(
@@ -254,17 +254,17 @@
 base::Value AccessibilityTreeFormatterBlink::BuildTreeForNode(
     ui::AXNode* node) const {
   CHECK(node);
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   RecursiveBuildTree(*node, &dict);
-  return dict;
+  return base::Value(std::move(dict));
 }
 
 base::Value AccessibilityTreeFormatterBlink::BuildNode(
     ui::AXPlatformNodeDelegate* node) const {
   CHECK(node);
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   AddProperties(*BrowserAccessibility::FromAXPlatformNodeDelegate(node), &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 std::string AccessibilityTreeFormatterBlink::DumpInternalAccessibilityTree(
@@ -279,80 +279,80 @@
 
 void AccessibilityTreeFormatterBlink::RecursiveBuildTree(
     const BrowserAccessibility& node,
-    base::Value* dict) const {
+    base::Value::Dict* dict) const {
   if (!ShouldDumpNode(node))
     return;
 
-  AddProperties(node, static_cast<base::DictionaryValue*>(dict));
+  AddProperties(node, dict);
   if (!ShouldDumpChildren(node))
     return;
 
   base::Value::List children;
   for (const auto* child_node : node.AllChildren()) {
-    base::Value child_dict(base::Value::Type::DICTIONARY);
+    base::Value::Dict child_dict;
     RecursiveBuildTree(*child_node, &child_dict);
     children.Append(std::move(child_dict));
   }
-  dict->SetKey(kChildrenDictAttr, base::Value(std::move(children)));
+  dict->Set(kChildrenDictAttr, base::Value(std::move(children)));
 }
 
 void AccessibilityTreeFormatterBlink::RecursiveBuildTree(
     const ui::AXNode& node,
-    base::Value* dict) const {
-  AddProperties(node, static_cast<base::DictionaryValue*>(dict));
+    base::Value::Dict* dict) const {
+  AddProperties(node, dict);
 
   base::Value::List children;
   for (ui::AXNode* child_node : node.children()) {
-    base::Value child_dict(base::Value::Type::DICTIONARY);
+    base::Value::Dict child_dict;
     RecursiveBuildTree(*child_node, &child_dict);
     children.Append(std::move(child_dict));
   }
-  dict->SetKey(kChildrenDictAttr, base::Value(std::move(children)));
+  dict->Set(kChildrenDictAttr, base::Value(std::move(children)));
 }
 
 void AccessibilityTreeFormatterBlink::AddProperties(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   int id = node.GetId();
-  dict->SetIntKey("id", id);
+  dict->Set("id", id);
 
-  dict->SetStringKey("internalRole", ui::ToString(node.GetRole()));
+  dict->Set("internalRole", ui::ToString(node.GetRole()));
 
   gfx::Rect bounds =
       gfx::ToEnclosingRect(node.GetData().relative_bounds.bounds);
-  dict->SetIntKey("boundsX", bounds.x());
-  dict->SetIntKey("boundsY", bounds.y());
-  dict->SetIntKey("boundsWidth", bounds.width());
-  dict->SetIntKey("boundsHeight", bounds.height());
+  dict->Set("boundsX", bounds.x());
+  dict->Set("boundsY", bounds.y());
+  dict->Set("boundsWidth", bounds.width());
+  dict->Set("boundsHeight", bounds.height());
 
   ui::AXOffscreenResult offscreen_result = ui::AXOffscreenResult::kOnscreen;
   gfx::Rect page_bounds = node.GetClippedRootFrameBoundsRect(&offscreen_result);
-  dict->SetIntKey("pageBoundsX", page_bounds.x());
-  dict->SetIntKey("pageBoundsY", page_bounds.y());
-  dict->SetIntKey("pageBoundsWidth", page_bounds.width());
-  dict->SetIntKey("pageBoundsHeight", page_bounds.height());
+  dict->Set("pageBoundsX", page_bounds.x());
+  dict->Set("pageBoundsY", page_bounds.y());
+  dict->Set("pageBoundsWidth", page_bounds.width());
+  dict->Set("pageBoundsHeight", page_bounds.height());
 
-  dict->SetBoolKey("transform",
-                   node.GetData().relative_bounds.transform &&
-                       !node.GetData().relative_bounds.transform->IsIdentity());
+  dict->Set("transform",
+            node.GetData().relative_bounds.transform &&
+                !node.GetData().relative_bounds.transform->IsIdentity());
 
   gfx::Rect unclipped_bounds =
       node.GetUnclippedRootFrameBoundsRect(&offscreen_result);
-  dict->SetIntKey("unclippedBoundsX", unclipped_bounds.x());
-  dict->SetIntKey("unclippedBoundsY", unclipped_bounds.y());
-  dict->SetIntKey("unclippedBoundsWidth", unclipped_bounds.width());
-  dict->SetIntKey("unclippedBoundsHeight", unclipped_bounds.height());
+  dict->Set("unclippedBoundsX", unclipped_bounds.x());
+  dict->Set("unclippedBoundsY", unclipped_bounds.y());
+  dict->Set("unclippedBoundsWidth", unclipped_bounds.width());
+  dict->Set("unclippedBoundsHeight", unclipped_bounds.height());
 
   for (int32_t state_index = static_cast<int32_t>(ax::mojom::State::kNone);
        state_index <= static_cast<int32_t>(ax::mojom::State::kMaxValue);
        ++state_index) {
     auto state = static_cast<ax::mojom::State>(state_index);
     if (node.HasState(state))
-      dict->SetBoolPath(ui::ToString(state), true);
+      dict->SetByDottedPath(ui::ToString(state), true);
   }
 
   if (offscreen_result == ui::AXOffscreenResult::kOffscreen)
-    dict->SetBoolKey(STATE_OFFSCREEN, true);
+    dict->Set(STATE_OFFSCREEN, true);
 
   for (int32_t attr_index =
            static_cast<int32_t>(ax::mojom::StringAttribute::kNone);
@@ -362,7 +362,7 @@
     auto attr = static_cast<ax::mojom::StringAttribute>(attr_index);
     auto maybe_value = GetStringAttribute(*node.node(), attr);
     if (maybe_value.has_value())
-      dict->SetStringPath(ui::ToString(attr), maybe_value.value());
+      dict->SetByDottedPath(ui::ToString(attr), maybe_value.value());
   }
 
   for (int32_t attr_index =
@@ -372,7 +372,7 @@
     auto attr = static_cast<ax::mojom::IntAttribute>(attr_index);
     auto maybe_value = ui::ComputeAttribute(&node, attr);
     if (maybe_value.has_value()) {
-      dict->SetStringPath(
+      dict->SetByDottedPath(
           ui::ToString(attr),
           IntAttrToString(*node.node(), attr, maybe_value.value()));
     }
@@ -385,7 +385,7 @@
     auto attr = static_cast<ax::mojom::FloatAttribute>(attr_index);
     if (node.HasFloatAttribute(attr) &&
         std::isfinite(node.GetFloatAttribute(attr)))
-      dict->SetDoublePath(ui::ToString(attr), node.GetFloatAttribute(attr));
+      dict->SetByDottedPath(ui::ToString(attr), node.GetFloatAttribute(attr));
   }
 
   for (int32_t attr_index =
@@ -394,7 +394,7 @@
        ++attr_index) {
     auto attr = static_cast<ax::mojom::BoolAttribute>(attr_index);
     if (node.HasBoolAttribute(attr))
-      dict->SetBoolPath(ui::ToString(attr), node.GetBoolAttribute(attr));
+      dict->SetByDottedPath(ui::ToString(attr), node.GetBoolAttribute(attr));
   }
 
   for (int32_t attr_index =
@@ -418,7 +418,7 @@
           value_list.Append(values[i]);
         }
       }
-      dict->SetKey(ui::ToString(attr), std::move(value_list));
+      dict->Set(ui::ToString(attr), std::move(value_list));
     }
   }
 
@@ -428,12 +428,12 @@
   int anchor_id = unignored_selection.anchor_object_id;
   if (id == anchor_id) {
     int anchor_offset = unignored_selection.anchor_offset;
-    dict->SetIntPath("TreeData.textSelStartOffset", anchor_offset);
+    dict->SetByDottedPath("TreeData.textSelStartOffset", anchor_offset);
   }
   int focus_id = unignored_selection.focus_object_id;
   if (id == focus_id) {
     int focus_offset = unignored_selection.focus_offset;
-    dict->SetIntPath("TreeData.textSelEndOffset", focus_offset);
+    dict->SetByDottedPath("TreeData.textSelEndOffset", focus_offset);
   }
 
   std::vector<std::string> actions_strings;
@@ -446,21 +446,21 @@
       actions_strings.push_back(ui::ToString(action));
   }
   if (!actions_strings.empty())
-    dict->SetStringKey("actions", base::JoinString(actions_strings, ","));
+    dict->Set("actions", base::JoinString(actions_strings, ","));
 }
 
 void AccessibilityTreeFormatterBlink::AddProperties(
     const ui::AXNode& node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   int id = node.id();
-  dict->SetIntKey("id", id);
-  dict->SetStringKey("internalRole", ui::ToString(node.GetRole()));
+  dict->Set("id", id);
+  dict->Set("internalRole", ui::ToString(node.GetRole()));
 
   gfx::Rect bounds = gfx::ToEnclosingRect(node.data().relative_bounds.bounds);
-  dict->SetIntKey("boundsX", bounds.x());
-  dict->SetIntKey("boundsY", bounds.y());
-  dict->SetIntKey("boundsWidth", bounds.width());
-  dict->SetIntKey("boundsHeight", bounds.height());
+  dict->Set("boundsX", bounds.x());
+  dict->Set("boundsY", bounds.y());
+  dict->Set("boundsWidth", bounds.width());
+  dict->Set("boundsHeight", bounds.height());
 
   // TODO(kschmi): Add support for the following (potentially via AXTree):
   //  GetClippedRootFrameBoundsRect
@@ -490,16 +490,16 @@
   //    SetSize
   //    SetSize
 
-  dict->SetBoolKey("transform",
-                   node.data().relative_bounds.transform &&
-                       !node.data().relative_bounds.transform->IsIdentity());
+  dict->Set("transform",
+            node.data().relative_bounds.transform &&
+                !node.data().relative_bounds.transform->IsIdentity());
 
   for (int32_t state_index = static_cast<int32_t>(ax::mojom::State::kNone);
        state_index <= static_cast<int32_t>(ax::mojom::State::kMaxValue);
        ++state_index) {
     auto state = static_cast<ax::mojom::State>(state_index);
     if (node.HasState(state))
-      dict->SetBoolPath(ui::ToString(state), true);
+      dict->SetByDottedPath(ui::ToString(state), true);
   }
 
   for (int32_t attr_index =
@@ -510,7 +510,7 @@
     auto attr = static_cast<ax::mojom::StringAttribute>(attr_index);
     auto maybe_value = GetStringAttribute(node, attr);
     if (maybe_value.has_value())
-      dict->SetStringPath(ui::ToString(attr), maybe_value.value());
+      dict->SetByDottedPath(ui::ToString(attr), maybe_value.value());
   }
 
   for (int32_t attr_index =
@@ -520,8 +520,8 @@
     auto attr = static_cast<ax::mojom::IntAttribute>(attr_index);
     int32_t value;
     if (node.GetIntAttribute(attr, &value)) {
-      dict->SetStringPath(ui::ToString(attr),
-                          IntAttrToString(node, attr, value));
+      dict->SetByDottedPath(ui::ToString(attr),
+                            IntAttrToString(node, attr, value));
     }
   }
 
@@ -532,7 +532,7 @@
     auto attr = static_cast<ax::mojom::FloatAttribute>(attr_index);
     if (node.HasFloatAttribute(attr) &&
         std::isfinite(node.GetFloatAttribute(attr)))
-      dict->SetDoublePath(ui::ToString(attr), node.GetFloatAttribute(attr));
+      dict->SetByDottedPath(ui::ToString(attr), node.GetFloatAttribute(attr));
   }
 
   for (int32_t attr_index =
@@ -541,7 +541,7 @@
        ++attr_index) {
     auto attr = static_cast<ax::mojom::BoolAttribute>(attr_index);
     if (node.HasBoolAttribute(attr))
-      dict->SetBoolPath(ui::ToString(attr), node.GetBoolAttribute(attr));
+      dict->SetByDottedPath(ui::ToString(attr), node.GetBoolAttribute(attr));
   }
 
   for (int32_t attr_index =
@@ -568,7 +568,7 @@
           value_list.Append(value);
         }
       }
-      dict->SetKey(ui::ToString(attr), std::move(value_list));
+      dict->Set(ui::ToString(attr), std::move(value_list));
     }
   }
 
@@ -578,12 +578,12 @@
   int anchor_id = unignored_selection.anchor_object_id;
   if (id == anchor_id) {
     int anchor_offset = unignored_selection.anchor_offset;
-    dict->SetIntPath("TreeData.textSelStartOffset", anchor_offset);
+    dict->SetByDottedPath("TreeData.textSelStartOffset", anchor_offset);
   }
   int focus_id = unignored_selection.focus_object_id;
   if (id == focus_id) {
     int focus_offset = unignored_selection.focus_offset;
-    dict->SetIntPath("TreeData.textSelEndOffset", focus_offset);
+    dict->SetByDottedPath("TreeData.textSelEndOffset", focus_offset);
   }
 
   std::vector<std::string> actions_strings;
@@ -596,7 +596,7 @@
       actions_strings.push_back(ui::ToString(action));
   }
   if (!actions_strings.empty())
-    dict->SetStringKey("actions", base::JoinString(actions_strings, ","));
+    dict->Set("actions", base::JoinString(actions_strings, ","));
 }
 
 std::string AccessibilityTreeFormatterBlink::ProcessTreeForOutput(
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.h b/content/browser/accessibility/accessibility_tree_formatter_blink.h
index 4a2b20979..bed21e5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.h
@@ -35,16 +35,17 @@
 
  private:
   void RecursiveBuildTree(const BrowserAccessibility& node,
-                          base::Value* dict) const;
+                          base::Value::Dict* dict) const;
 
-  void RecursiveBuildTree(const ui::AXNode& node, base::Value* dict) const;
+  void RecursiveBuildTree(const ui::AXNode& node,
+                          base::Value::Dict* dict) const;
 
   base::Value BuildNode(ui::AXPlatformNodeDelegate* node) const override;
 
   void AddProperties(const BrowserAccessibility& node,
-                     base::DictionaryValue* dict) const;
+                     base::Value::Dict* dict) const;
 
-  void AddProperties(const ui::AXNode& node, base::DictionaryValue* dict) const;
+  void AddProperties(const ui::AXNode& node, base::Value::Dict* dict) const;
 
   std::string ProcessTreeForOutput(
       const base::DictionaryValue& node) const override;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
index c5d5bd83..851ec96 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
@@ -209,14 +209,14 @@
   BrowserAccessibility* root_internal =
       BrowserAccessibility::FromAXPlatformNodeDelegate(root);
 
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   RecursiveBuildTree(*root_internal, &dict);
-  return dict;
+  return base::Value(std::move(dict));
 }
 
 void AccessibilityTreeFormatterFuchsia::RecursiveBuildTree(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
+    base::Value::Dict* dict) const {
   if (!ShouldDumpNode(node))
     return;
 
@@ -238,26 +238,25 @@
     BrowserAccessibilityFuchsia* child_browser_accessibility =
         static_cast<BrowserAccessibilityFuchsia*>(child_node->GetDelegate());
 
-    std::unique_ptr<base::DictionaryValue> child_dict(
-        new base::DictionaryValue);
-    RecursiveBuildTree(*child_browser_accessibility, child_dict.get());
-    children.Append(base::Value::FromUniquePtrValue(std::move(child_dict)));
+    base::Value::Dict child_dict;
+    RecursiveBuildTree(*child_browser_accessibility, &child_dict);
+    children.Append(std::move(child_dict));
   }
-  dict->GetDict().Set(kChildrenDictAttr, std::move(children));
+  dict->Set(kChildrenDictAttr, std::move(children));
 }
 
 base::Value AccessibilityTreeFormatterFuchsia::BuildNode(
     ui::AXPlatformNodeDelegate* node) const {
   CHECK(node);
-  base::DictionaryValue dict;
+  base::Value::Dict dict;
   AddProperties(*BrowserAccessibility::FromAXPlatformNodeDelegate(node), &dict);
-  return std::move(dict);
+  return base::Value(std::move(dict));
 }
 
 void AccessibilityTreeFormatterFuchsia::AddProperties(
     const BrowserAccessibility& node,
-    base::DictionaryValue* dict) const {
-  dict->SetIntKey("id", node.GetId());
+    base::Value::Dict* dict) const {
+  dict->Set("id", node.GetId());
 
   const BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
       static_cast<const BrowserAccessibilityFuchsia*>(&node);
@@ -268,44 +267,45 @@
       browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
   // Add fuchsia node attributes.
-  dict->SetStringKey("role", FuchsiaRoleToString(fuchsia_node.role()));
+  dict->Set("role", FuchsiaRoleToString(fuchsia_node.role()));
 
-  dict->SetStringKey("actions", FuchsiaActionsToString(fuchsia_node.actions()));
+  dict->Set("actions", FuchsiaActionsToString(fuchsia_node.actions()));
 
   if (fuchsia_node.has_attributes()) {
     const fuchsia::accessibility::semantics::Attributes& attributes =
         fuchsia_node.attributes();
 
     if (attributes.has_label() && !attributes.label().empty())
-      dict->SetStringKey("label", attributes.label());
+      dict->Set("label", attributes.label());
 
     if (attributes.has_secondary_label() &&
         !attributes.secondary_label().empty()) {
-      dict->SetStringKey("secondary_label", attributes.secondary_label());
+      dict->Set("secondary_label", attributes.secondary_label());
     }
 
     if (attributes.has_range()) {
       const auto& range_attributes = attributes.range();
 
       if (range_attributes.has_min_value())
-        dict->SetDoubleKey("min_value", range_attributes.min_value());
+        dict->Set("min_value", range_attributes.min_value());
 
       if (range_attributes.has_max_value())
-        dict->SetDoubleKey("max_value", range_attributes.max_value());
+        dict->Set("max_value", range_attributes.max_value());
 
       if (range_attributes.has_step_delta())
-        dict->SetDoubleKey("step_delta", range_attributes.step_delta());
+        dict->Set("step_delta", range_attributes.step_delta());
     }
 
     if (attributes.has_table_attributes()) {
       const auto& table_attributes = attributes.table_attributes();
 
       if (table_attributes.has_number_of_rows())
-        dict->SetIntKey("number_of_rows", table_attributes.number_of_rows());
+        dict->Set("number_of_rows",
+                  static_cast<int>(table_attributes.number_of_rows()));
 
       if (table_attributes.has_number_of_columns()) {
-        dict->SetIntKey("number_of_columns",
-                        table_attributes.number_of_columns());
+        dict->Set("number_of_columns",
+                  static_cast<int>(table_attributes.number_of_columns()));
       }
     }
 
@@ -313,40 +313,44 @@
       const auto& table_row_attributes = attributes.table_row_attributes();
 
       if (table_row_attributes.has_row_index())
-        dict->SetIntKey("row_index", table_row_attributes.row_index());
+        dict->Set("row_index",
+                  static_cast<int>(table_row_attributes.row_index()));
     }
 
     if (attributes.has_table_cell_attributes()) {
       const auto& table_cell_attributes = attributes.table_cell_attributes();
 
       if (table_cell_attributes.has_row_index())
-        dict->SetIntKey("cell_row_index", table_cell_attributes.row_index());
+        dict->Set("cell_row_index",
+                  static_cast<int>(table_cell_attributes.row_index()));
 
       if (table_cell_attributes.has_column_index()) {
-        dict->SetIntKey("cell_column_index",
-                        table_cell_attributes.column_index());
+        dict->Set("cell_column_index",
+                  static_cast<int>(table_cell_attributes.column_index()));
       }
 
       if (table_cell_attributes.has_row_span())
-        dict->SetIntKey("cell_row_span", table_cell_attributes.row_span());
+        dict->Set("cell_row_span",
+                  static_cast<int>(table_cell_attributes.row_span()));
 
       if (table_cell_attributes.has_column_span()) {
-        dict->SetIntKey("cell_column_span",
-                        table_cell_attributes.column_span());
+        dict->Set("cell_column_span",
+                  static_cast<int>(table_cell_attributes.column_span()));
       }
     }
 
     if (attributes.has_list_attributes()) {
-      dict->SetIntKey("list_size", attributes.list_attributes().size());
+      dict->Set("list_size",
+                static_cast<int>(attributes.list_attributes().size()));
     }
 
     if (attributes.has_list_element_attributes()) {
-      dict->SetIntKey("list_element_index",
-                      attributes.list_element_attributes().index());
+      dict->Set("list_element_index",
+                static_cast<int>(attributes.list_element_attributes().index()));
     }
 
     if (attributes.has_is_keyboard_key())
-      dict->SetBoolKey("is_keyboard_key", attributes.is_keyboard_key());
+      dict->Set("is_keyboard_key", attributes.is_keyboard_key());
   }
 
   if (fuchsia_node.has_states()) {
@@ -354,42 +358,40 @@
         fuchsia_node.states();
 
     if (states.has_selected())
-      dict->SetBoolKey("selected", states.selected());
+      dict->Set("selected", states.selected());
 
     if (states.has_checked_state()) {
-      dict->SetStringKey("checked_state",
-                         CheckedStateToString(states.checked_state()));
+      dict->Set("checked_state", CheckedStateToString(states.checked_state()));
     }
 
     if (states.has_hidden())
-      dict->SetBoolKey("hidden", states.hidden());
+      dict->Set("hidden", states.hidden());
 
     if (states.has_value() && !states.value().empty())
-      dict->SetStringKey("value", states.value());
+      dict->Set("value", states.value());
 
     if (states.has_viewport_offset()) {
-      dict->SetStringKey("viewport_offset",
-                         ViewportOffsetToString(states.viewport_offset()));
+      dict->Set("viewport_offset",
+                ViewportOffsetToString(states.viewport_offset()));
     }
 
     if (states.has_toggled_state()) {
-      dict->SetStringKey("toggled_state",
-                         ToggledStateToString(states.toggled_state()));
+      dict->Set("toggled_state", ToggledStateToString(states.toggled_state()));
     }
 
     if (states.has_focusable())
-      dict->SetBoolKey("focusable", states.focusable());
+      dict->Set("focusable", states.focusable());
 
     if (states.has_has_input_focus())
-      dict->SetBoolKey("has_input_focus", states.has_input_focus());
+      dict->Set("has_input_focus", states.has_input_focus());
   }
 
   if (fuchsia_node.has_location())
-    dict->SetStringKey("location", LocationToString(fuchsia_node.location()));
+    dict->Set("location", LocationToString(fuchsia_node.location()));
 
   if (fuchsia_node.has_transform()) {
-    dict->SetStringKey(
-        "transform", Mat4ToString(fuchsia_node.node_to_container_transform()));
+    dict->Set("transform",
+              Mat4ToString(fuchsia_node.node_to_container_transform()));
   }
 }
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
index d6a0762..4081216 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
@@ -32,13 +32,13 @@
 
  private:
   void RecursiveBuildTree(const BrowserAccessibility& node,
-                          base::DictionaryValue* dict) const;
+                          base::Value::Dict* dict) const;
 
   std::string ProcessTreeForOutput(
       const base::DictionaryValue& node) const override;
 
   void AddProperties(const BrowserAccessibility& node,
-                     base::DictionaryValue* dict) const;
+                     base::Value::Dict* dict) const;
 };
 
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 121da49..a705de9 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -670,6 +670,14 @@
     if (event.event_type == ax::mojom::Event::kLoadComplete) {
       DCHECK_EQ(event_target, GetRoot());
       DCHECK(event_target->IsPlatformDocument());
+
+      // Don't fire multiple load-complete events. One may have been added by
+      // RenderAccessibilityImpl::SendPendingAccessibilityEvents, and firing
+      // multiple events can result in screen readers double-presenting the
+      // load and/or interrupting speech. See, for instance, crbug.com/1352464.
+      if (received_load_complete_event)
+        continue;
+
       received_load_complete_event = true;
     }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 1ff13bc3..d218beb0 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -95,6 +95,12 @@
 BrowserAccessibility* BrowserAccessibilityManagerAndroid::RetargetForEvents(
     BrowserAccessibility* node,
     RetargetEventType type) const {
+  // TODO(crbug.com/1350627): Node should not be null. But this seems to be
+  // happening in the wild for reasons not yet determined. Make this a
+  // DCHECK.
+  if (!node)
+    return nullptr;
+
   // Sometimes we get events on nodes in our internal accessibility tree
   // that aren't exposed on Android. Get |updated| to point to the lowest
   // ancestor that is exposed.
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index c185be7a..af6500f7 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -334,7 +334,8 @@
           std::move(special_storage_policy),
           MakeStorageDelegate(),
           std::make_unique<AttributionCookieCheckerImpl>(storage_partition),
-          std::make_unique<AttributionReportNetworkSender>(storage_partition),
+          std::make_unique<AttributionReportNetworkSender>(
+              storage_partition->GetURLLoaderFactoryForBrowserProcess()),
           std::make_unique<AttributionDataHostManagerImpl>(this)) {}
 
 AttributionManagerImpl::AttributionManagerImpl(
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender.cc b/content/browser/attribution_reporting/attribution_report_network_sender.cc
index 62466831..d9bd4400 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender.cc
+++ b/content/browser/attribution_reporting/attribution_report_network_sender.cc
@@ -13,7 +13,6 @@
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_utils.h"
 #include "content/browser/attribution_reporting/send_result.h"
-#include "content/public/browser/storage_partition.h"
 #include "net/base/isolation_info.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -22,6 +21,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_request_body.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
@@ -43,8 +43,10 @@
 }  // namespace
 
 AttributionReportNetworkSender::AttributionReportNetworkSender(
-    StoragePartition* storage_partition)
-    : storage_partition_(storage_partition) {}
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(std::move(url_loader_factory)) {
+  DCHECK(url_loader_factory_);
+}
 
 AttributionReportNetworkSender::~AttributionReportNetworkSender() = default;
 
@@ -52,13 +54,6 @@
     AttributionReport report,
     bool is_debug_report,
     ReportSentCallback sent_callback) {
-  // The browser process URLLoaderFactory is not created by default, so don't
-  // create it until it is directly needed.
-  if (!url_loader_factory_) {
-    url_loader_factory_ =
-        storage_partition_->GetURLLoaderFactoryForBrowserProcess();
-  }
-
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = report.ReportURL(is_debug_report);
   resource_request->method = net::HttpRequestHeaders::kPostMethod;
@@ -126,11 +121,6 @@
                      is_debug_report, std::move(sent_callback)));
 }
 
-void AttributionReportNetworkSender::SetURLLoaderFactoryForTesting(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
-  url_loader_factory_ = url_loader_factory;
-}
-
 void AttributionReportNetworkSender::OnReportSent(
     UrlLoaderList::iterator it,
     AttributionReport report,
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender.h b/content/browser/attribution_reporting/attribution_report_network_sender.h
index 5d21b5ab..cf7f7448 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender.h
+++ b/content/browser/attribution_reporting/attribution_report_network_sender.h
@@ -9,23 +9,22 @@
 #include <memory>
 
 #include "base/callback_forward.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
 #include "content/browser/attribution_reporting/attribution_report_sender.h"
 #include "content/common/content_export.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace net {
 class HttpResponseHeaders;
 }  // namespace net
 
 namespace network {
+class SharedURLLoaderFactory;
 class SimpleURLLoader;
 }  // namespace network
 
 namespace content {
 
 class AttributionReport;
-class StoragePartition;
 
 // Issues POST requests containing attribution reports. Maintains a set of all
 // ongoing UrlLoaders used for posting reports. Created and owned by
@@ -33,7 +32,8 @@
 class CONTENT_EXPORT AttributionReportNetworkSender
     : public AttributionReportSender {
  public:
-  explicit AttributionReportNetworkSender(StoragePartition* storage_partition);
+  explicit AttributionReportNetworkSender(
+      scoped_refptr<network::SharedURLLoaderFactory>);
   AttributionReportNetworkSender(const AttributionReportNetworkSender&) =
       delete;
   AttributionReportNetworkSender& operator=(
@@ -48,10 +48,6 @@
                   bool is_debug_report,
                   ReportSentCallback sent_callback) override;
 
-  // Tests inject a TestURLLoaderFactory so they can mock the network response.
-  void SetURLLoaderFactoryForTesting(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-
  private:
   // This is a std::list so that iterators remain valid during modifications.
   using UrlLoaderList = std::list<std::unique_ptr<network::SimpleURLLoader>>;
@@ -66,10 +62,7 @@
   // Reports that are actively being sent.
   UrlLoaderList loaders_in_progress_;
 
-  // Must outlive |this|.
-  raw_ptr<StoragePartition> storage_partition_;
-
-  // Lazily accessed URLLoaderFactory used for network requests.
+  // Used for network requests.
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 };
 
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
index c355c86..a29e8d7f 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
@@ -73,26 +73,20 @@
   AttributionReportNetworkSenderTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
         network_sender_(std::make_unique<AttributionReportNetworkSender>(
-            /*storage_partition=*/nullptr)),
-        shared_url_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_)) {
-    network_sender_->SetURLLoaderFactoryForTesting(shared_url_loader_factory_);
-  }
+                &test_url_loader_factory_))) {}
 
  protected:
   // |task_environment_| must be initialized first.
   content::BrowserTaskEnvironment task_environment_;
 
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
   base::MockCallback<base::OnceCallback<void(AttributionReport, SendResult)>>
       callback_;
 
   // Unique ptr so it can be reset during testing.
   std::unique_ptr<AttributionReportNetworkSender> network_sender_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-
- private:
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
 };
 
 TEST_F(AttributionReportNetworkSenderTest,
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 05e2ebb6..708fa7d8 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -437,12 +437,12 @@
     else
       policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
 
-    // Block this DLL even if it is not loaded by the browser process.
-    policy->AddDllToUnload(L"cmsetac.dll");
-
     if (policy->GetConfig()->IsConfigured())
       return true;
 
+    // Block this DLL even if it is not loaded by the browser process.
+    policy->GetConfig()->AddDllToUnload(L"cmsetac.dll");
+
     if (cmd_line_.HasSwitch(switches::kEnableLogging)) {
       std::wstring log_file_path = logging::GetLogFileFullPath();
       if (!log_file_path.empty()) {
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index d40e72be..9eebbc4 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -1042,7 +1042,7 @@
  public:
   // Subset of parameters passed to SellerWorklet's ScoreAd method.
   struct ScoreAdParams {
-    ScoreAdCallback callback;
+    mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient> score_ad_client;
     double bid;
     url::Origin interest_group_owner;
   };
@@ -1077,7 +1077,8 @@
                uint32_t browser_signal_bidding_duration_msecs,
                const absl::optional<base::TimeDelta> seller_timeout,
                uint64_t trace_id,
-               ScoreAdCallback score_ad_callback) override {
+               mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient>
+                   score_ad_client) override {
     // SendPendingSignalsRequests() should only be called once all ads are
     // scored.
     EXPECT_FALSE(send_pending_signals_requests_called_);
@@ -1089,7 +1090,7 @@
     EXPECT_EQ(seller_timeout.value(), base::Milliseconds(500));
 
     ScoreAdParams score_ad_params;
-    score_ad_params.callback = std::move(score_ad_callback);
+    score_ad_params.score_ad_client = std::move(score_ad_client);
     score_ad_params.bid = bid;
     score_ad_params.interest_group_owner = browser_signal_interest_group_owner;
     score_ad_params_.emplace_front(std::move(score_ad_params));
@@ -5784,14 +5785,16 @@
     auto score_ad_params = seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
     EXPECT_EQ(7, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/11,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-             /*data_version=*/0,
-             /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/11,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+            /*data_version=*/0,
+            /*has_data_version=*/false,
+            /*debug_loss_report_url=*/absl::nullopt,
+            /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+            /*errors=*/{});
 
     // Finish the auction.
     seller_worklet->WaitForReportResult();
@@ -5880,14 +5883,16 @@
   PrivateAggregationRequests score_ad_1_pa_requests;
   score_ad_1_pa_requests.push_back(
       kExpectedScoreAdPrivateAggregationRequest.Clone());
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/11,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt,
-           std::move(score_ad_1_pa_requests),
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/11,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt,
+          std::move(score_ad_1_pa_requests),
+          /*errors=*/{});
 
   // Score Bidder2's bid.
   score_ad_params = seller_worklet->WaitForScoreAd();
@@ -5896,14 +5901,16 @@
   PrivateAggregationRequests score_ad_2_pa_requests;
   score_ad_2_pa_requests.push_back(
       kExpectedScoreAdPrivateAggregationRequest.Clone());
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt,
-           std::move(score_ad_2_pa_requests),
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt,
+          std::move(score_ad_2_pa_requests),
+          /*errors=*/{});
 
   PrivateAggregationRequests report_result_pa_requests;
   report_result_pa_requests.push_back(
@@ -6040,24 +6047,28 @@
       bidder1_worklet.reset();
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(5, score_ad_params.bid);
-      std::move(score_ad_params.callback)
-          .Run(/*score=*/10,
-               auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-               /*data_version=*/0, /*has_data_version=*/false,
-               /*debug_loss_report_url=*/absl::nullopt,
-               /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-               /*errors=*/{});
+      mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+          std::move(score_ad_params.score_ad_client))
+          ->OnScoreAdComplete(
+              /*score=*/10,
+              auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+              /*data_version=*/0, /*has_data_version=*/false,
+              /*debug_loss_report_url=*/absl::nullopt,
+              /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+              /*errors=*/{});
 
       // Score Bidder2's bid.
       EXPECT_EQ(kBidder2, score_ad_params2.interest_group_owner);
       EXPECT_EQ(7, score_ad_params2.bid);
-      std::move(score_ad_params2.callback)
-          .Run(/*score=*/11,
-               auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-               /*data_version=*/0, /*has_data_version=*/false,
-               /*debug_loss_report_url=*/absl::nullopt,
-               /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-               /*errors=*/{});
+      mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+          std::move(score_ad_params2.score_ad_client))
+          ->OnScoreAdComplete(
+              /*score=*/11,
+              auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+              /*data_version=*/0, /*has_data_version=*/false,
+              /*debug_loss_report_url=*/absl::nullopt,
+              /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+              /*errors=*/{});
 
       seller_worklet->WaitForReportResult();
       DCHECK_EQ(CrashPhase::kReportResult, crash_phase);
@@ -6162,16 +6173,18 @@
   auto score_ad_params = component_seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(2, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/3,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
-               /*ad=*/"null",
-               /*bid=*/0,
-               /*has_bid=*/false),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/3,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
+              /*ad=*/"null",
+              /*bid=*/0,
+              /*has_bid=*/false),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Top-level seller worklet scores the bid.
   auto top_level_seller_worklet =
@@ -6180,13 +6193,15 @@
   score_ad_params = top_level_seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(2, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/4,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/4,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Top-level seller worklet returns a report url.
   top_level_seller_worklet->WaitForReportResult();
@@ -6275,16 +6290,18 @@
     auto score_ad_params = component_seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(2, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/3,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
-                 /*ad=*/"null",
-                 /*bid=*/0,
-                 /*has_bid=*/false),
-             /*data_version=*/0, /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/3,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
+                /*ad=*/"null",
+                /*bid=*/0,
+                /*has_bid=*/false),
+            /*data_version=*/0, /*has_data_version=*/false,
+            /*debug_loss_report_url=*/absl::nullopt,
+            /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+            /*errors=*/{});
 
     // Top-level seller worklet scores the bid.
     auto top_level_seller_worklet =
@@ -6293,13 +6310,15 @@
     score_ad_params = top_level_seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(2, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/4,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-             /*data_version=*/0, /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/4,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+            /*data_version=*/0, /*has_data_version=*/false,
+            /*debug_loss_report_url=*/absl::nullopt,
+            /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+            /*errors=*/{});
 
     // Top-level seller worklet returns a report url.
     top_level_seller_worklet->WaitForReportResult();
@@ -6498,12 +6517,14 @@
     auto score_ad_params = component_seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
     EXPECT_EQ(2, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/3, test_case.params.Clone(),
-             /*data_version=*/0, /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(/*score=*/3, test_case.params.Clone(),
+                            /*data_version=*/0, /*has_data_version=*/false,
+                            /*debug_loss_report_url=*/absl::nullopt,
+                            /*debug_win_report_url=*/absl::nullopt,
+                            /*pa_requests=*/{},
+                            /*errors=*/{});
 
     // The auction fails, because of the bad ComponentAuctionModifiedBidParams.
     auction_run_loop_->Run();
@@ -6552,16 +6573,18 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(2, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/3,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
-               /*ad=*/"null",
-               /*bid=*/0,
-               /*has_bid=*/false),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/3,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
+              /*ad=*/"null",
+              /*bid=*/0,
+              /*has_bid=*/false),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   auction_run_loop_->Run();
 
@@ -6619,13 +6642,15 @@
       auto score_ad_params = seller_worklet->WaitForScoreAd();
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(1, score_ad_params.bid);
-      std::move(score_ad_params.callback)
-          .Run(/*score=*/11,
-               auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-               /*data_version=*/0, /*has_data_version=*/false,
-               /*debug_loss_report_url=*/absl::nullopt,
-               /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-               /*errors=*/{});
+      mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+          std::move(score_ad_params.score_ad_client))
+          ->OnScoreAdComplete(
+              /*score=*/11,
+              auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+              /*data_version=*/0, /*has_data_version=*/false,
+              /*debug_loss_report_url=*/absl::nullopt,
+              /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+              /*errors=*/{});
 
       // Finish the auction.
       seller_worklet->WaitForReportResult();
@@ -6713,13 +6738,15 @@
       auto score_ad_params = seller_worklet->WaitForScoreAd();
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(1, score_ad_params.bid);
-      std::move(score_ad_params.callback)
-          .Run(/*score=*/11,
-               auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-               /*data_version=*/0, /*has_data_version=*/false,
-               /*debug_loss_report_url=*/absl::nullopt,
-               /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-               /*errors=*/{});
+      mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+          std::move(score_ad_params.score_ad_client))
+          ->OnScoreAdComplete(
+              /*score=*/11,
+              auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+              /*data_version=*/0, /*has_data_version=*/false,
+              /*debug_loss_report_url=*/absl::nullopt,
+              /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+              /*errors=*/{});
 
       // Finish the auction.
       seller_worklet->WaitForReportResult();
@@ -6947,13 +6974,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Bidder1 never gets to report anything, since the seller providing a bad
   // report URL aborts the auction.
@@ -7003,13 +7032,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Bidder1 never gets to report anything, since the seller providing a bad
   // report URL aborts the auction.
@@ -7069,28 +7100,32 @@
   auto score_ad_params = component_seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
-               /*ad=*/"null",
-               /*bid=*/0,
-               /*has_bid=*/false),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
+              /*ad=*/"null",
+              /*bid=*/0,
+              /*has_bid=*/false),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Top-level seller scores the bid.
   score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Top-level seller worklet returns a valid HTTPS report URL.
   seller_worklet->WaitForReportResult();
@@ -7150,13 +7185,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   seller_worklet->WaitForReportResult();
   seller_worklet->InvokeReportResultCallback(
@@ -7210,13 +7247,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   seller_worklet->WaitForReportResult();
   seller_worklet->InvokeReportResultCallback(
@@ -7275,13 +7314,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(7, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/11,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           /*debug_loss_report_url=*/absl::nullopt,
-           /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/11,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
 
   // Finish the auction.
   seller_worklet->WaitForReportResult();
@@ -7337,13 +7378,15 @@
     auto score_ad_params = seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/10,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-             /*data_version=*/0, /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/10,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+            /*data_version=*/0, /*has_data_version=*/false,
+            /*debug_loss_report_url=*/absl::nullopt,
+            /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+            /*errors=*/{});
 
     // Bidder2 returns a bid, which is then scored.
     bidder2_worklet->InvokeGenerateBidCallback(/*bid=*/5,
@@ -7351,13 +7394,15 @@
     score_ad_params = seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/10,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-             /*data_version=*/0, /*has_data_version=*/false,
-             /*debug_loss_report_url=*/absl::nullopt,
-             /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/10,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+            /*data_version=*/0, /*has_data_version=*/false,
+            /*debug_loss_report_url=*/absl::nullopt,
+            /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+            /*errors=*/{});
     // Need to flush the service pipe to make sure the AuctionRunner has
     // received the score.
     seller_worklet->Flush();
@@ -7482,27 +7527,31 @@
             EXPECT_EQ(10, score_ad_params2.bid);
             break;
           case Event::kBid1Scored:
-            std::move(score_ad_params1.callback)
-                .Run(/*score=*/bidder1_wins ? 11 : 9,
-                     auction_worklet::mojom::
-                         ComponentAuctionModifiedBidParamsPtr(),
-                     /*data_version=*/0, /*has_data_version=*/false,
-                     /*debug_loss_report_url=*/absl::nullopt,
-                     /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-                     /*errors=*/{});
+            mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+                std::move(score_ad_params1.score_ad_client))
+                ->OnScoreAdComplete(
+                    /*score=*/bidder1_wins ? 11 : 9,
+                    auction_worklet::mojom::
+                        ComponentAuctionModifiedBidParamsPtr(),
+                    /*data_version=*/0, /*has_data_version=*/false,
+                    /*debug_loss_report_url=*/absl::nullopt,
+                    /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+                    /*errors=*/{});
             // Wait for the AuctionRunner to receive the score.
             task_environment_.RunUntilIdle();
             break;
           case Event::kBid2Scored:
-            std::move(score_ad_params2.callback)
-                .Run(/*score=*/10,
-                     auction_worklet::mojom::
-                         ComponentAuctionModifiedBidParamsPtr(),
-                     /*data_version=*/0,
-                     /*has_data_version=*/false,
-                     /*debug_loss_report_url=*/absl::nullopt,
-                     /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
-                     /*errors=*/{});
+            mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+                std::move(score_ad_params2.score_ad_client))
+                ->OnScoreAdComplete(/*score=*/10,
+                                    auction_worklet::mojom::
+                                        ComponentAuctionModifiedBidParamsPtr(),
+                                    /*data_version=*/0,
+                                    /*has_data_version=*/false,
+                                    /*debug_loss_report_url=*/absl::nullopt,
+                                    /*debug_win_report_url=*/absl::nullopt,
+                                    /*pa_requests=*/{},
+                                    /*errors=*/{});
             // Wait for the AuctionRunner to receive the score.
             task_environment_.RunUntilIdle();
             break;
@@ -8861,13 +8910,15 @@
     auto score_ad_params = seller_worklet->WaitForScoreAd();
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
-    std::move(score_ad_params.callback)
-        .Run(/*score=*/10,
-             auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-             /*data_version=*/0, /*has_data_version=*/false,
-             test_case.seller_debug_loss_report_url,
-             test_case.seller_debug_win_report_url, /*pa_requests=*/{},
-             /*errors=*/{});
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+        std::move(score_ad_params.score_ad_client))
+        ->OnScoreAdComplete(
+            /*score=*/10,
+            auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+            /*data_version=*/0, /*has_data_version=*/false,
+            test_case.seller_debug_loss_report_url,
+            test_case.seller_debug_win_report_url, /*pa_requests=*/{},
+            /*errors=*/{});
     auction_run_loop_->Run();
     EXPECT_EQ(test_case.expected_error_message, TakeBadMessage());
 
@@ -8921,13 +8972,15 @@
   auto score_ad_params = seller_worklet->WaitForScoreAd();
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
-  std::move(score_ad_params.callback)
-      .Run(/*score=*/10,
-           auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
-           /*data_version=*/0, /*has_data_version=*/false,
-           GURL("https://seller-debug-loss-reporting.com/1"),
-           GURL("https://seller-debug-win-reporting.com/1"), /*pa_requests=*/{},
-           /*errors=*/{});
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*data_version=*/0, /*has_data_version=*/false,
+          GURL("https://seller-debug-loss-reporting.com/1"),
+          GURL("https://seller-debug-win-reporting.com/1"), /*pa_requests=*/{},
+          /*errors=*/{});
 
   seller_worklet->WaitForReportResult();
   seller_worklet->InvokeReportResultCallback();
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index e3105446..87bd2ef2 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -277,7 +277,8 @@
                uint32_t browser_signal_bidding_duration_msecs,
                const absl::optional<base::TimeDelta> seller_timeout,
                uint64_t trace_id,
-               ScoreAdCallback score_ad_callback) override {
+               mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient>
+                   score_ad_client) override {
     NOTREACHED();
   }
 
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 52cb810..f3f9ca6 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -275,7 +275,7 @@
   // Closes all Mojo pipes and release all weak pointers.
   void ClosePipes() {
     // This is needed in addition to closing worklet pipes since the callbacks
-    // passed to Mojo aren't currently cancellable.
+    // passed to Mojo pipes this class doesn't own aren't cancellable.
     weak_ptr_factory_.InvalidateWeakPtrs();
 
     for (BidState& bid_state : bid_states_) {
@@ -835,9 +835,11 @@
 
 void InterestGroupAuction::ClosePipes() {
   // This is needed in addition to closing worklet pipes since the callbacks
-  // passed to Mojo aren't currently cancellable.
+  // passed to Mojo pipes this class doesn't own aren't cancellable.
   weak_ptr_factory_.InvalidateWeakPtrs();
 
+  score_ad_receivers_.Clear();
+
   for (auto& buyer_helper : buyer_helpers_) {
     buyer_helper->ClosePipes();
   }
@@ -1299,18 +1301,64 @@
 
   ++bids_being_scored_;
   Bid* bid_raw = bid.get();
+
+  mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient> score_ad_remote;
+  score_ad_receivers_.Add(
+      this, score_ad_remote.InitWithNewPipeAndPassReceiver(), std::move(bid));
   seller_worklet_handle_->GetSellerWorklet()->ScoreAd(
       bid_raw->ad_metadata, bid_raw->bid, config_->non_shared_params,
       GetOtherSellerParam(*bid_raw), bid_raw->interest_group->owner,
       bid_raw->render_url, bid_raw->ad_components,
       bid_raw->bid_duration.InMilliseconds(), SellerTimeout(),
-      *bid_raw->bid_state->trace_id,
-      base::BindOnce(&InterestGroupAuction::OnBidScored,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(bid)));
+      *bid_raw->bid_state->trace_id, std::move(score_ad_remote));
 }
 
-void InterestGroupAuction::OnBidScored(
-    std::unique_ptr<Bid> bid,
+bool InterestGroupAuction::ValidateScoreBidCompleteResult(
+    double score,
+    auction_worklet::mojom::ComponentAuctionModifiedBidParams*
+        component_auction_modified_bid_params,
+    const absl::optional<GURL>& debug_loss_report_url,
+    const absl::optional<GURL>& debug_win_report_url) {
+  // If `debug_loss_report_url` or `debug_win_report_url` is not a valid HTTPS
+  // URL, the auction should fail because the worklet is compromised.
+  if (debug_loss_report_url.has_value() &&
+      !IsUrlValid(debug_loss_report_url.value())) {
+    score_ad_receivers_.ReportBadMessage(
+        "Invalid seller debugging loss report URL");
+    return false;
+  }
+  if (debug_win_report_url.has_value() &&
+      !IsUrlValid(debug_win_report_url.value())) {
+    score_ad_receivers_.ReportBadMessage(
+        "Invalid seller debugging win report URL");
+    return false;
+  }
+
+  // Only validate `component_auction_modified_bid_params` if the bid was
+  // accepted.
+  if (score > 0) {
+    // If they accept a bid / return a positive score, component auction
+    // SellerWorklets must return a `component_auction_modified_bid_params`,
+    // and top-level auctions must not.
+    if ((parent_ == nullptr) !=
+        (component_auction_modified_bid_params == nullptr)) {
+      score_ad_receivers_.ReportBadMessage(
+          "Invalid component_auction_modified_bid_params");
+      return false;
+    }
+    // If a component seller modified the bid, the new bid must also be valid.
+    if (component_auction_modified_bid_params &&
+        component_auction_modified_bid_params->has_bid &&
+        !IsValidBid(component_auction_modified_bid_params->bid)) {
+      score_ad_receivers_.ReportBadMessage(
+          "Invalid component_auction_modified_bid_params bid");
+      return false;
+    }
+  }
+  return true;
+}
+
+void InterestGroupAuction::OnScoreAdComplete(
     double score,
     auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
         component_auction_modified_bid_params,
@@ -1321,6 +1369,17 @@
     PrivateAggregationRequests pa_requests,
     const std::vector<std::string>& errors) {
   DCHECK_GT(bids_being_scored_, 0);
+
+  if (!ValidateScoreBidCompleteResult(
+          score, component_auction_modified_bid_params.get(),
+          debug_loss_report_url, debug_win_report_url)) {
+    OnBiddingAndScoringComplete(AuctionResult::kBadMojoMessage);
+    return;
+  }
+
+  std::unique_ptr<Bid> bid = std::move(score_ad_receivers_.current_context());
+  score_ad_receivers_.Remove(score_ad_receivers_.current_receiver());
+
   TRACE_EVENT_NESTABLE_ASYNC_END0("fledge", "seller_worklet_score_ad",
                                   *bid->bid_state->trace_id);
   bid->bid_state->EndTracing();
@@ -1340,20 +1399,6 @@
                                   std::move_iterator(pa_requests.end()));
   }
 
-  // If `debug_loss_report_url` or `debug_win_report_url` is not a valid HTTPS
-  // URL, the auction should fail because the worklet is compromised.
-  if (debug_loss_report_url.has_value() &&
-      !IsUrlValid(debug_loss_report_url.value())) {
-    mojo::ReportBadMessage("Invalid seller debugging loss report URL");
-    OnBiddingAndScoringComplete(AuctionResult::kBadMojoMessage);
-    return;
-  }
-  if (debug_win_report_url.has_value() &&
-      !IsUrlValid(debug_win_report_url.value())) {
-    mojo::ReportBadMessage("Invalid seller debugging win report URL");
-    OnBiddingAndScoringComplete(AuctionResult::kBadMojoMessage);
-    return;
-  }
   errors_.insert(errors_.end(), errors.begin(), errors.end());
 
   // Use separate fields for component and top-level seller reports, so both can
@@ -1372,27 +1417,15 @@
 
   // A score <= 0 means the seller rejected the bid.
   if (score <= 0) {
+    // Need to delete `bid` because OnBiddingAndScoringComplete() may delete
+    // this, which leaves danging pointers on the stack. While this is safe to
+    // do (nothing has access to `bid` to dereference them), it makes the
+    // dangling pointer tooling sad.
+    bid.reset();
     MaybeCompleteBiddingAndScoringPhase();
     return;
   }
 
-  // If they accept a bid / return a positive score, component auction
-  // SellerWorklets must return a `component_auction_modified_bid_params`, and
-  // top-level auctions must not.
-  if (component_auction_modified_bid_params.is_null() != (parent_ == nullptr)) {
-    mojo::ReportBadMessage("Invalid component_auction_modified_bid_params");
-    OnBiddingAndScoringComplete(AuctionResult::kBadMojoMessage);
-    return;
-  }
-  // If a component seller modified the bid, the new bid must also be valid.
-  if (component_auction_modified_bid_params &&
-      component_auction_modified_bid_params->has_bid &&
-      !IsValidBid(component_auction_modified_bid_params->bid)) {
-    mojo::ReportBadMessage("Invalid component_auction_modified_bid_params bid");
-    OnBiddingAndScoringComplete(AuctionResult::kBadMojoMessage);
-    return;
-  }
-
   bool is_top_bid = false;
   const url::Origin& owner = bid->interest_group->owner;
 
@@ -1435,6 +1468,7 @@
         std::move(bid), std::move(component_auction_modified_bid_params));
   }
 
+  bid.reset();
   MaybeCompleteBiddingAndScoringPhase();
 }
 
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index 474947c..eb19471d 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -26,6 +26,7 @@
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom-forward.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
@@ -63,7 +64,8 @@
 //
 // * ReportResult / ReportWin phase: This phase invokes ReportResult() on
 // winning seller worklets and ReportWin() in the winning bidder worklet.
-class CONTENT_EXPORT InterestGroupAuction {
+class CONTENT_EXPORT InterestGroupAuction
+    : public auction_worklet::mojom::ScoreAdClient {
  public:
   // Post auction signals (signals only available after auction completes such
   // as winning bid) for debug loss/win reporting.
@@ -248,18 +250,18 @@
 
     // InterestGroup that made the bid. Owned by the BidState of that
     // InterestGroup.
-    const raw_ptr<const blink::InterestGroup, DanglingUntriaged> interest_group;
+    const raw_ptr<const blink::InterestGroup> interest_group;
 
     // Points to the InterestGroupAd within `interest_group`.
-    const raw_ptr<const blink::InterestGroup::Ad, DanglingUntriaged> bid_ad;
+    const raw_ptr<const blink::InterestGroup::Ad> bid_ad;
 
     // `bid_state` of the InterestGroup that made the bid. This should not be
     // written to, except for adding seller debug reporting URLs.
-    const raw_ptr<BidState, DanglingUntriaged> bid_state;
+    const raw_ptr<BidState> bid_state;
 
     // The Auction with the interest group that made this bid. Important in the
     // case of component auctions.
-    const raw_ptr<InterestGroupAuction, DanglingUntriaged> auction;
+    const raw_ptr<InterestGroupAuction> auction;
   };
 
   // Combines a Bid with seller score and seller state needed to invoke its
@@ -310,7 +312,7 @@
   InterestGroupAuction(const InterestGroupAuction&) = delete;
   InterestGroupAuction& operator=(const InterestGroupAuction&) = delete;
 
-  ~InterestGroupAuction();
+  ~InterestGroupAuction() override;
 
   // Starts loading the interest groups that can participate in an auction.
   //
@@ -501,17 +503,27 @@
   // Calls into the seller asynchronously to score the passed in bid.
   void ScoreBidIfReady(std::unique_ptr<Bid> bid);
 
-  // Callback from ScoreBid().
-  void OnBidScored(std::unique_ptr<Bid> bid,
-                   double score,
-                   auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
-                       component_auction_modified_bid_params,
-                   uint32_t scoring_signals_data_version,
-                   bool has_scoring_signals_data_version,
-                   const absl::optional<GURL>& debug_loss_report_url,
-                   const absl::optional<GURL>& debug_win_report_url,
-                   PrivateAggregationRequests pa_requests,
-                   const std::vector<std::string>& errors);
+  // Validates the passed in result from ScoreBidComplete(). On failure, reports
+  // a bad message to the active receiver in `score_ad_receivers_` and returns
+  // false.
+  bool ValidateScoreBidCompleteResult(
+      double score,
+      auction_worklet::mojom::ComponentAuctionModifiedBidParams*
+          component_auction_modified_bid_params,
+      const absl::optional<GURL>& debug_loss_report_url,
+      const absl::optional<GURL>& debug_win_report_url);
+
+  // auction_worklet::mojom::ScoreAdClient implementation:
+  void OnScoreAdComplete(
+      double score,
+      auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
+          component_auction_modified_bid_params,
+      uint32_t scoring_signals_data_version,
+      bool has_scoring_signals_data_version,
+      const absl::optional<GURL>& debug_loss_report_url,
+      const absl::optional<GURL>& debug_win_report_url,
+      PrivateAggregationRequests pa_requests,
+      const std::vector<std::string>& errors) override;
 
   // Invoked when the bid becomes the new highest scoring other bid, to handle
   // calculation of post auction signals. `owner` is nullptr in the event the
@@ -758,6 +770,12 @@
   // problem).
   bool all_bids_scored_ = false;
 
+  // Receivers for OnScoreAd() callbacks. Owns Bids, which have raw pointers to
+  // other objects, so must be last, to avoid triggering tooling to check for
+  // dangling pointers.
+  mojo::ReceiverSet<auction_worklet::mojom::ScoreAdClient, std::unique_ptr<Bid>>
+      score_ad_receivers_;
+
   base::WeakPtrFactory<InterestGroupAuction> weak_ptr_factory_{this};
 };
 
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 8fc4e67..74ffa82 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/base_paths.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/environment.h"
@@ -19,6 +20,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
@@ -79,6 +81,13 @@
 constexpr char kKrb5ConfEnvName[] = "KRB5_CONFIG";
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+constexpr char kKrb5CCFilePrefix[] = "FILE:";
+constexpr char kKrb5Directory[] = "kerberos";
+constexpr char kKrb5CCFile[] = "krb5cc";
+constexpr char kKrb5ConfFile[] = "krb5.conf";
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 bool g_force_create_network_service_directly = false;
 mojo::Remote<network::mojom::NetworkService>* g_network_service_remote =
     nullptr;
@@ -292,6 +301,22 @@
   return *storage;
 }
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+base::FilePath GetKerberosDir() {
+  base::FilePath dir;
+  base::PathService::Get(base::DIR_HOME, &dir);
+  return dir.Append(kKrb5Directory);
+}
+
+std::string GetKrb5CCEnvValue() {
+  return kKrb5CCFilePrefix + GetKerberosDir().Append(kKrb5CCFile).value();
+}
+
+std::string GetKrb5ConfEnvValue() {
+  return GetKerberosDir().Append(kKrb5ConfFile).value();
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 void CreateInProcessNetworkService(
     mojo::PendingReceiver<network::mojom::NetworkService> receiver) {
   TRACE_EVENT0("loading", "CreateInProcessNetworkService");
@@ -328,6 +353,14 @@
 #if BUILDFLAG(IS_POSIX)
   // Send Kerberos environment variables to the network service.
   if (IsOutOfProcessNetworkService()) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    network_service_params->environment.push_back(
+        network::mojom::EnvironmentVariable::New(kKrb5CCEnvName,
+                                                 GetKrb5CCEnvValue()));
+    network_service_params->environment.push_back(
+        network::mojom::EnvironmentVariable::New(kKrb5ConfEnvName,
+                                                 GetKrb5ConfEnvValue()));
+#else
     std::unique_ptr<base::Environment> env(base::Environment::Create());
     std::string value;
     if (env->HasVar(kKrb5CCEnvName)) {
@@ -340,8 +373,9 @@
       network_service_params->environment.push_back(
           network::mojom::EnvironmentVariable::New(kKrb5ConfEnvName, value));
     }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   }
-#endif
+#endif  // BUILDFLAG(IS_POSIX)
   return network_service_params;
 }
 
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index f0a70ff6..95a0eaa3 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -475,9 +475,7 @@
     return false;
   }
 
-  if (render_frame_host().HasTransientUserActivation())
-    return true;
-  return false;
+  return render_frame_host().HasTransientUserActivation();
 }
 
 void ClipboardHostImpl::ReadAvailableCustomAndStandardFormats(
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h
index 1de8bb5..e35e163 100644
--- a/content/browser/renderer_host/clipboard_host_impl.h
+++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -101,7 +101,7 @@
 
   // Performs a check to see if pasting `data` is allowed by data transfer
   // policies and invokes PasteIfPolicyAllowedCallback upon completion.
-  // PerformPasteIfContentAllowed maybe be invoked immediately if the policy
+  // PerformPasteIfContentAllowed may be invoked immediately if the policy
   // controller doesn't exist.
   void PasteIfPolicyAllowed(ui::ClipboardBuffer clipboard_buffer,
                             const ui::ClipboardFormatType& data_type,
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index 8e231038..1a11970 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -595,6 +595,8 @@
                            NavigationToAnonymousDocumentNetworkIsolationInfo);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplTest,
                            ChildOfAnonymousIsAnonymous);
+  FRIEND_TEST_ALL_PREFIXES(ContentPasswordManagerDriverTest,
+                           PasswordAutofillDisabledOnAnonymousIframe);
 
   // Called by the destructor. When `this` is an outer dummy FrameTreeNode
   // representing an inner FrameTree, this method destroys said inner FrameTree.
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index e2c503ac..a27f22e 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -70,6 +70,11 @@
 
 }  // namespace
 
+struct MediaDevicesDispatcherHost::AudioInputCapabilitiesRequest {
+  MediaDeviceSaltAndOrigin salt_and_origin;
+  GetAudioInputCapabilitiesCallback client_callback;
+};
+
 // static
 void MediaDevicesDispatcherHost::Create(
     int render_process_id,
@@ -450,11 +455,6 @@
           *raw_id, try_in_use_first));
 }
 
-struct MediaDevicesDispatcherHost::AudioInputCapabilitiesRequest {
-  MediaDeviceSaltAndOrigin salt_and_origin;
-  GetAudioInputCapabilitiesCallback client_callback;
-};
-
 void MediaDevicesDispatcherHost::GetDefaultAudioInputDeviceID(
     GetAudioInputCapabilitiesCallback client_callback,
     const MediaDeviceSaltAndOrigin& salt_and_origin) {
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index efa0c5c..b50be4b 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -6,14 +6,19 @@
 #include "base/metrics/statistics_recorder.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
+#include "content/browser/private_aggregation/private_aggregation_manager_impl.h"
+#include "content/browser/private_aggregation/private_aggregation_test_utils.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/shared_storage/shared_storage_worklet_driver.h"
 #include "content/browser/shared_storage/shared_storage_worklet_host.h"
 #include "content/browser/shared_storage/shared_storage_worklet_host_manager.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/private_aggregation_features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
@@ -33,9 +38,11 @@
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
 #include "third_party/blink/public/common/shared_storage/shared_storage_utils.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 
 namespace content {
 
@@ -2736,4 +2743,174 @@
         blink::features::FencedFramesImplementationType::kMPArch),
     &SharedStorageReportEventBrowserTest::DescribeParams);
 
+class SharedStoragePrivateAggregationDisabledBrowserTest
+    : public SharedStorageBrowserTest {
+ public:
+  SharedStoragePrivateAggregationDisabledBrowserTest() {
+    scoped_feature_list_.InitAndDisableFeature(content::kPrivateAggregationApi);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationDisabledBrowserTest,
+                       PrivateAggregationNotDefined) {
+  EXPECT_TRUE(NavigateToURL(shell(),
+                            https_server()->GetURL("a.test", kSimplePagePath)));
+
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  ExecuteScriptInWorklet(shell(), R"(
+      privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+    )");
+
+  ASSERT_EQ(1u, console_observer.messages().size());
+  EXPECT_EQ("ReferenceError: privateAggregation is not defined",
+            base::UTF16ToUTF8(console_observer.messages()[0].message));
+  EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kError,
+            console_observer.messages()[0].log_level);
+}
+
+class SharedStoragePrivateAggregationEnabledBrowserTest
+    : public SharedStorageBrowserTest {
+ public:
+  // TODO(alexmt): Consider factoring out along with FLEDGE definition.
+  class TestPrivateAggregationManagerImpl
+      : public PrivateAggregationManagerImpl {
+   public:
+    TestPrivateAggregationManagerImpl(
+        std::unique_ptr<PrivateAggregationBudgeter> budgeter,
+        std::unique_ptr<PrivateAggregationHost> host)
+        : PrivateAggregationManagerImpl(std::move(budgeter),
+                                        std::move(host),
+                                        /*storage_partition=*/nullptr) {}
+  };
+
+  SharedStoragePrivateAggregationEnabledBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(content::kPrivateAggregationApi);
+  }
+
+  void SetUpOnMainThread() override {
+    SharedStorageBrowserTest::SetUpOnMainThread();
+
+    a_test_origin_ = https_server()->GetOrigin("a.test");
+
+    private_aggregation_host_ = new PrivateAggregationHost(
+        /*on_report_request_received=*/mock_callback_.Get());
+
+    static_cast<StoragePartitionImpl*>(shell()
+                                           ->web_contents()
+                                           ->GetBrowserContext()
+                                           ->GetDefaultStoragePartition())
+        ->OverridePrivateAggregationManagerForTesting(
+            std::make_unique<TestPrivateAggregationManagerImpl>(
+                std::make_unique<MockPrivateAggregationBudgeter>(),
+                base::WrapUnique<PrivateAggregationHost>(
+                    private_aggregation_host_)));
+
+    EXPECT_TRUE(NavigateToURL(
+        shell(), https_server()->GetURL("a.test", kSimplePagePath)));
+  }
+
+  const base::MockRepeatingCallback<void(AggregatableReportRequest,
+                                         PrivateAggregationBudgetKey)>&
+  mock_callback() {
+    return mock_callback_;
+  }
+
+ protected:
+  url::Origin a_test_origin_;
+
+ private:
+  PrivateAggregationHost* private_aggregation_host_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  base::MockRepeatingCallback<void(AggregatableReportRequest,
+                                   PrivateAggregationBudgetKey)>
+      mock_callback_;
+};
+
+IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+                       BasicTest) {
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(mock_callback(), Run)
+      .WillOnce(testing::Invoke([&](AggregatableReportRequest request,
+                                    PrivateAggregationBudgetKey budget_key) {
+        ASSERT_EQ(request.payload_contents().contributions.size(), 1u);
+        EXPECT_EQ(request.payload_contents().contributions[0].bucket, 1);
+        EXPECT_EQ(request.payload_contents().contributions[0].value, 2);
+        EXPECT_EQ(request.shared_info().reporting_origin, a_test_origin_);
+        EXPECT_EQ(budget_key.origin(), a_test_origin_);
+        EXPECT_EQ(budget_key.api(),
+                  PrivateAggregationBudgetKey::Api::kSharedStorage);
+        run_loop.Quit();
+      }));
+  ExecuteScriptInWorklet(shell(), R"(
+      privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+    )");
+
+  EXPECT_TRUE(console_observer.messages().empty());
+
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+                       RejectedTest) {
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  ExecuteScriptInWorklet(shell(), R"(
+      privateAggregation.sendHistogramReport({bucket: -1, value: 2});
+    )");
+
+  ASSERT_EQ(1u, console_observer.messages().size());
+  EXPECT_EQ("TypeError: Bucket must be either an integer Number or BigInt",
+            base::UTF16ToUTF8(console_observer.messages()[0].message));
+  EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kError,
+            console_observer.messages()[0].log_level);
+}
+
+IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+                       MultipleRequests) {
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(mock_callback(), Run)
+      .WillOnce(testing::Invoke([&](AggregatableReportRequest request,
+                                    PrivateAggregationBudgetKey budget_key) {
+        ASSERT_EQ(request.payload_contents().contributions.size(), 1u);
+        EXPECT_EQ(request.payload_contents().contributions[0].bucket, 1);
+        EXPECT_EQ(request.payload_contents().contributions[0].value, 2);
+        EXPECT_EQ(request.shared_info().reporting_origin, a_test_origin_);
+        EXPECT_EQ(budget_key.origin(), a_test_origin_);
+        EXPECT_EQ(budget_key.api(),
+                  PrivateAggregationBudgetKey::Api::kSharedStorage);
+      }))
+      .WillOnce(testing::Invoke([&](AggregatableReportRequest request,
+                                    PrivateAggregationBudgetKey budget_key) {
+        ASSERT_EQ(request.payload_contents().contributions.size(), 1u);
+        EXPECT_EQ(request.payload_contents().contributions[0].bucket, 3);
+        EXPECT_EQ(request.payload_contents().contributions[0].value, 4);
+        EXPECT_EQ(request.shared_info().reporting_origin, a_test_origin_);
+        EXPECT_EQ(budget_key.origin(), a_test_origin_);
+        EXPECT_EQ(budget_key.api(),
+                  PrivateAggregationBudgetKey::Api::kSharedStorage);
+        run_loop.Quit();
+      }));
+
+  ExecuteScriptInWorklet(shell(), R"(
+      privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      privateAggregation.sendHistogramReport({bucket: 3, value: 4});
+    )");
+
+  EXPECT_TRUE(console_observer.messages().empty());
+
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.cc b/content/browser/shared_storage/shared_storage_worklet_host.cc
index 20a14ce..b386f91 100644
--- a/content/browser/shared_storage/shared_storage_worklet_host.cc
+++ b/content/browser/shared_storage/shared_storage_worklet_host.cc
@@ -4,16 +4,25 @@
 
 #include "content/browser/shared_storage/shared_storage_worklet_host.h"
 
+#include <utility>
+
+#include "base/check.h"
 #include "base/metrics/histogram_functions.h"
 #include "components/services/storage/shared_storage/public/mojom/shared_storage.mojom.h"
 #include "components/services/storage/shared_storage/shared_storage_manager.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
+#include "content/browser/private_aggregation/private_aggregation_host.h"
+#include "content/browser/private_aggregation/private_aggregation_manager.h"
 #include "content/browser/renderer_host/page_impl.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/shared_storage/shared_storage_document_service_impl.h"
 #include "content/browser/shared_storage/shared_storage_url_loader_factory_proxy.h"
 #include "content/browser/shared_storage/shared_storage_worklet_driver.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/common/renderer.mojom.h"
+#include "content/public/browser/browser_context.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace content {
 
@@ -659,13 +668,34 @@
     driver_->StartWorkletService(
         shared_storage_worklet_service_.BindNewPipeAndPassReceiver());
 
-    shared_storage_worklet_service_->BindSharedStorageWorkletServiceClient(
-        shared_storage_worklet_service_client_.BindNewEndpointAndPassRemote());
+    shared_storage_worklet_service_->Initialize(
+        shared_storage_worklet_service_client_.BindNewEndpointAndPassRemote(),
+        MaybeBindPrivateAggregationHost());
   }
 
   return shared_storage_worklet_service_.get();
 }
 
+mojo::PendingRemote<content::mojom::PrivateAggregationHost>
+SharedStorageWorkletHost::MaybeBindPrivateAggregationHost() {
+  DCHECK(browser_context_);
+  PrivateAggregationManager* private_aggregation_manager =
+      PrivateAggregationManager::GetManager(*browser_context_);
+  if (!private_aggregation_manager)
+    return mojo::PendingRemote<content::mojom::PrivateAggregationHost>();
+
+  mojo::PendingRemote<content::mojom::PrivateAggregationHost>
+      pending_pa_host_remote;
+  if (!private_aggregation_manager->BindNewReceiver(
+          shared_storage_origin_,
+          PrivateAggregationBudgetKey::Api::kSharedStorage,
+          pending_pa_host_remote.InitWithNewPipeAndPassReceiver())) {
+    return mojo::PendingRemote<content::mojom::PrivateAggregationHost>();
+  }
+
+  return pending_pa_host_remote;
+}
+
 bool SharedStorageWorkletHost::IsSharedStorageAllowed() {
   return GetContentClient()->browser()->IsSharedStorageAllowed(
       browser_context_, main_frame_origin_, shared_storage_origin_);
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.h b/content/browser/shared_storage/shared_storage_worklet_host.h
index f3540021..cba3ab7 100644
--- a/content/browser/shared_storage/shared_storage_worklet_host.h
+++ b/content/browser/shared_storage/shared_storage_worklet_host.h
@@ -165,6 +165,12 @@
   shared_storage_worklet::mojom::SharedStorageWorkletService*
   GetAndConnectToSharedStorageWorkletService();
 
+  // Binds a receiver to the `PrivateAggregationManager` and returns the
+  // `PendingRemote`. If there is no `PrivateAggregationManger`, returns an
+  // invalid `PendingRemote`.
+  mojo::PendingRemote<content::mojom::PrivateAggregationHost>
+  MaybeBindPrivateAggregationHost();
+
   bool IsSharedStorageAllowed();
 
   AddModuleState add_module_state_ = AddModuleState::kNotInitiated;
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index ae60b2214..d079003 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -349,10 +349,6 @@
           {"DeferredShaping", blink::features::kDeferredFontShaping},
           {"DesktopPWAsSubApps", blink::features::kDesktopPWAsSubApps},
           {"DocumentTransition", blink::features::kDocumentTransition},
-          // TODO(crbug.com/649162): Remove DialogFocusNewSpecBehavior after
-          // the feature is in stable with no issues.
-          {"DialogFocusNewSpecBehavior",
-           blink::features::kDialogFocusNewSpecBehavior},
           {"EditContext", blink::features::kEditContext},
           {"ElementSuperRareData", blink::features::kElementSuperRareData},
           {"FileHandling", blink::features::kFileHandlingAPI},
@@ -365,6 +361,7 @@
           {"FontSrcLocalMatching", features::kFontSrcLocalMatching},
           {"HTMLParamElementUrlSupport",
            blink::features::kHTMLParamElementUrlSupport},
+          {"HTMLPopupAttribute", blink::features::kHTMLPopupAttribute},
           {"LayoutNG", blink::features::kLayoutNG},
           {"LegacyWindowsDWriteFontFallback",
            features::kLegacyWindowsDWriteFontFallback},
diff --git a/content/common/private_aggregation_host.mojom b/content/common/private_aggregation_host.mojom
index e62184c..df9a20c 100644
--- a/content/common/private_aggregation_host.mojom
+++ b/content/common/private_aggregation_host.mojom
@@ -7,8 +7,7 @@
 import "content/common/aggregatable_report.mojom";
 
 // Interface implemented in the browser for worklets and renderers to forward
-// histogram report requests. The worklets/renderers will need to construct
-// PendingReceivers and pass them to the browser to be bound.
+// histogram report requests.
 interface PrivateAggregationHost {
   // Requests a histogram report with the specified details be sent to the
   // origin associated with the remote. Note that only a small number of fields
diff --git a/content/common/shared_storage_worklet_service.mojom b/content/common/shared_storage_worklet_service.mojom
index 5a21be65..89a7c5ed 100644
--- a/content/common/shared_storage_worklet_service.mojom
+++ b/content/common/shared_storage_worklet_service.mojom
@@ -8,6 +8,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import
 "components/services/storage/shared_storage/public/mojom/shared_storage.mojom";
+import "content/common/private_aggregation_host.mojom";
 import "services/network/public/mojom/url_loader_factory.mojom";
 import "third_party/blink/public/mojom/shared_storage/shared_storage.mojom";
 import "url/mojom/origin.mojom";
@@ -69,9 +70,11 @@
 // in the worklet environment.
 // See https://github.com/pythagoraskitty/shared-storage/blob/main/README.md
 interface SharedStorageWorkletService {
-  // Bind to the client
-  BindSharedStorageWorkletServiceClient(
-    pending_associated_remote<SharedStorageWorkletServiceClient> client);
+  // Binds `client` and, if not null, `pa_host`. Must be the first call on this
+  // interface.
+  Initialize(
+    pending_associated_remote<SharedStorageWorkletServiceClient> client,
+    pending_remote<content.mojom.PrivateAggregationHost>? pa_host);
 
   // Handle sharedStorage.worklet.addModule(): download and load the script in
   // the worklet environment. The origin of the `script_source_url` should be
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index 8e96433..6a3cd957 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -50,7 +50,7 @@
 namespace display {
 class Screen;
 class ScopedNativeScreen;
-}
+}  // namespace display
 
 namespace net {
 namespace test {
@@ -120,6 +120,10 @@
       const std::string& frame_name,
       const blink::ParsedPermissionsPolicy& allow) = 0;
 
+  // Same as AppendChild above, but simulates a custom attributes.
+  virtual RenderFrameHost* AppendAnonymousChild(
+      const std::string& frame_name) = 0;
+
   // Gives tests access to RenderFrameHostImpl::OnDetach. Destroys |this|.
   virtual void Detach() = 0;
 
diff --git a/content/services/auction_worklet/public/mojom/seller_worklet.mojom b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
index 6265c844..f2cefa23 100644
--- a/content/services/auction_worklet/public/mojom/seller_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
@@ -54,6 +54,58 @@
   bool has_modified_bid;
 };
 
+// Interface for returning ScoreAd results. The advantage of having an interface
+// is that it makes ScoreAd() calls cancellable, and allows callbacks passed
+// over the Mojo pipe to be deleted when the Mojo pipe is, to avoid setting off
+// the raw pointer lifetime validation logic.
+interface ScoreAdClient {
+  // Called when a ScoreAd() invocation completes.
+  //
+  // Parameters:
+  // `score` Non-negative score the SellerWorklet assigns to the bid. A value
+  //  of 0 indicates either an error running the script, or that the script
+  //  indicated the bid should not be used.
+  //
+  // `component_auction_modified_bid_params` If this is a component seller
+  //  worklet, contains parameters to pass to the top-level seller worklet
+  //  in place of values from the original bidder worklet's BidderWorkletBid.
+  //
+  // `scoring_signals_data_version` The value of the Data-Version header served
+  //  with the trusted scoring signals.
+  //
+  // `has_scoring_signals_data_version` True to indicate Data-Version header
+  //  was present in the HTTP response for the trusted scoring signals.
+  //  TODO(https://crbug.com/657632): Update when optional integers supported.
+  //
+  // `debug_loss_report_url` The URL to request if this bid does not win the
+  //  auction. It's requested if the auction runs to completion and this is not
+  //  the winning bid, including the case that this worklet rejects this bid
+  //  outright, giving it a score <= 0. This field has the debug prefix because
+  //  it's part of an interim reporting API that will be replaced with
+  //  standardized reporting APIs once available. It must be a valid HTTPS URL.
+  //
+  // `debug_win_report_url` The URL to request if this bid wins the auction.
+  //  This field has the debug prefix because it's part of an interim reporting
+  //  API that will be replaced with standardized reporting APIs once available.
+  //  It must be a valid HTTPS URL.
+  //
+  // `pa_requests` The various requests made to the Private Aggregation API.
+  //
+  // `errors` are various error messages to be used for debugging. These are too
+  //  sensitive for the renderers to see. `errors` should not be assumed to be
+  //  empty if `score` is positive, nor should it be assumed to be non-empty if
+  //  `score` is 0.
+  OnScoreAdComplete(double score,
+                    ComponentAuctionModifiedBidParams?
+                        component_auction_modified_bid_params,
+                    uint32 scoring_signals_data_version,
+                    bool has_scoring_signals_data_version,
+                    url.mojom.Url? debug_loss_report_url,
+                    url.mojom.Url? debug_win_report_url,
+                    array<PrivateAggregationRequest> pa_requests,
+                    array<string> errors);
+};
+
 // Manages the auction workflow for one loaded FLEDGE seller worklet.
 // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md
 //
@@ -107,40 +159,8 @@
   // `trace_id` ID of a nestable asynchronous trace event of category `fledge`
   //  to use with tracing calls.
   //
-  // Returns:
-  // `score` Non-negative score the SellerWorklet assigns to the bid. A value
-  //  of 0 indicates either an error running the script, or that the script
-  //  indicated the bid should not be used.
-  //
-  // `component_auction_modified_bid_params` If this is a component seller
-  //  worklet, contains parameters to pass to the top-level seller worklet
-  //  in place of values from the original bidder worklet's BidderWorkletBid.
-  //
-  // `scoring_signals_data_version` The value of the Data-Version header served
-  //  with the trusted scoring signals.
-  //
-  // `has_scoring_signals_data_version` True to indicate Data-Version header
-  //  was present in the HTTP response for the trusted scoring signals.
-  //  TODO(https://crbug.com/657632): Update when optional integers supported.
-  //
-  // `debug_loss_report_url` The URL to request if this bid does not win the
-  //  auction. It's requested if the auction runs to completion and this is not
-  //  the winning bid, including the case that this worklet rejects this bid
-  //  outright, giving it a score <= 0. This field has the debug prefix because
-  //  it's part of an interim reporting API that will be replaced with
-  //  standardized reporting APIs once available. It must be a valid HTTPS URL.
-  //
-  // `debug_win_report_url` The URL to request if this bid wins the auction.
-  //  This field has the debug prefix because it's part of an interim reporting
-  //  API that will be replaced with standardized reporting APIs once available.
-  //  It must be a valid HTTPS URL.
-  //
-  // `pa_requests` The various requests made to the Private Aggregation API.
-  //
-  // `errors` are various error messages to be used for debugging. These are too
-  //  sensitive for the renderers to see. `errors` should not be assumed to be
-  //  empty if `score` is positive, nor should it be assumed to be non-empty if
-  //  `score` is 0.
+  // `score_ad_client` When the ScoreAd completes, successfully or not, its
+  // OnScoreAdComplete() method will invoked be invoked with the results.
   ScoreAd(string ad_metadata_json,
           double bid,
           blink.mojom.AuctionAdConfigNonSharedParams
@@ -151,16 +171,8 @@
           array<url.mojom.Url> browser_signal_ad_component_render_urls,
           uint32 browser_signal_bidding_duration_msecs,
           mojo_base.mojom.TimeDelta? seller_timeout,
-          uint64 trace_id) =>
-              (double score,
-              ComponentAuctionModifiedBidParams?
-                  component_auction_modified_bid_params,
-              uint32 scoring_signals_data_version,
-              bool has_scoring_signals_data_version,
-              url.mojom.Url? debug_loss_report_url,
-              url.mojom.Url? debug_win_report_url,
-              array<PrivateAggregationRequest> pa_requests,
-              array<string> errors);
+          uint64 trace_id,
+          pending_remote<ScoreAdClient> score_ad_client);
 
   // Hint to the worklet to send a network request for any needed trusted
   // signals data now. SellerWorklets normally wait briefy for there to be a
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index 8cb30d9..11063af 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -278,7 +278,8 @@
     uint32_t browser_signal_bidding_duration_msecs,
     const absl::optional<base::TimeDelta> seller_timeout,
     uint64_t trace_id,
-    ScoreAdCallback callback) {
+    mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient>
+        score_ad_client) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
   score_ad_tasks_.emplace_front();
 
@@ -299,7 +300,7 @@
       browser_signal_bidding_duration_msecs;
   score_ad_task->seller_timeout = seller_timeout;
   score_ad_task->trace_id = trace_id;
-  score_ad_task->callback = std::move(callback);
+  score_ad_task->score_ad_client.Bind(std::move(score_ad_client));
 
   // If `trusted_signals_request_manager_` exists, there's a trusted scoring
   // signals URL which needs to be fetched before the auction can be run.
@@ -1010,11 +1011,17 @@
   if (task->trusted_scoring_signals_error_msg)
     errors.insert(errors.begin(), *task->trusted_scoring_signals_error_msg);
 
-  std::move(task->callback)
-      .Run(score, std::move(component_auction_modified_bid_params),
-           scoring_signals_data_version.value_or(0),
-           scoring_signals_data_version.has_value(), debug_loss_report_url,
-           debug_win_report_url, std::move(pa_requests), std::move(errors));
+  // This is safe to do, even if the pipe was closed - the message will just be
+  // dropped.
+  //
+  // TOOD(mmenke): Consider watching for the pipe closing and aborting work if
+  // it does. Only useful if the SellerWorklet object is still in use, so
+  // unclear how useful it would be.
+  task->score_ad_client->OnScoreAdComplete(
+      score, std::move(component_auction_modified_bid_params),
+      scoring_signals_data_version.value_or(0),
+      scoring_signals_data_version.has_value(), debug_loss_report_url,
+      debug_win_report_url, std::move(pa_requests), std::move(errors));
   score_ad_tasks_.erase(task);
 }
 
diff --git a/content/services/auction_worklet/seller_worklet.h b/content/services/auction_worklet/seller_worklet.h
index fc8d24a5..0160fd6 100644
--- a/content/services/auction_worklet/seller_worklet.h
+++ b/content/services/auction_worklet/seller_worklet.h
@@ -95,7 +95,8 @@
       uint32_t browser_signal_bidding_duration_msecs,
       const absl::optional<base::TimeDelta> seller_timeout,
       uint64_t trace_id,
-      ScoreAdCallback callback) override;
+      mojo::PendingRemote<auction_worklet::mojom::ScoreAdClient>
+          score_ad_client) override;
   void SendPendingSignalsRequests() override;
   void ReportResult(
       const blink::AuctionConfig::NonSharedParams&
@@ -140,7 +141,7 @@
     absl::optional<base::TimeDelta> seller_timeout;
     uint64_t trace_id;
 
-    ScoreAdCallback callback;
+    mojo::Remote<auction_worklet::mojom::ScoreAdClient> score_ad_client;
 
     std::unique_ptr<TrustedSignalsRequestManager::Request>
         trusted_scoring_signals_request;
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 88801f2..480f8050 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -24,6 +24,8 @@
 #include "content/services/auction_worklet/worklet_devtools_debug_test_util.h"
 #include "content/services/auction_worklet/worklet_test_util.h"
 #include "content/services/auction_worklet/worklet_v8_debug_test_util.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/unique_receiver_set.h"
 #include "net/http/http_status_code.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -98,6 +100,57 @@
                             raw_return_value.c_str());
 }
 
+// A ScoreAdClient that takes a callback to call in OnScoreAdComplete().
+class TestScoreAdClient : public mojom::ScoreAdClient {
+ public:
+  using ScoreAdCompleteCallback = base::OnceCallback<void(
+      double score,
+      auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
+          component_auction_modified_bid_params,
+      uint32_t scoring_signals_data_version,
+      bool has_scoring_signals_data_version,
+      const absl::optional<GURL>& debug_loss_report_url,
+      const absl::optional<GURL>& debug_win_report_url,
+      PrivateAggregationRequests pa_requests,
+      const std::vector<std::string>& errors)>;
+
+  explicit TestScoreAdClient(ScoreAdCompleteCallback score_ad_complete_callback)
+      : score_ad_complete_callback_(std::move(score_ad_complete_callback)) {}
+
+  ~TestScoreAdClient() override = default;
+
+  // Helper that creates a TestScoreAdClient owned by a SelfOwnedReceiver.
+  static mojo::PendingRemote<mojom::ScoreAdClient> Create(
+      ScoreAdCompleteCallback score_ad_complete_callback) {
+    mojo::PendingRemote<mojom::ScoreAdClient> client_remote;
+    mojo::MakeSelfOwnedReceiver(std::make_unique<TestScoreAdClient>(
+                                    std::move(score_ad_complete_callback)),
+                                client_remote.InitWithNewPipeAndPassReceiver());
+    return client_remote;
+  }
+
+  // mojom::ScoreAdClient implementation:
+  void OnScoreAdComplete(
+      double score,
+      auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
+          component_auction_modified_bid_params,
+      uint32_t scoring_signals_data_version,
+      bool has_scoring_signals_data_version,
+      const absl::optional<GURL>& debug_loss_report_url,
+      const absl::optional<GURL>& debug_win_report_url,
+      PrivateAggregationRequests pa_requests,
+      const std::vector<std::string>& errors) override {
+    std::move(score_ad_complete_callback_)
+        .Run(score, std::move(component_auction_modified_bid_params),
+             scoring_signals_data_version, has_scoring_signals_data_version,
+             debug_loss_report_url, debug_win_report_url,
+             std::move(pa_requests), errors);
+  }
+
+ private:
+  ScoreAdCompleteCallback score_ad_complete_callback_;
+};
+
 class SellerWorkletTest : public testing::Test {
  public:
   explicit SellerWorkletTest(
@@ -260,7 +313,7 @@
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
         seller_timeout_,
         /*trace_id=*/1,
-        base::BindOnce(
+        TestScoreAdClient::Create(base::BindOnce(
             [](double expected_score,
                mojom::ComponentAuctionModifiedBidParamsPtr
                    expected_component_auction_modified_bid_params,
@@ -307,7 +360,7 @@
             std::move(expected_component_auction_modified_bid_params),
             expected_data_version, expected_debug_loss_report_url,
             expected_debug_win_report_url, std::move(expected_pa_requests),
-            expected_errors, std::move(done_closure)));
+            expected_errors, std::move(done_closure))));
   }
 
   void RunScoreAdOnWorkletExpectingCallbackNeverInvoked(
@@ -319,17 +372,18 @@
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
         seller_timeout_,
         /*trace_id=*/1,
-        base::BindOnce([](double score,
-                          mojom::ComponentAuctionModifiedBidParamsPtr
-                              component_auction_modified_bid_params,
-                          uint32_t scoring_signals_data_version,
-                          bool has_scoring_signals_data_version,
-                          const absl::optional<GURL>& debug_loss_report_url,
-                          const absl::optional<GURL>& debug_win_report_url,
-                          PrivateAggregationRequests pa_requests,
-                          const std::vector<std::string>& errors) {
-          ADD_FAILURE() << "This should not be invoked";
-        }));
+        TestScoreAdClient::Create(
+            base::BindOnce([](double score,
+                              mojom::ComponentAuctionModifiedBidParamsPtr
+                                  component_auction_modified_bid_params,
+                              uint32_t scoring_signals_data_version,
+                              bool has_scoring_signals_data_version,
+                              const absl::optional<GURL>& debug_loss_report_url,
+                              const absl::optional<GURL>& debug_win_report_url,
+                              PrivateAggregationRequests pa_requests,
+                              const std::vector<std::string>& errors) {
+              ADD_FAILURE() << "This should not be invoked";
+            })));
   }
 
   // Loads and runs a scode_ad() script, expecting the supplied result.
@@ -2284,7 +2338,7 @@
           browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
           seller_timeout_,
           /*trace_id=*/1,
-          base::BindLambdaForTesting(
+          TestScoreAdClient::Create(base::BindLambdaForTesting(
               [&run_loop](double score,
                           mojom::ComponentAuctionModifiedBidParamsPtr
                               component_auction_modified_bid_params,
@@ -2298,7 +2352,7 @@
                 EXPECT_FALSE(has_scoring_signals_data_version);
                 EXPECT_TRUE(errors.empty());
                 run_loop.Quit();
-              }));
+              })));
       run_loop.Run();
     }
 
@@ -2344,17 +2398,20 @@
       browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
       seller_timeout_,
       /*trace_id=*/1,
-      base::BindOnce([](double score,
-                        mojom::ComponentAuctionModifiedBidParamsPtr
-                            component_auction_modified_bid_params,
-                        uint32_t scoring_signals_data_version,
-                        bool has_scoring_signals_data_version,
-                        const absl::optional<GURL>& debug_loss_report_url,
-                        const absl::optional<GURL>& debug_win_report_url,
-                        PrivateAggregationRequests pa_requests,
-                        const std::vector<std::string>& errors) {
-        ADD_FAILURE() << "Callback should not be invoked since worklet deleted";
-      }));
+      TestScoreAdClient::Create(
+          base::BindOnce(
+              [](double score,
+                 mojom::ComponentAuctionModifiedBidParamsPtr
+                     component_auction_modified_bid_params,
+                 uint32_t scoring_signals_data_version,
+                 bool has_scoring_signals_data_version,
+                 const absl::optional<GURL>& debug_loss_report_url,
+                 const absl::optional<GURL>& debug_win_report_url,
+                 PrivateAggregationRequests pa_requests,
+                 const std::vector<std::string>& errors) {
+                ADD_FAILURE()
+                    << "Callback should not be invoked since worklet deleted";
+              })));
   base::RunLoop().RunUntilIdle();
   seller_worklet.reset();
   event_handle->Signal();
@@ -3233,7 +3290,7 @@
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
         seller_timeout_,
         /*trace_id=*/1,
-        base::BindLambdaForTesting(
+        TestScoreAdClient::Create(base::BindLambdaForTesting(
             [&run_loop](double score,
                         mojom::ComponentAuctionModifiedBidParamsPtr
                             component_auction_modified_bid_params,
@@ -3255,7 +3312,7 @@
                 EXPECT_EQ(absl::nullopt, debug_win_report_url);
               }
               run_loop.Quit();
-            }));
+            })));
     run_loop.Run();
   }
 }
diff --git a/content/services/shared_storage_worklet/BUILD.gn b/content/services/shared_storage_worklet/BUILD.gn
index eb6333c2..06bc3df0 100644
--- a/content/services/shared_storage_worklet/BUILD.gn
+++ b/content/services/shared_storage_worklet/BUILD.gn
@@ -10,6 +10,8 @@
     "console.h",
     "module_script_downloader.cc",
     "module_script_downloader.h",
+    "private_aggregation.cc",
+    "private_aggregation.h",
     "shared_storage.cc",
     "shared_storage.h",
     "shared_storage_iterator.cc",
diff --git a/content/services/shared_storage_worklet/private_aggregation.cc b/content/services/shared_storage_worklet/private_aggregation.cc
new file mode 100644
index 0000000..3059162
--- /dev/null
+++ b/content/services/shared_storage_worklet/private_aggregation.cc
@@ -0,0 +1,214 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/services/shared_storage_worklet/private_aggregation.h"
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "content/common/aggregatable_report.mojom.h"
+#include "content/common/private_aggregation_host.mojom.h"
+#include "content/services/shared_storage_worklet/worklet_v8_helper.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/dictionary.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-external.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-template.h"
+
+namespace shared_storage_worklet {
+
+// TODO(crbug.com/1351757): The argument parsing logic in this file should be
+// factored out into a joint utils file and shared with the FLEDGE worklet.
+
+namespace {
+
+v8::Local<v8::String> CreateStringFromLiteral(v8::Isolate* isolate,
+                                              const char* ascii_string) {
+  DCHECK(base::IsStringASCII(ascii_string));
+  return v8::String::NewFromUtf8(isolate, ascii_string,
+                                 v8::NewStringType::kNormal,
+                                 strlen(ascii_string))
+      .ToLocalChecked();
+}
+
+v8::MaybeLocal<v8::String> CreateUtf8String(v8::Isolate* isolate,
+                                            base::StringPiece utf8_string) {
+  if (!base::IsStringUTF8(utf8_string))
+    return v8::MaybeLocal<v8::String>();
+  return v8::String::NewFromUtf8(isolate, utf8_string.data(),
+                                 v8::NewStringType::kNormal,
+                                 utf8_string.length());
+}
+
+// If returns `absl::nullopt`, will output an error to `error_out`.
+absl::optional<absl::uint128> ConvertBigIntToUint128(
+    v8::MaybeLocal<v8::BigInt> maybe_bigint,
+    std::string* error_out) {
+  DCHECK(error_out);
+
+  if (maybe_bigint.IsEmpty()) {
+    *error_out = "Failed to interpret as BigInt";
+    return absl::nullopt;
+  }
+
+  v8::Local<v8::BigInt> local_bigint = maybe_bigint.ToLocalChecked();
+  if (local_bigint.IsEmpty()) {
+    *error_out = "Failed to interpret as BigInt";
+    return absl::nullopt;
+  }
+  if (local_bigint->WordCount() > 2) {
+    *error_out = "BigInt is too large";
+    return absl::nullopt;
+  }
+  int sign_bit;
+  int word_count = 2;
+  uint64_t words[2];  // Least significant to most significant.
+  local_bigint->ToWordsArray(&sign_bit, &word_count, words);
+  if (sign_bit) {
+    *error_out = "BigInt must be non-negative";
+    return absl::nullopt;
+  }
+  if (word_count == 0) {
+    words[0] = 0;
+  }
+  if (word_count <= 1) {
+    words[1] = 0;
+  }
+
+  return absl::MakeUint128(words[1], words[0]);
+}
+
+}  // namespace
+
+PrivateAggregation::PrivateAggregation(
+    content::mojom::PrivateAggregationHost& private_aggregation_host)
+    : private_aggregation_host_(private_aggregation_host) {}
+
+PrivateAggregation::~PrivateAggregation() = default;
+
+gin::WrapperInfo PrivateAggregation::kWrapperInfo = {gin::kEmbedderNativeGin};
+
+gin::ObjectTemplateBuilder PrivateAggregation::GetObjectTemplateBuilder(
+    v8::Isolate* isolate) {
+  return Wrappable<PrivateAggregation>::GetObjectTemplateBuilder(isolate)
+      .SetMethod("sendHistogramReport",
+                 &PrivateAggregation::SendHistogramReport);
+}
+
+const char* PrivateAggregation::GetTypeName() {
+  return "PrivateAggregation";
+}
+
+void PrivateAggregation::SendHistogramReport(gin::Arguments* args) {
+  v8::Isolate* isolate = args->isolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  std::vector<v8::Local<v8::Value>> argument_list = args->GetAll();
+
+  if (argument_list.size() != 1 || argument_list[0].IsEmpty() ||
+      !argument_list[0]->IsObject()) {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "sendHistogramReport requires 1 object parameter")));
+    return;
+  }
+
+  gin::Dictionary dict(isolate);
+
+  if (!gin::ConvertFromV8(isolate, argument_list[0], &dict)) {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "Invalid argument in sendHistogramReport")));
+    return;
+  }
+
+  v8::Local<v8::Value> js_bucket;
+  v8::Local<v8::Value> js_value;
+
+  if (!dict.Get("bucket", &js_bucket) || js_bucket.IsEmpty() ||
+      js_bucket->IsNullOrUndefined()) {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "Invalid or missing bucket in sendHistogramReport argument")));
+    return;
+  }
+
+  if (!dict.Get("value", &js_value) || js_value.IsEmpty() ||
+      js_value->IsNullOrUndefined()) {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "Invalid or missing value in sendHistogramReport argument")));
+    return;
+  }
+
+  absl::uint128 bucket;
+  int value;
+
+  if (js_bucket->IsUint32()) {
+    v8::Maybe<uint32_t> maybe_bucket = js_bucket->Uint32Value(context);
+    if (maybe_bucket.IsNothing()) {
+      isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+          isolate, "Failed to interpret value as integer")));
+      return;
+    }
+    bucket = maybe_bucket.ToChecked();
+  } else if (js_bucket->IsBigInt()) {
+    std::string error;
+    absl::optional<absl::uint128> maybe_bucket =
+        ConvertBigIntToUint128(js_bucket->ToBigInt(context), &error);
+    if (!maybe_bucket.has_value()) {
+      DCHECK(base::IsStringUTF8(error));
+      isolate->ThrowException(v8::Exception::TypeError(
+          CreateUtf8String(isolate, error).ToLocalChecked()));
+      return;
+    }
+    bucket = maybe_bucket.value();
+  } else {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "Bucket must be either an integer Number or BigInt")));
+    return;
+  }
+
+  if (js_value->IsInt32()) {
+    v8::Maybe<int32_t> maybe_value = js_value->Int32Value(context);
+    if (maybe_value.IsNothing()) {
+      isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+          isolate, "Failed to interpret value as integer")));
+      return;
+    }
+    value = maybe_value.ToChecked();
+  } else if (js_value->IsBigInt()) {
+    isolate->ThrowException(v8::Exception::TypeError(
+        CreateStringFromLiteral(isolate, "Value cannot be a BigInt")));
+    return;
+  } else {
+    isolate->ThrowException(v8::Exception::TypeError(
+        CreateStringFromLiteral(isolate, "Value must be an integer Number")));
+    return;
+  }
+
+  if (value < 0) {
+    isolate->ThrowException(v8::Exception::TypeError(
+        CreateStringFromLiteral(isolate, "Value must be non-negative")));
+    return;
+  }
+
+  std::vector<content::mojom::AggregatableReportHistogramContributionPtr>
+      contributions;
+  contributions.push_back(
+      content::mojom::AggregatableReportHistogramContribution::New(bucket,
+                                                                   value));
+
+  private_aggregation_host_->SendHistogramReport(
+      std::move(contributions),
+      // TODO(alexmt): consider allowing this to be set
+      content::mojom::AggregationServiceMode::kDefault);
+}
+
+}  // namespace shared_storage_worklet
diff --git a/content/services/shared_storage_worklet/private_aggregation.h b/content/services/shared_storage_worklet/private_aggregation.h
new file mode 100644
index 0000000..47dd2a2
--- /dev/null
+++ b/content/services/shared_storage_worklet/private_aggregation.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_SERVICES_SHARED_STORAGE_WORKLET_PRIVATE_AGGREGATION_H_
+#define CONTENT_SERVICES_SHARED_STORAGE_WORKLET_PRIVATE_AGGREGATION_H_
+
+#include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/private_aggregation_host.mojom-forward.h"
+#include "content/common/shared_storage_worklet_service.mojom.h"
+#include "gin/object_template_builder.h"
+#include "gin/wrappable.h"
+#include "v8/include/v8-forward.h"
+
+namespace gin {
+class Arguments;
+}  // namespace gin
+
+namespace shared_storage_worklet {
+
+class PrivateAggregation final : public gin::Wrappable<PrivateAggregation> {
+ public:
+  explicit PrivateAggregation(
+      content::mojom::PrivateAggregationHost& private_aggregation_host);
+  ~PrivateAggregation() override;
+
+  static gin::WrapperInfo kWrapperInfo;
+
+  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+      v8::Isolate* isolate) override;
+
+  const char* GetTypeName() override;
+
+ private:
+  void SendHistogramReport(gin::Arguments* args);
+
+  raw_ref<content::mojom::PrivateAggregationHost> private_aggregation_host_;
+
+  base::WeakPtrFactory<PrivateAggregation> weak_ptr_factory_{this};
+};
+
+}  // namespace shared_storage_worklet
+
+#endif  // CONTENT_SERVICES_SHARED_STORAGE_WORKLET_PRIVATE_AGGREGATION_H_
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.cc b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.cc
index f3b2fd6..7abd7d6 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.cc
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.cc
@@ -9,8 +9,10 @@
 #include <utility>
 
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/services/shared_storage_worklet/console.h"
 #include "content/services/shared_storage_worklet/module_script_downloader.h"
+#include "content/services/shared_storage_worklet/private_aggregation.h"
 #include "content/services/shared_storage_worklet/shared_storage.h"
 #include "content/services/shared_storage_worklet/unnamed_operation_handler.h"
 #include "content/services/shared_storage_worklet/url_selection_operation_handler.h"
@@ -48,6 +50,7 @@
     mojo::PendingRemote<network::mojom::URLLoaderFactory>
         pending_url_loader_factory,
     mojom::SharedStorageWorkletServiceClient* client,
+    content::mojom::PrivateAggregationHost* private_aggregation_host,
     const GURL& script_source_url,
     mojom::SharedStorageWorkletService::AddModuleCallback callback) {
   mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory(
@@ -56,12 +59,14 @@
   module_script_downloader_ = std::make_unique<ModuleScriptDownloader>(
       url_loader_factory.get(), script_source_url,
       base::BindOnce(&SharedStorageWorkletGlobalScope::OnModuleScriptDownloaded,
-                     weak_ptr_factory_.GetWeakPtr(), client, script_source_url,
+                     weak_ptr_factory_.GetWeakPtr(), client,
+                     private_aggregation_host, script_source_url,
                      std::move(callback)));
 }
 
 void SharedStorageWorkletGlobalScope::OnModuleScriptDownloaded(
     mojom::SharedStorageWorkletServiceClient* client,
+    content::mojom::PrivateAggregationHost* private_aggregation_host,
     const GURL& script_source_url,
     mojom::SharedStorageWorkletService::AddModuleCallback callback,
     std::unique_ptr<std::string> response_body,
@@ -107,6 +112,15 @@
             console_->GetWrapper(Isolate()).ToLocalChecked())
       .Check();
 
+  if (private_aggregation_host) {
+    private_aggregation_ =
+        std::make_unique<PrivateAggregation>(*private_aggregation_host);
+    global
+        ->Set(context, gin::StringToSymbol(Isolate(), "privateAggregation"),
+              private_aggregation_->GetWrapper(Isolate()).ToLocalChecked())
+        .Check();
+  }
+
   global
       ->Set(context, gin::StringToSymbol(Isolate(), "register"),
             gin::CreateFunctionTemplate(
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h
index cfd6eadb..a875d60 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h
@@ -6,6 +6,7 @@
 #define CONTENT_SERVICES_SHARED_STORAGE_WORKLET_SHARED_STORAGE_WORKLET_GLOBAL_SCOPE_H_
 
 #include "content/common/content_export.h"
+#include "content/common/private_aggregation_host.mojom-forward.h"
 #include "content/common/shared_storage_worklet_service.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -22,6 +23,7 @@
 class UrlSelectionOperationHandler;
 class UnnamedOperationHandler;
 class Console;
+class PrivateAggregation;
 class SharedStorage;
 class ModuleScriptDownloader;
 
@@ -38,11 +40,13 @@
       mojo::PendingRemote<network::mojom::URLLoaderFactory>
           pending_url_loader_factory,
       mojom::SharedStorageWorkletServiceClient* client,
+      content::mojom::PrivateAggregationHost* private_aggregation_host,
       const GURL& script_source_url,
       mojom::SharedStorageWorkletService::AddModuleCallback callback);
 
   void OnModuleScriptDownloaded(
       mojom::SharedStorageWorkletServiceClient* client,
+      content::mojom::PrivateAggregationHost* private_aggregation_host,
       const GURL& script_source_url,
       mojom::SharedStorageWorkletService::AddModuleCallback callback,
       std::unique_ptr<std::string> response_body,
@@ -75,6 +79,7 @@
   v8::Global<v8::Context> global_context_;
 
   std::unique_ptr<Console> console_;
+  std::unique_ptr<PrivateAggregation> private_aggregation_;
   std::unique_ptr<SharedStorage> shared_storage_;
 
   std::unique_ptr<UrlSelectionOperationHandler>
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
index 40b834c..b87b4df1 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
@@ -4,16 +4,21 @@
 
 #include "content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h"
 
+#include "base/check_op.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "components/services/storage/shared_storage/public/mojom/shared_storage.mojom.h"
+#include "content/common/aggregatable_report.mojom.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/services/shared_storage_worklet/worklet_v8_helper.h"
 #include "gin/arguments.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
 #include "gin/function_template.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "v8/include/v8-context.h"
 #include "v8/include/v8-function.h"
 #include "v8/include/v8-value-serializer.h"
@@ -221,6 +226,18 @@
   std::vector<std::string> observed_console_log_messages_;
 };
 
+class MockMojomPrivateAggregationHost
+    : public content::mojom::PrivateAggregationHost {
+ public:
+  // mojom::PrivateAggregationHost:
+  MOCK_METHOD(
+      void,
+      SendHistogramReport,
+      (std::vector<content::mojom::AggregatableReportHistogramContributionPtr>,
+       content::mojom::AggregationServiceMode),
+      (override));
+};
+
 }  // namespace
 
 class SharedStorageWorkletGlobalScopeTest : public testing::Test {
@@ -229,6 +246,8 @@
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
     test_client_ = std::make_unique<TestClient>(
         task_environment_.GetMainThreadTaskRunner());
+    mock_private_aggregation_host_ =
+        std::make_unique<MockMojomPrivateAggregationHost>();
     global_scope_ = std::make_unique<SharedStorageWorkletGlobalScope>();
   }
 
@@ -358,11 +377,16 @@
   }
 
   TestClient* test_client() { return test_client_.get(); }
+  MockMojomPrivateAggregationHost* mock_private_aggregation_host() {
+    return mock_private_aggregation_host_.get();
+  }
 
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_;
 
   std::unique_ptr<TestClient> test_client_;
+  std::unique_ptr<MockMojomPrivateAggregationHost>
+      mock_private_aggregation_host_;
 
   std::unique_ptr<SharedStorageWorkletGlobalScope> global_scope_;
 };
@@ -373,7 +397,8 @@
 
 TEST_F(SharedStorageWorkletGlobalScopeTest, OnModuleScriptDownloadedSuccess) {
   global_scope_->OnModuleScriptDownloaded(
-      test_client_.get(), GURL("https://example.test"), base::DoNothing(),
+      test_client_.get(), mock_private_aggregation_host_.get(),
+      GURL("https://example.test"), base::DoNothing(),
       /*response_body=*/std::make_unique<std::string>(),
       /*error_message=*/{});
 
@@ -391,6 +416,8 @@
   EXPECT_EQ(GetTypeOf("sharedStorage.keys"), "function");
   EXPECT_EQ(GetTypeOf("sharedStorage.entries"), "function");
   EXPECT_EQ(GetTypeOf("sharedStorage.length"), "function");
+  EXPECT_EQ(GetTypeOf("privateAggregation"), "object");
+  EXPECT_EQ(GetTypeOf("privateAggregation.sendHistogramReport"), "function");
 }
 
 TEST_F(SharedStorageWorkletGlobalScopeTest, OnModuleScriptDownloadedWithError) {
@@ -402,17 +429,31 @@
         callback_called = true;
       });
 
-  global_scope_->OnModuleScriptDownloaded(test_client_.get(),
-                                          GURL("https://example.test"),
-                                          std::move(cb), nullptr, "error1");
+  global_scope_->OnModuleScriptDownloaded(
+      test_client_.get(), mock_private_aggregation_host_.get(),
+      GURL("https://example.test"), std::move(cb), nullptr, "error1");
 
   EXPECT_FALSE(IsolateInitialized());
   EXPECT_TRUE(callback_called);
 }
 
+TEST_F(SharedStorageWorkletGlobalScopeTest,
+       OnModuleScriptDownloadedWithoutPrivateAggregationHost) {
+  global_scope_->OnModuleScriptDownloaded(
+      test_client_.get(), /*private_aggregation_host=*/nullptr,
+      GURL("https://example.test"), base::DoNothing(),
+      /*response_body=*/std::make_unique<std::string>(),
+      /*error_message=*/{});
+
+  EXPECT_TRUE(IsolateInitialized());
+
+  EXPECT_EQ(GetTypeOf("privateAggregation"), "undefined");
+}
+
 class SharedStorageAddModuleTest : public SharedStorageWorkletGlobalScopeTest {
  public:
-  void SimulateAddModule(const std::string& script_body) {
+  void SimulateAddModule(const std::string& script_body,
+                         bool define_private_aggregation_host = true) {
     bool callback_called = false;
 
     auto cb = base::BindLambdaForTesting(
@@ -424,7 +465,10 @@
         });
 
     global_scope_->OnModuleScriptDownloaded(
-        test_client_.get(), GURL("https://example.test"), std::move(cb),
+        test_client_.get(),
+        define_private_aggregation_host ? mock_private_aggregation_host_.get()
+                                        : nullptr,
+        GURL("https://example.test"), std::move(cb),
         std::make_unique<std::string>(script_body), /*error_message=*/{});
 
     ASSERT_TRUE(callback_called);
@@ -595,12 +639,51 @@
             "already registered.");
 }
 
+TEST_F(SharedStorageAddModuleTest,
+       RegisterOperationWithPrivateAggregationCall_CallForwarded) {
+  // The operation will not be run.
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport).Times(0);
+
+  SimulateAddModule(R"(
+    class TestClass {
+      async run() {
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      }
+    }
+
+    register("test-operation", TestClass);
+  )");
+
+  EXPECT_TRUE(success());
+  EXPECT_TRUE(error_message().empty());
+}
+
+TEST_F(SharedStorageAddModuleTest,
+       RegisterOperationWithPrivateAggregationCall_PAHostNotDefined) {
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport).Times(0);
+
+  SimulateAddModule(R"(
+    class TestClass {
+      async run() {
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      }
+    }
+
+    register("test-operation", TestClass);
+  )",
+                    /*define_private_aggregation_host=*/false);
+
+  EXPECT_TRUE(success());
+  EXPECT_TRUE(error_message().empty());
+}
+
 class SharedStorageRunOperationTest
     : public SharedStorageWorkletGlobalScopeTest {
  public:
   // The caller should provide a valid module script. The purpose of this test
   // suite is to test RunOperation.
-  void SimulateAddModule(const std::string& script_body) {
+  void SimulateAddModule(const std::string& script_body,
+                         bool define_private_aggregation_host = true) {
     bool add_module_callback_called = false;
 
     auto add_module_callback = base::BindLambdaForTesting(
@@ -610,8 +693,10 @@
         });
 
     global_scope_->OnModuleScriptDownloaded(
-        test_client_.get(), GURL("https://example.test"),
-        std::move(add_module_callback),
+        test_client_.get(),
+        define_private_aggregation_host ? mock_private_aggregation_host_.get()
+                                        : nullptr,
+        GURL("https://example.test"), std::move(add_module_callback),
         std::make_unique<std::string>(script_body), /*error_message=*/{});
 
     ASSERT_TRUE(add_module_callback_called);
@@ -1322,6 +1407,61 @@
   EXPECT_EQ(url_selection_operation_index(), 0u);
 }
 
+TEST_F(SharedStorageRunOperationTest,
+       UnnamedOperationWithPrivateAggregationCall_Success) {
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [](std::vector<
+                 content::mojom::AggregatableReportHistogramContributionPtr>
+                 contributions,
+             content::mojom::AggregationServiceMode aggregation_mode) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(aggregation_mode,
+                      content::mojom::AggregationServiceMode::kDefault);
+          }));
+
+  SimulateAddModule(R"(
+      class TestClass {
+        async run() {
+          privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+        }
+      }
+
+      register("test-operation", TestClass);
+    )");
+
+  SimulateRunOperation("test-operation", /*serialized_data=*/{});
+
+  EXPECT_TRUE(unnamed_operation_finished());
+  EXPECT_TRUE(unnamed_operation_success());
+  EXPECT_TRUE(unnamed_operation_error_message().empty());
+}
+
+TEST_F(SharedStorageRunOperationTest,
+       UnnamedOperationWithPrivateAggregationCall_PAHostNotDefined) {
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport).Times(0);
+
+  SimulateAddModule(R"(
+      class TestClass {
+        async run() {
+          privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+        }
+      }
+
+      register("test-operation", TestClass);
+    )",
+                    /*define_private_aggregation_host=*/false);
+
+  SimulateRunOperation("test-operation", /*serialized_data=*/{});
+
+  EXPECT_TRUE(unnamed_operation_finished());
+  EXPECT_FALSE(unnamed_operation_success());
+  EXPECT_EQ(unnamed_operation_error_message(),
+            "ReferenceError: privateAggregation is not defined");
+}
+
 class SharedStorageObjectMethodTest : public SharedStorageRunOperationTest {
  public:
   SharedStorageObjectMethodTest() {
@@ -2036,4 +2176,188 @@
             "123 456 true undefined null [object Object]");
 }
 
+class SharedStoragePrivateAggregationTest
+    : public SharedStorageRunOperationTest {
+ public:
+  SharedStoragePrivateAggregationTest() {
+    // Run AddModule so that `privateAggregation` is exposed.
+    SimulateAddModule(R"()");
+  }
+
+  void ExecuteScriptExpectNoError(const std::string& script_body) {
+    std::string error_message;
+    ExecuteScript(script_body, &error_message);
+    EXPECT_TRUE(error_message.empty());
+  }
+
+  void ExecuteScriptAndValidateContribution(const std::string& script_body,
+                                            absl::uint128 expected_bucket,
+                                            int expected_value) {
+    EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport)
+        .WillOnce(testing::Invoke(
+            [=](std::vector<
+                    content::mojom::AggregatableReportHistogramContributionPtr>
+                    contributions,
+                content::mojom::AggregationServiceMode aggregation_mode) {
+              ASSERT_EQ(contributions.size(), 1u);
+              EXPECT_EQ(contributions[0]->bucket, expected_bucket);
+              EXPECT_EQ(contributions[0]->value, expected_value);
+              EXPECT_EQ(aggregation_mode,
+                        content::mojom::AggregationServiceMode::kDefault);
+            }));
+
+    ExecuteScriptExpectNoError(script_body);
+  }
+
+  std::string ExecuteScriptReturningError(const std::string& script_body) {
+    std::string error_message;
+    ExecuteScript(script_body, &error_message);
+    EXPECT_FALSE(error_message.empty());
+    return error_message;
+  }
+
+ private:
+  void ExecuteScript(const std::string& script_body, std::string* out_error) {
+    WorkletV8Helper::HandleScope scope(Isolate());
+    v8::Local<v8::Context> context = LocalContext();
+    v8::Context::Scope context_scope(context);
+
+    WorkletV8Helper::CompileAndRunScript(
+        LocalContext(), script_body, GURL("https://example.test"), out_error);
+  }
+};
+
+TEST_F(SharedStoragePrivateAggregationTest, BasicTest) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 2});",
+      /*expected_bucket=*/1, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, BigIntBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: 2});",
+      /*expected_bucket=*/1, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, ZeroBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 0, value: 2});",
+      /*expected_bucket=*/0, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, ZeroBigIntBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 0n, value: 2});",
+      /*expected_bucket=*/0, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, ZeroValue) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 0});",
+      /*expected_bucket=*/1, /*expected_value=*/0);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, LargeBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 18446744073709551616n, "
+      "value: 2});",
+      /*expected_bucket=*/absl::MakeUint128(/*high=*/1, /*low=*/0),
+      /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, MaxBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "340282366920938463463374607431768211455n, value: 2});",
+      /*expected_bucket=*/absl::Uint128Max(), /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NonIntegerBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1.5, value: 2});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: Bucket must be either "
+            "an integer Number or BigInt.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, TooLargeBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "340282366920938463463374607431768211456n, value: 2});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: BigInt is too large.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "-1, value: 2});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: Bucket must be either "
+            "an integer Number or BigInt.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeBigIntBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "-1n, value: 2});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: BigInt must be "
+            "non-negative.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NonIntegerValue_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 2.3});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: Value must be an "
+            "integer Number.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeValue_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: -1});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: Value must be "
+            "non-negative.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, MultipleRequests) {
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [](std::vector<
+                 content::mojom::AggregatableReportHistogramContributionPtr>
+                 contributions,
+             content::mojom::AggregationServiceMode aggregation_mode) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(aggregation_mode,
+                      content::mojom::AggregationServiceMode::kDefault);
+          }))
+      .WillOnce(testing::Invoke(
+          [](std::vector<
+                 content::mojom::AggregatableReportHistogramContributionPtr>
+                 contributions,
+             content::mojom::AggregationServiceMode aggregation_mode) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 3);
+            EXPECT_EQ(contributions[0]->value, 4);
+            EXPECT_EQ(aggregation_mode,
+                      content::mojom::AggregationServiceMode::kDefault);
+          }));
+
+  ExecuteScriptExpectNoError(
+      R"(
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+        privateAggregation.sendHistogramReport({bucket: 3, value: 4});
+      )");
+}
+
 }  // namespace shared_storage_worklet
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.cc b/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.cc
index 44de14b..1c6eaaba 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.cc
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.cc
@@ -4,6 +4,13 @@
 
 #include "content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h"
 
+#include <utility>
+
+#include "base/check.h"
+#include "content/common/private_aggregation_host.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
 namespace shared_storage_worklet {
 
 SharedStorageWorkletServiceImpl::SharedStorageWorkletServiceImpl(
@@ -15,11 +22,15 @@
 
 SharedStorageWorkletServiceImpl::~SharedStorageWorkletServiceImpl() = default;
 
-void SharedStorageWorkletServiceImpl::BindSharedStorageWorkletServiceClient(
+void SharedStorageWorkletServiceImpl::Initialize(
     mojo::PendingAssociatedRemote<mojom::SharedStorageWorkletServiceClient>
-        client) {
+        client,
+    mojo::PendingRemote<content::mojom::PrivateAggregationHost>
+        private_aggregation_host) {
   DCHECK(!global_scope_);
   client_.Bind(std::move(client));
+  if (private_aggregation_host)
+    private_aggregation_host_.Bind(std::move(private_aggregation_host));
 }
 
 void SharedStorageWorkletServiceImpl::AddModule(
@@ -28,9 +39,10 @@
     const GURL& script_source_url,
     AddModuleCallback callback) {
   DCHECK(!global_scope_);
-  GetGlobalScope()->AddModule(std::move(pending_url_loader_factory),
-                              client_.get(), script_source_url,
-                              std::move(callback));
+  GetGlobalScope()->AddModule(
+      std::move(pending_url_loader_factory), client_.get(),
+      private_aggregation_host_ ? private_aggregation_host_.get() : nullptr,
+      script_source_url, std::move(callback));
 }
 
 void SharedStorageWorkletServiceImpl::RunURLSelectionOperation(
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h b/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h
index f97671c9..74bd803 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_SERVICES_SHARED_STORAGE_WORKLET_SHARED_STORAGE_WORKLET_SERVICE_IMPL_H_
 #define CONTENT_SERVICES_SHARED_STORAGE_WORKLET_SHARED_STORAGE_WORKLET_SERVICE_IMPL_H_
 
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/common/shared_storage_worklet_service.mojom.h"
 #include "content/services/shared_storage_worklet/shared_storage_worklet_global_scope.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
@@ -12,6 +13,7 @@
 #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 "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 
 namespace shared_storage_worklet {
@@ -28,21 +30,19 @@
   ~SharedStorageWorkletServiceImpl() override;
 
   // mojom::SharedStorageWorkletService implementation:
-  void BindSharedStorageWorkletServiceClient(
-      mojo::PendingAssociatedRemote<mojom::SharedStorageWorkletServiceClient>
-          client) override;
-
+  void Initialize(mojo::PendingAssociatedRemote<
+                      mojom::SharedStorageWorkletServiceClient> client,
+                  mojo::PendingRemote<content::mojom::PrivateAggregationHost>
+                      private_aggregation_host) override;
   void AddModule(mojo::PendingRemote<network::mojom::URLLoaderFactory>
                      pending_url_loader_factory,
                  const GURL& script_source_url,
                  AddModuleCallback callback) override;
-
   void RunURLSelectionOperation(
       const std::string& name,
       const std::vector<GURL>& urls,
       const std::vector<uint8_t>& serialized_data,
       RunURLSelectionOperationCallback callback) override;
-
   void RunOperation(const std::string& name,
                     const std::vector<uint8_t>& serialized_data,
                     RunOperationCallback callback) override;
@@ -69,6 +69,11 @@
   // depend on / preserve the order with messages of other types.
   mojo::AssociatedRemote<mojom::SharedStorageWorkletServiceClient> client_;
 
+  // No need to be associated as message ordering (relative to shared storage
+  // operations) is unimportant.
+  mojo::Remote<content::mojom::PrivateAggregationHost>
+      private_aggregation_host_;
+
   std::unique_ptr<SharedStorageWorkletGlobalScope> global_scope_;
 };
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
index 77c85e5f..6dbbb21 100644
--- a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
@@ -80,6 +80,9 @@
 crbug.com/1340081 [ linux release amd-0x7340 ] Maps_maps [ Failure ]
 crbug.com/1340081 [ linux release intel ] Maps_maps [ RetryOnFailure ]
 
+# Flaking frequently on Fuchsia and nobody has triaged why.
+crbug.com/1288217 [ fuchsia ] Maps_maps [ Failure ]
+
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
 #######################################################################
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 4548df0..4c3c645 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -231,6 +231,9 @@
 crbug.com/1201009 [ fuchsia ] Pixel_VideoStreamFromWebGLCanvas_OneCopy [ Skip ]
 crbug.com/1201009 [ fuchsia ] Pixel_VideoStreamFromWebGLCanvas_TwoCopy_Accelerated [ Skip ]
 
+# 5 minute timeout on ChromeOS FYI Release (amd64-generic)
+crbug.com/1167246 [ chromeos chromeos-board-amd64-generic ] Pixel_WebGLContextRestored [ Skip ]
+
 # Timeout on Nexus 5
 crbug.com/1353189 [ android android-nexus-5 ] Pixel_VideoStreamFromWebGLAlphaCanvas_TwoCopy_CpuReadback [ Skip ]
 
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index c655ad21..089ff98 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/public/common/navigation/navigation_params.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
 #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
+#include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
 #include "third_party/blink/public/mojom/frame/tree_scope_type.mojom.h"
 #include "third_party/blink/public/mojom/loader/mixed_content.mojom.h"
@@ -183,6 +184,15 @@
       child_creation_observer_.last_created_frame());
 }
 
+TestRenderFrameHost* TestRenderFrameHost::AppendAnonymousChild(
+    const std::string& frame_name) {
+  TestRenderFrameHost* rfh = AppendChildWithPolicy(frame_name, {});
+  auto attributes = blink::mojom::IframeAttributes::New();
+  attributes->anonymous = true;
+  rfh->frame_tree_node()->SetAttributes(std::move(attributes));
+  return rfh;
+}
+
 void TestRenderFrameHost::Detach() {
   if (IsFencedFrameRoot()) {
     // In production code, detaching Fenced Frames is intiated in a renderer
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index d97c8ba..753fb61 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -96,6 +96,8 @@
   TestRenderFrameHost* AppendChildWithPolicy(
       const std::string& frame_name,
       const blink::ParsedPermissionsPolicy& allow) override;
+  TestRenderFrameHost* AppendAnonymousChild(
+      const std::string& frame_name) override;
   void Detach() override;
   void SendNavigateWithTransition(int nav_entry_id,
                                   bool did_create_new_entry,
diff --git a/content/web_test/renderer/web_test_spell_checker.cc b/content/web_test/renderer/web_test_spell_checker.cc
index f6b278b6..5ada316 100644
--- a/content/web_test/renderer/web_test_spell_checker.cc
+++ b/content/web_test/renderer/web_test_spell_checker.cc
@@ -154,12 +154,15 @@
   // well-spelled words, it is easier to compare the given word with misspelled
   // ones than to compare with well-spelled ones.
   static const char* misspelled_words[] = {
-      // These words are known misspelled words in webkit tests.
-      // If there are other misspelled words in webkit tests, please add them in
+      // These words are known misspelled words in web tests.
+      // If there are other misspelled words in web tests, please add them in
       // this array.
       "foo", "Foo", "baz", "fo", "LibertyF", "chello", "xxxtestxxx", "XXxxx",
       "Textx", "blockquoted", "asd", "Lorem", "Nunc", "Curabitur", "eu", "adlj",
-      "adaasj", "sdklj", "jlkds", "jsaada", "jlda", "zz", "contentEditable",
+      "adaasj", "sdklj", "jlkds", "jsaada", "jlda", "contentEditable",
+      // Prefer to match the full word than a partial word when there's an
+      // ambiguous boundary.
+      "zz't", "zz",
       // The following words are used by unit tests.
       "ifmmp", "qwertyuiopasd", "qwertyuiopasdf", "upper case", "wellcome"};
 
diff --git a/docs/speed/binary_size/fuchsia_binary_size_trybot.md b/docs/speed/binary_size/fuchsia_binary_size_trybot.md
index fd2cb3b..d9778078 100644
--- a/docs/speed/binary_size/fuchsia_binary_size_trybot.md
+++ b/docs/speed/binary_size/fuchsia_binary_size_trybot.md
@@ -68,10 +68,6 @@
   }
 }
 ```
-  - If the compressed size grew by less than 100kB, and the
-    uncompressed size decreased, then **ignore** this failure. Add a
-    [footer](#skipping-the-check) to
-    the CL (see below) to document this (and ignore this failure).
   - If `cast_runner` grew in size, you may need assistance from
   the Chrome-Fuchsia team (fuchsia-dev@chromium.org).
 - If you are writing a new feature or including a new library, consider:
@@ -109,8 +105,7 @@
 
 Add a **footer** to the commit description along the lines of:
 
-- `Fuchsia-Binary-Size: Size increase is unavoidable (see above).`
-- `Fuchsia-Binary-Size: Uncompressed size actually decreased.`
+- `Fuchsia-Binary-Size: Size increase is unavoidable.`
 - `Fuchsia-Binary-Size: Increase is temporary.`
 - `Fuchsia-Binary-Size: See commit description.` <-- use this if longer
 than one line.
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index db99e0a..5ca2524 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -39,6 +39,8 @@
 
 * [chromium_presubmit](https://ci.chromium.org/p/chromium/builders/try/chromium_presubmit) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""chromium_presubmit"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""chromium_presubmit""))
 
+* [fuchsia-binary-size](https://ci.chromium.org/p/chromium/builders/try/fuchsia-binary-size) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""fuchsia-binary-size"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""fuchsia-binary-size""))
+
 * [fuchsia-x64-cast](https://ci.chromium.org/p/chromium/builders/try/fuchsia-x64-cast) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""fuchsia-x64-cast"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""fuchsia-x64-cast""))
 
 * [fuchsia_arm64](https://ci.chromium.org/p/chromium/builders/try/fuchsia_arm64) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""fuchsia_arm64"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""fuchsia_arm64""))
@@ -497,9 +499,6 @@
 * [android-pie-arm64-coverage-experimental-rel](https://ci.chromium.org/p/chromium/builders/try/android-pie-arm64-coverage-experimental-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-pie-arm64-coverage-experimental-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-pie-arm64-coverage-experimental-rel""))
   * Experiment percentage: 3.0
 
-* [fuchsia-binary-size](https://ci.chromium.org/p/chromium/builders/try/fuchsia-binary-size) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""fuchsia-binary-size"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""fuchsia-binary-size""))
-  * Experiment percentage: 100.0
-
 * [linux-1mbu-compile-fyi-rel](https://ci.chromium.org/p/chromium/builders/try/linux-1mbu-compile-fyi-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux-1mbu-compile-fyi-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""linux-1mbu-compile-fyi-rel""))
   * Experiment percentage: 5.0
 
diff --git a/infra/config/generated/cq-usage/default.cfg b/infra/config/generated/cq-usage/default.cfg
index 236eb67..52fd7f7e 100644
--- a/infra/config/generated/cq-usage/default.cfg
+++ b/infra/config/generated/cq-usage/default.cfg
@@ -50,6 +50,9 @@
         disable_reuse: true
       }
       builders {
+        name: "chromium/try/fuchsia-binary-size"
+      }
+      builders {
         name: "chromium/try/fuchsia-x64-cast"
       }
       builders {
diff --git a/infra/config/generated/cq-usage/full.cfg b/infra/config/generated/cq-usage/full.cfg
index ef5466ba..8b88479 100644
--- a/infra/config/generated/cq-usage/full.cfg
+++ b/infra/config/generated/cq-usage/full.cfg
@@ -283,6 +283,12 @@
         location_regexp_exclude: ".+/[+]/infra/config/.+"
       }
       builders {
+        name: "chromium/try/fuchsia-binary-size"
+        location_regexp: ".*"
+        location_regexp_exclude: ".+/[+]/docs/.+"
+        location_regexp_exclude: ".+/[+]/infra/config/.+"
+      }
+      builders {
         name: "chromium/try/fuchsia-compile-x64-dbg"
         location_regexp: ".+/[+]/base/fuchsia/.+"
         location_regexp: ".+/[+]/fuchsia/.+"
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 77fee60..0f9abe4 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1871,7 +1871,6 @@
       }
       builders {
         name: "chromium/try/fuchsia-binary-size"
-        experiment_percentage: 100
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 5b168f7..ad794dbd 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -45334,7 +45334,7 @@
   name: "flakiness"
   acls {
     role: WRITER
-    group: "google/chrome-flakiness@google.com"
+    group: "mdb/chrome-flakiness"
   }
   acls {
     group: "all"
@@ -49936,6 +49936,10 @@
     role: SCHEDULER
     identity: "user:gbeaty@google.com"
   }
+  acls {
+    role: SCHEDULER
+    identity: "user:reviver-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
   swarming {
     builders {
       name: "coordinator"
@@ -49972,7 +49976,7 @@
         '        "project": "chromium"'
         '      },'
         '      "dimensions": {'
-        '        "builderless": 1,'
+        '        "builderless": "1",'
         '        "cpu": "x86-64",'
         '        "free_space": "standard",'
         '        "os": "Ubuntu-18.04",'
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index dcb7d08..b3d9d9f1 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -315,7 +315,7 @@
   }
   bindings {
     role: "role/buildbucket.owner"
-    principals: "group:google/chrome-flakiness@google.com"
+    principals: "group:mdb/chrome-flakiness"
   }
   bindings {
     role: "role/buildbucket.reader"
@@ -484,6 +484,7 @@
   bindings {
     role: "role/buildbucket.triggerer"
     principals: "user:gbeaty@google.com"
+    principals: "user:reviver-builder@chops-service-accounts.iam.gserviceaccount.com"
   }
 }
 realms {
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
index 7d35d49..f79aafab 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -60,9 +60,7 @@
             ],
         },
     },
-    tryjob = try_.job(
-        experiment_percentage = 100,
-    ),
+    tryjob = try_.job(),
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/flakiness/flakiness.star b/infra/config/subprojects/flakiness/flakiness.star
index c2b4fe8..59eeb9f2 100644
--- a/infra/config/subprojects/flakiness/flakiness.star
+++ b/infra/config/subprojects/flakiness/flakiness.star
@@ -31,7 +31,7 @@
         ),
         acl.entry(
             roles = acl.BUILDBUCKET_OWNER,
-            groups = "google/chrome-flakiness@google.com",
+            groups = "mdb/chrome-flakiness",
         ),
     ],
 )
diff --git a/infra/config/subprojects/reviver/reviver.star b/infra/config/subprojects/reviver/reviver.star
index de3e386f..4c6126f 100644
--- a/infra/config/subprojects/reviver/reviver.star
+++ b/infra/config/subprojects/reviver/reviver.star
@@ -17,7 +17,10 @@
             roles = acl.BUILDBUCKET_TRIGGERER,
             # TODO(crbug/1346396) Switch this to something more sensible once
             # the builders are verified
-            users = "gbeaty@google.com",
+            users = [
+                "gbeaty@google.com",
+                "reviver-builder@chops-service-accounts.iam.gserviceaccount.com",
+            ],
         ),
         acl.entry(
             roles = acl.BUILDBUCKET_OWNER,
@@ -43,7 +46,7 @@
             "bucket": "ci",
             "builder": name,
         },
-        "dimensions": dimensions,
+        "dimensions": {k: str(v) for k, v in dimensions.items()},
     }
 
 builder(
diff --git a/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.h b/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.h
index 589bae6..18eba6d 100644
--- a/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.h
+++ b/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.h
@@ -68,7 +68,7 @@
   // ShoppingPersistedDataTabHelper.
   const PriceDrop* GetPriceDrop();
 
-  // Log metrics for a given |price_drop_log_id|
+  // Log metrics for a given `price_drop_log_id`
   void LogMetrics(PriceDropLogId price_drop_log_id);
 
  private:
@@ -86,7 +86,7 @@
                                     int64_t previous_price_micros);
 
   // Converts price from micros to a string according to the
-  // |currency_formatter| currency code and locale.
+  // `currency_formatter` currency code and locale.
   static std::u16string FormatPrice(
       payments::CurrencyFormatter* currency_formatter,
       long price_micros);
@@ -102,21 +102,21 @@
       optimization_guide::OptimizationGuideDecision decision,
       const optimization_guide::OptimizationMetadata& metadata);
 
-  // Acquires payments::CurrencyFormatter from |currency_formatter_map_|
+  // Acquires payments::CurrencyFormatter from `currency_formatter_map_`
   payments::CurrencyFormatter* GetCurrencyFormatter(
       const std::string& currency_code,
       const std::string& locale_name);
 
   // Parses the proto acquired from OptimizationGuide and stores in
-  // |price_drop_|.
+  // `price_drop_`.
   void ParseProto(
       const GURL& url,
       const absl::optional<commerce::PriceTrackingData>& price_metadata);
 
-  // Resets |price_drop_| when it is determined to no longer be valid.
+  // Resets `price_drop_` when it is determined to no longer be valid.
   void ResetPriceDrop();
 
-  // Sets the |price_drop_|. Only intended for testing purposes.
+  // Sets the `price_drop_`. Only intended for testing purposes.
   void SetPriceDropForTesting(std::unique_ptr<PriceDrop> price_drop) {
     price_drop_ = std::move(price_drop);
   }
diff --git a/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.mm b/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.mm
index a9a4ead..9beb0b9 100644
--- a/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.mm
+++ b/ios/chrome/browser/commerce/shopping_persisted_data_tab_helper.mm
@@ -226,7 +226,7 @@
 ShoppingPersistedDataTabHelper::GetCurrencyFormatter(
     const std::string& currency_code,
     const std::string& locale_name) {
-  // Create a currency formatter for |currency_code|, or if already created
+  // Create a currency formatter for `currency_code`, or if already created
   // return the cached version.
   std::pair<std::map<std::string, payments::CurrencyFormatter>::iterator, bool>
       emplace_result = currency_formatter_map_.emplace(
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h
index 31a53d4..54fe29f 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h
@@ -21,27 +21,27 @@
 // Name of Overlay initial presentation event.
 extern const char kBreadcrumbOverlay[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay was re-activated rather
+// Appended to `kBreadcrumbOverlay` event if overlay was re-activated rather
 // than presented for the first time (f.e. the user has switched to a tab with
 // an overlay).
 extern const char kBreadcrumbOverlayActivated[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is Http Authentication.
+// Appended to `kBreadcrumbOverlay` event if overlay is Http Authentication.
 extern const char kBreadcrumbOverlayHttpAuth[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is generic app dialog.
+// Appended to `kBreadcrumbOverlay` event if overlay is generic app dialog.
 extern const char kBreadcrumbOverlayAlert[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is app launch confirmation.
+// Appended to `kBreadcrumbOverlay` event if overlay is app launch confirmation.
 extern const char kBreadcrumbOverlayAppLaunch[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is JavaScript alert.
+// Appended to `kBreadcrumbOverlay` event if overlay is JavaScript alert.
 extern const char kBreadcrumbOverlayJsAlert[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is JavaScript confirm.
+// Appended to `kBreadcrumbOverlay` event if overlay is JavaScript confirm.
 extern const char kBreadcrumbOverlayJsConfirm[];
 
-// Appended to |kBreadcrumbOverlay| event if overlay is JavaScript prompt.
+// Appended to `kBreadcrumbOverlay` event if overlay is JavaScript prompt.
 extern const char kBreadcrumbOverlayJsPrompt[];
 
 class BreadcrumbManagerBrowserAgent
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent_unittest.mm
index 4fcfcea..64a7a556 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent_unittest.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent_unittest.mm
@@ -76,7 +76,7 @@
 };
 
 // Tests that an event logged by the BrowserAgent is returned with events for
-// the associated |browser_state_|.
+// the associated `browser_state_`.
 TEST_F(BreadcrumbManagerBrowserAgentTest, LogEvent) {
   ASSERT_EQ(0ul, breadcrumb_service_->GetEvents(0).size());
 
@@ -89,13 +89,13 @@
 
 // Tests that events logged through BrowserAgents associated with different
 // Browser instances are returned with events for the associated
-// |browser_state_| and are uniquely identifiable.
+// `browser_state_` and are uniquely identifiable.
 TEST_F(BreadcrumbManagerBrowserAgentTest, MultipleBrowsers) {
   ASSERT_EQ(0ul, breadcrumb_service_->GetEvents(0).size());
 
   BreadcrumbManagerBrowserAgent::CreateForBrowser(browser_.get());
 
-  // Insert WebState into |browser|.
+  // Insert WebState into `browser`.
   InsertWebState(browser_.get());
 
   // Create and setup second Browser.
@@ -103,7 +103,7 @@
       std::make_unique<TestBrowser>(browser_state_.get());
   BreadcrumbManagerBrowserAgent::CreateForBrowser(browser2.get());
 
-  // Insert WebState into |browser2|.
+  // Insert WebState into `browser2`.
   InsertWebState(browser2.get());
 
   std::list<std::string> events = breadcrumb_service_->GetEvents(0);
@@ -115,8 +115,8 @@
   std::size_t browser2_split_pos = events.back().find("Insert");
   // The start of the string must be unique to differentiate the associated
   // Browser object by including the BreadcrumbManagerBrowserAgent's
-  // |unique_id_|.
-  // (The Timestamp will match due to TimeSource::MOCK_TIME in the |task_env_|.)
+  // `unique_id_`.
+  // (The Timestamp will match due to TimeSource::MOCK_TIME in the `task_env_`.)
   std::string browser1_start = events.front().substr(browser1_split_pos);
   std::string browser2_start = events.back().substr(browser2_split_pos);
   EXPECT_STRNE(browser1_start.c_str(), browser2_start.c_str());
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.h
index 45c050ef..2e197b8 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.h
@@ -15,7 +15,7 @@
 class WebState;
 }  // namespace web
 
-// Handles logging of Breadcrumb events associated with |web_state_|.
+// Handles logging of Breadcrumb events associated with `web_state_`.
 class BreadcrumbManagerTabHelper
     : public breadcrumbs::BreadcrumbManagerTabHelper,
       public web::WebStateObserver,
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
index fb82df5..519b48fc 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_tab_helper.mm
@@ -95,9 +95,9 @@
   const bool is_scroll_event =
       event.find(breadcrumbs::kBreadcrumbScroll) != std::string::npos;
   if (!is_scroll_event) {
-    // |sequentially_scrolled_| is incremented for each scroll event and reset
+    // `sequentially_scrolled_` is incremented for each scroll event and reset
     // here when non-scrolling event is logged. The user can scroll multiple
-    // times and |sequentially_scrolled_| will allow to throttle the logs to
+    // times and `sequentially_scrolled_` will allow to throttle the logs to
     // avoid polluting breadcrumbs.
     sequentially_scrolled_ = 0;
   }
diff --git a/ios/chrome/browser/discover_feed/discover_feed_observer_bridge.h b/ios/chrome/browser/discover_feed/discover_feed_observer_bridge.h
index c06de613..8f10448d 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_observer_bridge.h
+++ b/ios/chrome/browser/discover_feed/discover_feed_observer_bridge.h
@@ -22,7 +22,7 @@
 
 @end
 
-// Bridge class that listens for |DiscoverFeedService| notifications and
+// Bridge class that listens for `DiscoverFeedService` notifications and
 // passes them to its Objective-C delegate.
 class DiscoverFeedObserverBridge : public DiscoverFeedObserver {
  public:
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.h b/ios/chrome/browser/discover_feed/discover_feed_service.h
index e26db81..41c47d86 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.h
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.h
@@ -54,8 +54,8 @@
   virtual UIViewController* NewFollowingFeedViewControllerWithConfiguration(
       DiscoverFeedViewControllerConfiguration* configuration) = 0;
 
-  // Removes the Discover |feed_view_controller|. It should be called whenever
-  // |feed_view_controller| will no longer be used.
+  // Removes the Discover `feed_view_controller`. It should be called whenever
+  // `feed_view_controller` will no longer be used.
   virtual void RemoveFeedViewController(
       UIViewController* feed_view_controller) = 0;
 
diff --git a/ios/chrome/browser/discover_feed/feed_model_configuration.h b/ios/chrome/browser/discover_feed/feed_model_configuration.h
index 4a4d07d..c0dbe52 100644
--- a/ios/chrome/browser/discover_feed/feed_model_configuration.h
+++ b/ios/chrome/browser/discover_feed/feed_model_configuration.h
@@ -17,15 +17,15 @@
 // Creates the configuration for a Discover feed.
 + (instancetype)discoverFeedModelConfiguration;
 
-// Creates the configuration for a Following feed with a given |sortType|.
+// Creates the configuration for a Following feed with a given `sortType`.
 + (instancetype)followingModelConfigurationWithSortType:
     (FollowingFeedSortType)sortType;
 
 // The type of feed to be created.
 @property(nonatomic, readonly) FeedType feedType;
 
-// The sorting order for the Following feed. Only used if |feedType| is
-// the Following feed. Otherwise, returns |FollowingFeedSortTypeUndefined|.
+// The sorting order for the Following feed. Only used if `feedType` is
+// the Following feed. Otherwise, returns `FollowingFeedSortTypeUndefined`.
 @property(nonatomic, readonly) FollowingFeedSortType followingFeedSortType;
 
 @end
diff --git a/ios/chrome/browser/discover_feed/feed_model_configuration.mm b/ios/chrome/browser/discover_feed/feed_model_configuration.mm
index f80a032..78628bbde 100644
--- a/ios/chrome/browser/discover_feed/feed_model_configuration.mm
+++ b/ios/chrome/browser/discover_feed/feed_model_configuration.mm
@@ -28,7 +28,7 @@
 
 #pragma mark - Private
 
-// Initializes |self| with a |feedType| and a |sortType|.
+// Initializes `self` with a `feedType` and a `sortType`.
 - (instancetype)initWithFeedType:(FeedType)feedType
            followingFeedSortType:(FollowingFeedSortType)sortType {
   if (self = [super init]) {
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index f94cfd9..68608d6 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -318,6 +318,8 @@
     "policy_app_interface_stub.mm",
     "policy_earl_grey_utils.h",
     "policy_earl_grey_utils.mm",
+    "scoped_policy_list.cc",
+    "scoped_policy_list.h",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/policy/policy_earl_grey_utils.h b/ios/chrome/browser/policy/policy_earl_grey_utils.h
index a581165..135667103 100644
--- a/ios/chrome/browser/policy/policy_earl_grey_utils.h
+++ b/ios/chrome/browser/policy/policy_earl_grey_utils.h
@@ -5,7 +5,6 @@
 #ifndef IOS_CHROME_BROWSER_POLICY_POLICY_EARL_GREY_UTILS_H_
 #define IOS_CHROME_BROWSER_POLICY_POLICY_EARL_GREY_UTILS_H_
 
-#import <Foundation/Foundation.h>
 #import <string>
 
 namespace base {
@@ -14,6 +13,11 @@
 
 namespace policy_test_utils {
 
+// Returns a JSON-encoded representation of the value for the given
+// `policy_key`. Looks for the policy in the platform policy provider under the
+// CHROME policy namespace.
+std::string GetValueForPlatformPolicy(const std::string& policy_key);
+
 // Sets the value of the policy with the |policy_key| key to the given boolean
 // value.
 void SetPolicy(bool enabled, const std::string& policy_key);
diff --git a/ios/chrome/browser/policy/policy_earl_grey_utils.mm b/ios/chrome/browser/policy/policy_earl_grey_utils.mm
index 42158e5..f8e01b17 100644
--- a/ios/chrome/browser/policy/policy_earl_grey_utils.mm
+++ b/ios/chrome/browser/policy/policy_earl_grey_utils.mm
@@ -26,6 +26,13 @@
 
 namespace policy_test_utils {
 
+std::string GetValueForPlatformPolicy(const std::string& policy_key) {
+  NSString* policy_key_as_nsstring = base::SysUTF8ToNSString(policy_key);
+  NSString* value_as_nsstring =
+      [PolicyAppInterface valueForPlatformPolicy:policy_key_as_nsstring];
+  return base::SysNSStringToUTF8(value_as_nsstring);
+}
+
 void SetPolicy(bool enabled, const std::string& policy_key) {
   SetPolicy(base::Value(enabled), policy_key);
 }
diff --git a/ios/chrome/browser/policy/scoped_policy_list.cc b/ios/chrome/browser/policy/scoped_policy_list.cc
new file mode 100644
index 0000000..b8bb31d
--- /dev/null
+++ b/ios/chrome/browser/policy/scoped_policy_list.cc
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/policy/scoped_policy_list.h"
+#import "ios/chrome/browser/policy/policy_earl_grey_utils.h"
+
+ScopedPolicyList::ScopedPolicyList() {}
+
+ScopedPolicyList::~ScopedPolicyList() {
+  Reset();
+}
+
+void ScopedPolicyList::SetPolicy(int value, const std::string& policy_key) {
+  // First check if this `policy` has been set using this object before.
+  // If not, store the current policy value before changing it.
+  const auto stored_original_value =
+      original_value_for_policy_.find(policy_key);
+  if (stored_original_value == original_value_for_policy_.end()) {
+    const auto original_value =
+        policy_test_utils::GetValueForPlatformPolicy(policy_key);
+    original_value_for_policy_.insert({policy_key, original_value});
+  }
+
+  policy_test_utils::SetPolicy(value, policy_key);
+}
+
+void ScopedPolicyList::Reset() {
+  for (const auto& policy_key_value : original_value_for_policy_) {
+    const auto& policy_key = policy_key_value.first;
+    const auto& original_value = policy_key_value.second;
+    policy_test_utils::SetPolicy(original_value, policy_key);
+  }
+}
diff --git a/ios/chrome/browser/policy/scoped_policy_list.h b/ios/chrome/browser/policy/scoped_policy_list.h
new file mode 100644
index 0000000..07588881
--- /dev/null
+++ b/ios/chrome/browser/policy/scoped_policy_list.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_POLICY_SCOPED_POLICY_LIST_H_
+#define IOS_CHROME_BROWSER_POLICY_SCOPED_POLICY_LIST_H_
+
+#include <string>
+#include <unordered_map>
+
+// `ScopedPolicyList` modifies the block policies through `PolicyAppInterface`
+// and resets these policies to their original values when it goes out of scope.
+class ScopedPolicyList {
+ public:
+  ScopedPolicyList();
+  ~ScopedPolicyList();
+
+  // Sets the value of the policy with the `policy_key` to the given integer
+  // value.
+  void SetPolicy(int value, const std::string& policy_key);
+
+  // Reset all policies which have been set to their original value
+  // i.e. the value they had when they were first set.
+  void Reset();
+
+ private:
+  std::unordered_map<std::string, std::string> original_value_for_policy_;
+};
+
+#endif  // IOS_CHROME_BROWSER_POLICY_SCOPED_POLICY_LIST_H_
diff --git a/ios/chrome/browser/segmentation_platform/BUILD.gn b/ios/chrome/browser/segmentation_platform/BUILD.gn
index 7b14432..ea4d35a 100644
--- a/ios/chrome/browser/segmentation_platform/BUILD.gn
+++ b/ios/chrome/browser/segmentation_platform/BUILD.gn
@@ -6,8 +6,6 @@
 
 source_set("segmentation_platform") {
   sources = [
-    "model_provider_factory_impl.h",
-    "model_provider_factory_impl.mm",
     "otr_web_state_observer.h",
     "otr_web_state_observer.mm",
     "segmentation_platform_config.h",
@@ -23,6 +21,7 @@
     "//components/optimization_guide/core",
     "//components/optimization_guide/core:features",
     "//components/prefs",
+    "//components/segmentation_platform/embedder",
     "//components/segmentation_platform/embedder/default_model",
     "//components/segmentation_platform/internal",
     "//components/segmentation_platform/internal/proto",
diff --git a/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.h b/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.h
deleted file mode 100644
index 29c8efc..0000000
--- a/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
-#define IOS_CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
-
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "base/task/sequenced_task_runner.h"
-#include "components/segmentation_platform/public/model_provider.h"
-#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
-
-namespace optimization_guide {
-class OptimizationGuideModelProvider;
-}
-
-namespace segmentation_platform {
-
-// Implements model provider factory for segmentation, which is the source of
-// models for segmentation service.
-class ModelProviderFactoryImpl : public ModelProviderFactory {
- public:
-  ModelProviderFactoryImpl(
-      optimization_guide::OptimizationGuideModelProvider*
-          optimization_guide_provider,
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
-
-  ~ModelProviderFactoryImpl() override;
-
-  ModelProviderFactoryImpl(ModelProviderFactoryImpl&) = delete;
-  ModelProviderFactoryImpl& operator=(ModelProviderFactoryImpl&) = delete;
-
-  // ModelProviderFactory impl:
-  std::unique_ptr<ModelProvider> CreateProvider(
-      proto::SegmentId segment_id) override;
-  std::unique_ptr<ModelProvider> CreateDefaultProvider(
-      proto::SegmentId segment_id) override;
-
- private:
-  raw_ptr<optimization_guide::OptimizationGuideModelProvider>
-      optimization_guide_provider_;
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-};
-
-}  // namespace segmentation_platform
-
-#endif  // IOS_CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
diff --git a/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.mm b/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.mm
deleted file mode 100644
index b474d1e..0000000
--- a/ios/chrome/browser/segmentation_platform/model_provider_factory_impl.mm
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/segmentation_platform/model_provider_factory_impl.h"
-
-#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
-#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
-#include "components/segmentation_platform/public/model_provider.h"
-#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
-#include "ios/chrome/browser/segmentation_platform/segmentation_platform_config.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace segmentation_platform {
-namespace {
-
-class DummyModelProvider : public ModelProvider {
- public:
-  DummyModelProvider()
-      : ModelProvider(proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {}
-  void InitAndFetchModel(
-      const ModelUpdatedCallback& model_updated_callback) override {}
-
-  void ExecuteModelWithInput(const std::vector<float>& inputs,
-                             ExecutionCallback callback) override {
-    std::move(callback).Run(absl::nullopt);
-  }
-
-  bool ModelAvailable() override { return false; }
-};
-
-}  // namespace
-
-ModelProviderFactoryImpl::ModelProviderFactoryImpl(
-    optimization_guide::OptimizationGuideModelProvider*
-        optimization_guide_provider,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
-    : optimization_guide_provider_(optimization_guide_provider),
-      background_task_runner_(background_task_runner) {}
-
-ModelProviderFactoryImpl::~ModelProviderFactoryImpl() = default;
-
-std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateProvider(
-    proto::SegmentId segment_id) {
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-  if (!optimization_guide_provider_) {
-    // Optimization guide may not be available in some tests,
-    return std::make_unique<DummyModelProvider>();
-  }
-  return std::make_unique<OptimizationGuideSegmentationModelProvider>(
-      optimization_guide_provider_, background_task_runner_, segment_id);
-#else
-  return std::make_unique<DummyModelProvider>();
-#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-}
-
-std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateDefaultProvider(
-    proto::SegmentId segment_id) {
-  return GetDefaultModelProvider(segment_id);
-}
-
-}  // namespace segmentation_platform
diff --git a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.h b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.h
index 6cd2e0c1..e8b60c0 100644
--- a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.h
+++ b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.h
@@ -18,9 +18,6 @@
 // Returns a Config created from the finch feature params.
 std::vector<std::unique_ptr<Config>> GetSegmentationPlatformConfig();
 
-// Returns a default model provider for the `target`.
-std::unique_ptr<ModelProvider> GetDefaultModelProvider(proto::SegmentId target);
-
 // Implementation of FieldTrialRegister that uses synthetic field trials to
 // record segmentation groups.
 class IOSFieldTrialRegisterImpl : public FieldTrialRegister {
diff --git a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm
index ab5899e..1bd1768 100644
--- a/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm
+++ b/ios/chrome/browser/segmentation_platform/segmentation_platform_config.mm
@@ -41,7 +41,8 @@
       stats::OptimizationTargetToHistogramVariant(segment_id);
   config->segments.insert(
       {segment_id,
-       std::make_unique<Config::SegmentMetadata>(feed_segment_name)});
+       std::make_unique<Config::SegmentMetadata>(
+           feed_segment_name, std::make_unique<FeedUserSegment>())});
   config->segment_selection_ttl =
       base::Days(base::GetFieldTrialParamByFeatureAsInt(
           features::kSegmentationPlatformFeedSegmentFeature,
@@ -70,16 +71,6 @@
   return configs;
 }
 
-std::unique_ptr<ModelProvider> GetDefaultModelProvider(
-    proto::SegmentId target) {
-  if (target == proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
-    return std::make_unique<FeedUserSegment>();
-  }
-
-  // Add default models here.
-  return nullptr;
-}
-
 IOSFieldTrialRegisterImpl::IOSFieldTrialRegisterImpl() = default;
 IOSFieldTrialRegisterImpl::~IOSFieldTrialRegisterImpl() = default;
 
diff --git a/ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.mm b/ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.mm
index ba70561..707bec95 100644
--- a/ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.mm
+++ b/ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.mm
@@ -2,31 +2,31 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
+#import "ios/chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
 
-#include "base/feature_list.h"
-#include "base/scoped_observation.h"
-#include "base/supports_user_data.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/time/default_clock.h"
-#include "components/keyed_service/core/service_access_type.h"
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "components/segmentation_platform/internal/dummy_ukm_data_manager.h"
-#include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
-#include "components/segmentation_platform/internal/ukm_data_manager.h"
-#include "components/segmentation_platform/public/config.h"
-#include "components/segmentation_platform/public/features.h"
-#include "ios/chrome/browser/application_context.h"
-#include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/history/history_service_factory.h"
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
-#include "ios/chrome/browser/segmentation_platform/model_provider_factory_impl.h"
-#include "ios/chrome/browser/segmentation_platform/otr_web_state_observer.h"
-#include "ios/chrome/browser/segmentation_platform/segmentation_platform_config.h"
+#import "base/feature_list.h"
+#import "base/scoped_observation.h"
+#import "base/supports_user_data.h"
+#import "base/task/sequenced_task_runner.h"
+#import "base/task/task_traits.h"
+#import "base/task/thread_pool.h"
+#import "base/time/default_clock.h"
+#import "components/keyed_service/core/service_access_type.h"
+#import "components/keyed_service/ios/browser_state_dependency_manager.h"
+#import "components/segmentation_platform/embedder/model_provider_factory_impl.h"
+#import "components/segmentation_platform/internal/dummy_ukm_data_manager.h"
+#import "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
+#import "components/segmentation_platform/internal/ukm_data_manager.h"
+#import "components/segmentation_platform/public/config.h"
+#import "components/segmentation_platform/public/features.h"
+#import "ios/chrome/browser/application_context.h"
+#import "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/history/history_service_factory.h"
+#import "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
+#import "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
+#import "ios/chrome/browser/segmentation_platform/otr_web_state_observer.h"
+#import "ios/chrome/browser/segmentation_platform/segmentation_platform_config.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -102,12 +102,12 @@
   params->db_provider = protodb_provider;
   params->clock = base::DefaultClock::GetInstance();
 
-  params->model_provider = std::make_unique<ModelProviderFactoryImpl>(
-      optimization_guide, params->task_runner);
   params->ukm_data_manager = GetUkmDataManager();
   params->profile_prefs = chrome_browser_state->GetPrefs();
 
   params->configs = GetSegmentationPlatformConfig();
+  params->model_provider = std::make_unique<ModelProviderFactoryImpl>(
+      optimization_guide, params->configs, params->task_runner);
   params->field_trial_register = std::make_unique<IOSFieldTrialRegisterImpl>();
 
   auto service =
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow.mm b/ios/chrome/browser/ui/authentication/authentication_flow.mm
index d8f4567..7c7ba29 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow.mm
@@ -98,12 +98,10 @@
   AuthenticationState _state;
   BOOL _didSignIn;
   BOOL _failedOrCancelled;
-  BOOL _shouldSignIn;
   BOOL _shouldSignOut;
   // YES if the signed in account is a managed account and the sign-in flow
   // includes sync.
   BOOL _shouldShowManagedConfirmation;
-  BOOL _shouldCommitSync;
   // YES if user policies have to be fetched.
   BOOL _shouldFetchUserPolicy;
 
@@ -214,10 +212,7 @@
     case BEGIN:
       return CHECK_SIGNIN_STEPS;
     case CHECK_SIGNIN_STEPS:
-      if (_shouldSignIn)
-        return FETCH_MANAGED_STATUS;
-      else
-        return CHECK_MERGE_CASE;
+      return FETCH_MANAGED_STATUS;
     case FETCH_MANAGED_STATUS:
       return CHECK_MERGE_CASE;
     case CHECK_MERGE_CASE:
@@ -231,19 +226,15 @@
         return SIGN_OUT_IF_NEEDED;
       else if (self.localDataClearingStrategy == SHOULD_CLEAR_DATA_CLEAR_DATA)
         return CLEAR_DATA;
-      else if (_shouldSignIn)
-        return SIGN_IN;
       else
-        return COMPLETE_WITH_SUCCESS;
+        return SIGN_IN;
     case SHOW_MANAGED_CONFIRMATION:
       if (_shouldSignOut)
         return SIGN_OUT_IF_NEEDED;
       else if (self.localDataClearingStrategy == SHOULD_CLEAR_DATA_CLEAR_DATA)
         return CLEAR_DATA;
-      else if (_shouldSignIn)
-        return SIGN_IN;
       else
-        return COMPLETE_WITH_SUCCESS;
+        return SIGN_IN;
     case SIGN_OUT_IF_NEEDED:
       return self.localDataClearingStrategy == SHOULD_CLEAR_DATA_CLEAR_DATA
                  ? CLEAR_DATA
@@ -251,10 +242,12 @@
     case CLEAR_DATA:
       return SIGN_IN;
     case SIGN_IN:
-      if (_shouldCommitSync)
-        return COMMIT_SYNC;
-      else
-        return COMPLETE_WITH_SUCCESS;
+      switch (_postSignInAction) {
+        case POST_SIGNIN_ACTION_COMMIT_SYNC:
+          return COMMIT_SYNC;
+        case POST_SIGNIN_ACTION_NONE:
+          return COMPLETE_WITH_SUCCESS;
+      }
     case COMMIT_SYNC:
       if (policy::IsUserPolicyEnabled() && _shouldFetchUserPolicy)
         return REGISTER_FOR_USER_POLICY;
@@ -416,8 +409,6 @@
     // sign-out is required.
     _shouldSignOut = YES;
   }
-  _shouldSignIn = YES;
-  _shouldCommitSync = _postSignInAction == POST_SIGNIN_ACTION_COMMIT_SYNC;
 }
 
 - (void)signInIdentity:(ChromeIdentity*)identity {
@@ -456,7 +447,7 @@
     bool isManagedAccount = _identityToSignInHostedDomain.length > 0;
     signin_metrics::RecordSigninAccountType(signin::ConsentLevel::kSignin,
                                             isManagedAccount);
-    if (_shouldCommitSync)
+    if (_postSignInAction == POST_SIGNIN_ACTION_COMMIT_SYNC)
       signin_metrics::RecordSigninAccountType(signin::ConsentLevel::kSync,
                                               isManagedAccount);
   }
diff --git a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
index 0fd8d960..8c6ad8d 100644
--- a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
+++ b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
@@ -480,10 +480,10 @@
 
       // Configures fonts for titled layout.
       self.titleLabel.font =
-          [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+          [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];
       self.titleLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
       self.textLabel.font =
-          [UIFont preferredFontForTextStyle:UIFontTextStyleCallout];
+          [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
       self.textLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
 
       // In the standard layout, the button has a background.
diff --git a/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn b/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
index 2e8ab699..4b89df3 100644
--- a/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
+++ b/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
@@ -64,7 +64,10 @@
   testonly = true
   sources = [ "incognito_interstitial_egtest.mm" ]
   deps = [
+    "//components/policy:generated",
     "//ios/chrome/browser:pref_names",
+    "//ios/chrome/browser/policy:eg_test_support+eg2",
+    "//ios/chrome/browser/policy:policy_util",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/test:eg_test_support+eg2",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
diff --git a/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_egtest.mm b/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_egtest.mm
index 83ffa6d5..aeda6b7d 100644
--- a/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_egtest.mm
+++ b/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_egtest.mm
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 
 #import "base/strings/sys_string_conversions.h"
+#import "components/policy/policy_constants.h"
+#import "ios/chrome/browser/policy/policy_earl_grey_utils.h"
+#import "ios/chrome/browser/policy/policy_util.h"
+#import "ios/chrome/browser/policy/scoped_policy_list.h"
 #import "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -25,7 +29,9 @@
 @interface IncognitoInterstitialTestCase : ChromeTestCase
 @end
 
-@implementation IncognitoInterstitialTestCase
+@implementation IncognitoInterstitialTestCase {
+  ScopedPolicyList scopedPolicies;
+}
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config = [super appConfigurationForTestCase];
@@ -38,10 +44,16 @@
   [ChromeEarlGrey setBoolValue:YES
                    forUserPref:prefs::kIncognitoInterstitialEnabled];
 
+  // Set Incognito Mode to "available",
+  // as this is an assumption for most of these tests.
+  scopedPolicies.SetPolicy(static_cast<int>(IncognitoModePrefs::kEnabled),
+                           policy::key::kIncognitoModeAvailability);
+
   GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
 }
 
 - (void)tearDown {
+  scopedPolicies.Reset();
   [ChromeEarlGrey setBoolValue:NO
                    forUserPref:prefs::kIncognitoInterstitialEnabled];
   [super tearDown];
@@ -227,4 +239,56 @@
   [ChromeEarlGrey waitForIncognitoTabCount:1];
 }
 
+// Test the interstitial is not presented when Incognito Mode is disabled
+// through Enterprise policy.
+- (void)testInterstitialIsNotPresentedWhenIncognitoModeIsDisabled {
+  // Disabling Incognito mode.
+  ScopedPolicyList scopedIncognitoModeDisabled;
+  scopedIncognitoModeDisabled.SetPolicy(
+      static_cast<int>(IncognitoModePrefs::kDisabled),
+      policy::key::kIncognitoModeAvailability);
+
+  // Close the NTP to go to the tab switcher.
+  [ChromeEarlGrey closeCurrentTab];
+
+  // Starting from NTP, loading a new URL.
+  GURL destinationURL = self.testServer->GetURL("/destination.html");
+  [ChromeEarlGrey sceneOpenURL:destinationURL];
+  // Wait for the expected page content to be displayed.
+  double timeout = 10.0;
+  if (@available(iOS 16.0, *)) {
+    timeout = 20.0;
+  }
+  [ChromeEarlGrey waitForWebStateContainingText:"You've arrived"
+                                        timeout:timeout];
+  // Wait for the Incognito tab count to be one, as expected.
+  [ChromeEarlGrey waitForMainTabCount:1];
+}
+
+// Test the interstitial is not presented when Incognito Mode is forced
+// through Enterprise policy.
+- (void)testInterstitialIsNotPresentedWhenIncognitoModeIsForced {
+  // Forcing Incognito mode.
+  ScopedPolicyList scopedIncognitoModeForced;
+  scopedIncognitoModeForced.SetPolicy(
+      static_cast<int>(IncognitoModePrefs::kForced),
+      policy::key::kIncognitoModeAvailability);
+
+  // Close the NTP to go to the tab switcher.
+  [ChromeEarlGrey closeCurrentTab];
+
+  // Starting from NTP, loading a new URL.
+  GURL destinationURL = self.testServer->GetURL("/destination.html");
+  [ChromeEarlGrey sceneOpenURL:destinationURL];
+  // Wait for the expected page content to be displayed.
+  double timeout = 10.0;
+  if (@available(iOS 16.0, *)) {
+    timeout = 20.0;
+  }
+  [ChromeEarlGrey waitForWebStateContainingText:"You've arrived"
+                                        timeout:timeout];
+  // Wait for the Incognito tab count to be one, as expected.
+  [ChromeEarlGrey waitForIncognitoTabCount:1];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
index b4c1278..4158fa6 100644
--- a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
@@ -752,14 +752,14 @@
   [self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
                                                   kTappedFollowButton
                                 asInteraction:NO];
-  base::RecordAction(base::UserMetricsAction("MobileMenuUnfollow"));
+  base::RecordAction(base::UserMetricsAction("MobileMenuFollow"));
 }
 
 - (void)recordUnfollowFromMenu {
   [self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
                                                   kTappedUnfollowButton
                                 asInteraction:NO];
-  base::RecordAction(base::UserMetricsAction("MobileMenuFollow"));
+  base::RecordAction(base::UserMetricsAction("MobileMenuUnfollow"));
 }
 
 - (void)recordFollowConfirmationShownWithType:
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller_unittest.mm
index f7cb5c52..6507930 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller_unittest.mm
@@ -19,6 +19,7 @@
 #import "components/sync/driver/mock_sync_service.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
 #include "components/sync_preferences/pref_service_syncable.h"
+#import "components/sync_preferences/testing_pref_service_syncable.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/main/test_browser.h"
@@ -57,12 +58,13 @@
                               error:nil];
 }
 
-// Bitset
-typedef NS_ENUM(NSUInteger, PrivacyTableViewControllerTestConfig) {
+struct PrivacyTableViewControllerTestConfig {
   // Tests should run with Enhanced Protection flag enabled.
-  PrivacyTableViewControllerTestConfigEnhancedProtectionEnabled = 1 << 0,
+  bool enhancedProtectionEnabled;
   // Tests should run with Third-party intents in Incognito flag enabled.
-  PrivacyTableViewControllerTestConfig3PIntentsInIncognitoEnabled = 1 << 1,
+  bool thirdPartyIntentsInIncognitoEnabled;
+  // Available of Incognito mode tests should run with.
+  IncognitoModePrefs incognitoModeAvailability;
 };
 
 // `ScopedFeatureList` wrapper so `PrivacyTableViewControllerTest` can ensure
@@ -93,8 +95,7 @@
         enabledDisabledFeatures;
 
     // Explicitly enable/disable Enhanced Protection flag.
-    if (GetParam() &
-        PrivacyTableViewControllerTestConfigEnhancedProtectionEnabled) {
+    if (GetParam().enhancedProtectionEnabled) {
       enabledDisabledFeatures.first.push_back(
           safe_browsing::kEnhancedProtection);
     } else {
@@ -103,8 +104,7 @@
     }
 
     // Explicitly enable/disable Third-party intents in Incognito flag.
-    if (GetParam() &
-        PrivacyTableViewControllerTestConfigEnhancedProtectionEnabled) {
+    if (GetParam().thirdPartyIntentsInIncognitoEnabled) {
       enabledDisabledFeatures.first.push_back(kIOS3PIntentsInIncognito);
     } else {
       enabledDisabledFeatures.second.push_back(kIOS3PIntentsInIncognito);
@@ -117,7 +117,6 @@
     ChromeTableViewControllerTest::SetUp();
 
     TestChromeBrowserState::Builder test_cbs_builder;
-    test_cbs_builder.SetPrefService(CreatePrefService());
     test_cbs_builder.AddTestingFactory(
         SyncServiceFactory::GetInstance(),
         base::BindRepeating(&BuildMockSyncService));
@@ -129,6 +128,12 @@
     initialValueForSpdyProxyEnabled_ =
         [[defaults valueForKey:kSpdyProxyEnabled] copy];
     [defaults setValue:@"Disabled" forKey:kSpdyProxyEnabled];
+
+    // Set Incognito Mode availability depending on test config.
+    chrome_browser_state_->GetTestingPrefService()->SetManagedPref(
+        prefs::kIncognitoModeAvailability,
+        std::make_unique<base::Value>(
+            static_cast<int>(GetParam().incognitoModeAvailability)));
   }
 
   void TearDown() override {
@@ -143,15 +148,6 @@
     ChromeTableViewControllerTest::TearDown();
   }
 
-  // Makes a PrefService to be used by the test.
-  std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
-    scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
-        new user_prefs::PrefRegistrySyncable);
-    RegisterBrowserStatePrefs(registry.get());
-    sync_preferences::PrefServiceMockFactory factory;
-    return factory.CreateSyncable(registry.get());
-  }
-
   ChromeTableViewController* InstantiateController() override {
     return [[PrivacyTableViewController alloc] initWithBrowser:browser_.get()
                                         reauthenticationModule:nil];
@@ -335,10 +331,29 @@
     PrivacyTableViewControllerTestAllConfigs,
     PrivacyTableViewControllerTest,
     testing::Values(
-        0,
-        PrivacyTableViewControllerTestConfigEnhancedProtectionEnabled,
-        PrivacyTableViewControllerTestConfig3PIntentsInIncognitoEnabled,
-        PrivacyTableViewControllerTestConfigEnhancedProtectionEnabled |
-            PrivacyTableViewControllerTestConfig3PIntentsInIncognitoEnabled));
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ false,
+            /* thirdPartyIntentsInIncognitoEnabled= */ false,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kEnabled},
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ true,
+            /* thirdPartyIntentsInIncognitoEnabled= */ false,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kEnabled},
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ false,
+            /* thirdPartyIntentsInIncognitoEnabled= */ true,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kEnabled},
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ true,
+            /* thirdPartyIntentsInIncognitoEnabled= */ true,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kEnabled},
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ true,
+            /* thirdPartyIntentsInIncognitoEnabled= */ true,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kDisabled},
+        PrivacyTableViewControllerTestConfig{
+            /* enhancedProtectionEnabled= */ true,
+            /* thirdPartyIntentsInIncognitoEnabled= */ true,
+            /* incognitoModeAvailability= */ IncognitoModePrefs::kForced}));
 
 }  // namespace
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 2d16323..b034cbcd 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 @@
-0d93ead6cbadbd2bbe69b1b1418b6b96e3a826e3
\ No newline at end of file
+3fb8ff183a344864202cc255d8f92e7692a9ae49
\ 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 b8aa67a..db40265 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 @@
-d274270cc718a3160e5bf51dd4600d9b085fac81
\ No newline at end of file
+199926c8ce192ea4c4e9c007a2421ec27b8b43ea
\ 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 9877cb5f..04a29d0 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 @@
-8f56d23a22438a3e1c1f7c659596d2aeffbb625b
\ No newline at end of file
+07c4a9c9fea90110ff16ca032afe81c86a5b6a60
\ 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 5c9b68f..a470b03 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 @@
-d49b006d0fcdd2db7d64de52d29e30054d6b44fd
\ No newline at end of file
+4ebb3940ab188a77e298cb92e149b86cf826bad1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index f9a5f4d0..54653c90 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-bd683aff4d11756b84422d688f7c3fa2e6213cea
\ No newline at end of file
+8925f7ef6103fd4115b14b5409dbc375882017e9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index af4a032..ef5b9b76 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a97aa9b93e7001d1dc49e69493acdb3fe87133b4
\ No newline at end of file
+5aa0e56e23520b2ec25ffbbba31d39bcd2598dba
\ 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 f248c449..c89c49d 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 @@
-0527fe31f706fb7f5e2c8bd821963bcd26b155d7
\ No newline at end of file
+b85a95bc7af21b678678a92e18bf91a2779aa612
\ 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 ebb9e200..1b2ca7f 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 @@
-a82311bb8b9714305e20a48ec2b7ee2d9e37c3ba
\ No newline at end of file
+ec0bae9e9b59a879667301fa2646085aa7a9fb29
\ 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 dd240aa..0dce4176 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 @@
-7cd4e6f45691223d12a506561bd1664c963c03c4
\ No newline at end of file
+f4207bea355bf834758644ea9e45ba54d74dcc0b
\ 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 17219a1..57bfba1 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 @@
-3df93084769e270e1bc472165cd82fe04a151516
\ No newline at end of file
+daa53dd4bd8dad8d5808e75f9864932866b606fb
\ No newline at end of file
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index bffa2a5..e9c0cfc 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -61,6 +61,9 @@
   "public/cwv_flags.h",
   "public/cwv_html_element.h",
   "public/cwv_identity.h",
+  "public/cwv_leak_check_credential.h",
+  "public/cwv_leak_check_service.h",
+  "public/cwv_leak_check_service_observer.h",
   "public/cwv_lookalike_url_handler.h",
   "public/cwv_metrics_provider.h",
   "public/cwv_navigation_action.h",
@@ -177,10 +180,16 @@
     "internal/metrics/cwv_metrics_provider.mm",
     "internal/metrics/cwv_metrics_provider_internal.h",
     "internal/passwords/cwv_credential_provider_extension_utils.mm",
+    "internal/passwords/cwv_leak_check_credential.mm",
+    "internal/passwords/cwv_leak_check_credential_internal.h",
+    "internal/passwords/cwv_leak_check_service.mm",
+    "internal/passwords/cwv_leak_check_service_internal.h",
     "internal/passwords/cwv_password.mm",
     "internal/passwords/cwv_password_internal.h",
     "internal/passwords/web_view_account_password_store_factory.h",
     "internal/passwords/web_view_account_password_store_factory.mm",
+    "internal/passwords/web_view_bulk_leak_check_service_factory.h",
+    "internal/passwords/web_view_bulk_leak_check_service_factory.mm",
     "internal/passwords/web_view_password_change_success_tracker_factory.h",
     "internal/passwords/web_view_password_change_success_tracker_factory.mm",
     "internal/passwords/web_view_password_feature_manager.h",
@@ -311,6 +320,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser:affiliation",
     "//components/password_manager/core/browser:password_generator",
+    "//components/password_manager/core/browser/leak_detection:leak_detection",
     "//components/password_manager/core/common",
     "//components/password_manager/core/common:features",
     "//components/password_manager/ios",
@@ -482,6 +492,8 @@
     "internal/cwv_x509_certificate_unittest.mm",
     "internal/metrics/cwv_metrics_provider_unittest.mm",
     "internal/passwords/cwv_credential_provider_extension_utils_unittest.mm",
+    "internal/passwords/cwv_leak_check_credential_unittest.mm",
+    "internal/passwords/cwv_leak_check_service_unittest.mm",
     "internal/passwords/cwv_password_unittest.mm",
     "internal/passwords/web_view_password_manager_client_unittest.mm",
     "internal/safe_browsing/cwv_unsafe_url_handler_unittest.mm",
@@ -509,6 +521,7 @@
     "//components/autofill/ios/form_util:test_support",
     "//components/invalidation/impl:test_support",
     "//components/password_manager/core/browser:test_support",
+    "//components/password_manager/core/browser/leak_detection:test_support",
     "//components/prefs:test_support",
     "//components/signin/public/base:test_support",
     "//components/signin/public/identity_manager:test_support",
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 4b00c34..63b53cd 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -8,6 +8,7 @@
 
 #include "base/threading/thread_restrictions.h"
 #include "components/keyed_service/core/service_access_type.h"
+#import "components/password_manager/core/browser/bulk_leak_check_service_interface.h"
 #include "components/password_manager/core/browser/password_store_interface.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/web_view/internal/app/application_context.h"
@@ -16,7 +17,9 @@
 #import "ios/web_view/internal/cwv_preferences_internal.h"
 #import "ios/web_view/internal/cwv_user_content_controller_internal.h"
 #import "ios/web_view/internal/cwv_web_view_internal.h"
+#import "ios/web_view/internal/passwords/cwv_leak_check_service_internal.h"
 #import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h"
+#import "ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h"
 #include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
 #import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
 #import "ios/web_view/internal/sync/web_view_sync_service_factory.h"
@@ -46,6 +49,7 @@
 @implementation CWVWebViewConfiguration
 
 @synthesize autofillDataManager = _autofillDataManager;
+@synthesize leakCheckService = _leakCheckService;
 @synthesize preferences = _preferences;
 @synthesize syncController = _syncController;
 @synthesize userContentController = _userContentController;
@@ -159,6 +163,19 @@
   return _syncController;
 }
 
+#pragma mark - LeakCheckService
+
+- (CWVLeakCheckService*)leakCheckService {
+  if (!_leakCheckService && self.persistent) {
+    password_manager::BulkLeakCheckServiceInterface* bulkLeakCheckService =
+        ios_web_view::WebViewBulkLeakCheckServiceFactory::GetForBrowserState(
+            self.browserState);
+    _leakCheckService = [[CWVLeakCheckService alloc]
+        initWithBulkLeakCheckService:bulkLeakCheckService];
+  }
+  return _leakCheckService;
+}
+
 #pragma mark - Public Methods
 
 - (BOOL)isPersistent {
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_credential.mm b/ios/web_view/internal/passwords/cwv_leak_check_credential.mm
new file mode 100644
index 0000000..14641c0
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_credential.mm
@@ -0,0 +1,82 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h"
+
+#import "base/strings/sys_string_conversions.h"
+#import "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+#import "components/password_manager/core/browser/password_form.h"
+#import "ios/web_view/internal/passwords/cwv_password_internal.h"
+#import "ios/web_view/internal/utils/nsobject_description_utils.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CWVLeakCheckCredential {
+  std::unique_ptr<password_manager::LeakCheckCredential> _internalCredential;
+}
+
++ (CWVLeakCheckCredential*)canonicalLeakCheckCredentialWithPassword:
+    (CWVPassword*)password {
+  // Canonicalization referenced from:
+  // components/password_manager/core/browser/ui/credential_utils.h
+  auto canonical_credential =
+      std::make_unique<password_manager::LeakCheckCredential>(
+          password_manager::CanonicalizeUsername(
+              password.internalPasswordForm->username_value),
+          password.internalPasswordForm->password_value);
+
+  return [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::move(canonical_credential)];
+}
+
+- (instancetype)initWithCredential:
+    (std::unique_ptr<password_manager::LeakCheckCredential>)credential {
+  self = [super init];
+  DCHECK(credential);
+  if (self) {
+    _internalCredential = std::move(credential);
+  }
+  return self;
+}
+
+- (const password_manager::LeakCheckCredential&)internalCredential {
+  return *_internalCredential;
+}
+
+- (NSString*)username {
+  return base::SysUTF16ToNSString(_internalCredential->username());
+}
+
+- (NSString*)password {
+  return base::SysUTF16ToNSString(_internalCredential->password());
+}
+
+#pragma mark - NSObject
+
+- (BOOL)isEqual:(id)object {
+  if (![object isKindOfClass:[self class]]) {
+    return NO;
+  }
+  if (object == self) {
+    return YES;
+  }
+  CWVLeakCheckCredential* other = (CWVLeakCheckCredential*)object;
+  return [other.username isEqualToString:self.username] &&
+         [other.password isEqualToString:self.password];
+}
+
+- (NSUInteger)hash {
+  return self.username.hash ^ self.password.hash;
+}
+
+#pragma mark - NSCopying
+
+- (id)copyWithZone:(NSZone*)zone {
+  // Object is immutable.
+  return self;
+}
+
+@end
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h b/ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h
new file mode 100644
index 0000000..299e22d
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_CREDENTIAL_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_CREDENTIAL_INTERNAL_H_
+
+#include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
+#import "ios/web_view/public/cwv_leak_check_credential.h"
+
+@interface CWVLeakCheckCredential ()
+
+// Initializer to wrap the provided credential. User data associated with the
+// provided credential will not be passed to the leak check service.
+- (instancetype)initWithCredential:
+    (std::unique_ptr<password_manager::LeakCheckCredential>)credential
+    NS_DESIGNATED_INITIALIZER;
+
+// The internal credential we wrap. This is intentionally not declared as a
+// property to hide it from [NSObject valueForKey:]
+- (const password_manager::LeakCheckCredential&)internalCredential;
+
+@end
+
+#endif  // IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_CREDENTIAL_INTERNAL_H_
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_credential_unittest.mm b/ios/web_view/internal/passwords/cwv_leak_check_credential_unittest.mm
new file mode 100644
index 0000000..d0db04ed
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_credential_unittest.mm
@@ -0,0 +1,103 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h"
+
+#import "base/strings/utf_string_conversions.h"
+#import "components/password_manager/core/browser/password_form.h"
+#import "ios/web_view/internal/passwords/cwv_password_internal.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using password_manager::LeakCheckCredential;
+
+namespace ios_web_view {
+
+class CWVLeakCheckCredentialTest : public PlatformTest {
+ protected:
+  // Creates a CWVPassword for testing its conversion to a
+  // CWVLeakCheckCredential.
+  CWVPassword* CreateCWVPassword(std::u16string username,
+                                 std::u16string password) {
+    password_manager::PasswordForm password_form;
+    password_form.url = GURL("http://www.example.com/accounts/LoginAuth");
+    password_form.action = GURL("http://www.example.com/accounts/Login");
+    password_form.username_element = u"Email";
+    password_form.username_value = username;
+    password_form.password_element = u"Passwd";
+    password_form.password_value = password;
+    password_form.submit_element = u"signIn";
+    password_form.signon_realm = "http://www.example.com/";
+    password_form.scheme = password_manager::PasswordForm::Scheme::kHtml;
+    password_form.blocked_by_user = false;
+    password_form.encrypted_password = base::UTF16ToUTF8(password);
+
+    return [[CWVPassword alloc] initWithPasswordForm:password_form];
+  }
+};
+
+// Tests that the internal credential matches the username/password fields of
+// the CWVPassword used to initialize it.
+TEST_F(CWVLeakCheckCredentialTest, CWVPasswordInitialization) {
+  CWVLeakCheckCredential* credential = [CWVLeakCheckCredential
+      canonicalLeakCheckCredentialWithPassword:CreateCWVPassword(u"username",
+                                                                 u"password")];
+
+  EXPECT_EQ(credential.internalCredential.username(), u"username");
+  EXPECT_EQ(credential.internalCredential.password(), u"password");
+}
+
+// Test that canonicalization is applied by seeing if a username with a domain
+// and without are regarded as equivalent.
+TEST_F(CWVLeakCheckCredentialTest, CWVPasswordInitializationCanonical) {
+  CWVPassword* password = CreateCWVPassword(u"username", u"password");
+  CWVPassword* password_with_domain =
+      CreateCWVPassword(u"username@google.com", u"password");
+
+  EXPECT_NSEQ(
+      [CWVLeakCheckCredential
+          canonicalLeakCheckCredentialWithPassword:password],
+      [CWVLeakCheckCredential
+          canonicalLeakCheckCredentialWithPassword:password_with_domain]);
+}
+
+// Tests that isEquals and hash methods are equivalent for different objects
+// with equivalent credentials.
+TEST_F(CWVLeakCheckCredentialTest, Equals) {
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(u"username",
+                                                               u"password")];
+  CWVLeakCheckCredential* same_credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(u"username",
+                                                               u"password")];
+
+  EXPECT_NSEQ(credential, same_credential);
+  EXPECT_EQ(credential.hash, same_credential.hash);
+}
+
+// Tests that isEquals and hash methods are not equivalent for different objects
+// with different credentials.
+TEST_F(CWVLeakCheckCredentialTest, NotEquals) {
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(u"username",
+                                                               u"password")];
+  CWVLeakCheckCredential* different_password = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(u"username",
+                                                               u"secret")];
+  CWVLeakCheckCredential* different_username = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(u"bob",
+                                                               u"password")];
+
+  EXPECT_NSNE(credential, different_password);
+  EXPECT_NE(credential.hash, different_password.hash);
+  EXPECT_NSNE(credential, different_username);
+  EXPECT_NE(credential.hash, different_username.hash);
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_service.mm b/ios/web_view/internal/passwords/cwv_leak_check_service.mm
new file mode 100644
index 0000000..2d06814
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_service.mm
@@ -0,0 +1,154 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/strings/sys_string_conversions.h"
+#import "ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h"
+#import "ios/web_view/internal/passwords/cwv_leak_check_service_internal.h"
+
+#import "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
+#import "ios/web_view/public/cwv_leak_check_service_observer.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using password_manager::BulkLeakCheckServiceInterface;
+using password_manager::IsLeaked;
+using password_manager::LeakCheckCredential;
+
+// Private interface callable by the ObserverBridge.
+@interface CWVLeakCheckService ()
+
+// Called when the ObserverBridge is notified of a change in state.
+- (void)bulkLeakCheckServiceDidChangeState;
+
+// Called when the ObserverBridge is notified of a finished credential.
+// |credential| The credential that finished.
+// |isLeaked| Whether the credential was leaked or not.
+- (void)bulkLeakCheckServiceDidFinishCredential:
+            (const LeakCheckCredential&)credential
+                                       isLeaked:(BOOL)isLeaked;
+
+@end
+
+namespace ios_web_view {
+
+// C++ to ObjC bridge for BulkLeakCheckServiceInterface::Observer.
+class ObserverBridge : public BulkLeakCheckServiceInterface::Observer {
+ public:
+  ObserverBridge(CWVLeakCheckService* service) : service_(service) {
+    DCHECK(service_);
+  }
+
+  void OnStateChanged(
+      BulkLeakCheckServiceInterface::State internalState) override {
+    [service_ bulkLeakCheckServiceDidChangeState];
+  }
+
+  void OnCredentialDone(const LeakCheckCredential& credential,
+                        IsLeaked is_leaked) override {
+    [service_ bulkLeakCheckServiceDidFinishCredential:credential
+                                             isLeaked:*is_leaked];
+  }
+
+ private:
+  __weak CWVLeakCheckService* service_;
+};
+
+}  // namespace ios_web_view
+
+@implementation CWVLeakCheckService {
+  BulkLeakCheckServiceInterface* _bulkLeakCheckService;
+  std::unique_ptr<ios_web_view::ObserverBridge> _observerBridge;
+  NSHashTable<id<CWVLeakCheckServiceObserver>>* _observers;
+}
+
+- (instancetype)initWithBulkLeakCheckService:
+    (BulkLeakCheckServiceInterface*)bulkLeakCheckService {
+  DCHECK(bulkLeakCheckService);
+  self = [super init];
+  if (self) {
+    _bulkLeakCheckService = bulkLeakCheckService;
+    _observerBridge = std::make_unique<ios_web_view::ObserverBridge>(self);
+    _bulkLeakCheckService->AddObserver(_observerBridge.get());
+    _observers = [NSHashTable weakObjectsHashTable];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _bulkLeakCheckService->RemoveObserver(_observerBridge.get());
+}
+
+- (CWVLeakCheckServiceState)state {
+  switch (_bulkLeakCheckService->GetState()) {
+    case BulkLeakCheckServiceInterface::State::kIdle:
+      return CWVLeakCheckServiceStateIdle;
+    case BulkLeakCheckServiceInterface::State::kRunning:
+      return CWVLeakCheckServiceStateRunning;
+    case BulkLeakCheckServiceInterface::State::kCanceled:
+      return CWVLeakCheckServiceStateCanceled;
+    case BulkLeakCheckServiceInterface::State::kSignedOut:
+      return CWVLeakCheckServiceStateSignedOut;
+    case BulkLeakCheckServiceInterface::State::kTokenRequestFailure:
+      return CWVLeakCheckServiceStateTokenRequestFailure;
+    case BulkLeakCheckServiceInterface::State::kHashingFailure:
+      return CWVLeakCheckServiceStateHashingFailure;
+    case BulkLeakCheckServiceInterface::State::kNetworkError:
+      return CWVLeakCheckServiceStateNetworkError;
+    case BulkLeakCheckServiceInterface::State::kServiceError:
+      return CWVLeakCheckServiceStateServiceError;
+    case BulkLeakCheckServiceInterface::State::kQuotaLimit:
+      return CWVLeakCheckServiceStateQuotaLimit;
+  }
+}
+
+- (void)addObserver:(__weak id<CWVLeakCheckServiceObserver>)observer {
+  [_observers addObject:observer];
+}
+
+- (void)removeObserver:(__weak id<CWVLeakCheckServiceObserver>)observer {
+  [_observers removeObject:observer];
+}
+
+- (void)checkCredentials:(NSArray<CWVLeakCheckCredential*>*)credentials {
+  std::vector<LeakCheckCredential> internalCredentials;
+  if (!credentials.count)
+    return;
+
+  for (CWVLeakCheckCredential* credential in credentials) {
+    internalCredentials.emplace_back(credential.internalCredential.username(),
+                                     credential.internalCredential.password());
+  }
+
+  _bulkLeakCheckService->CheckUsernamePasswordPairs(
+      std::move(internalCredentials));
+}
+
+- (void)cancel {
+  _bulkLeakCheckService->Cancel();
+}
+
+- (void)bulkLeakCheckServiceDidChangeState {
+  for (id<CWVLeakCheckServiceObserver> observer in _observers) {
+    [observer leakCheckServiceDidChangeState:self];
+  }
+}
+
+- (void)bulkLeakCheckServiceDidFinishCredential:
+            (const LeakCheckCredential&)internalCredential
+                                       isLeaked:(BOOL)isLeaked {
+  auto internalCredentialCopy = std::make_unique<LeakCheckCredential>(
+      internalCredential.username(), internalCredential.password());
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::move(internalCredentialCopy)];
+
+  for (id<CWVLeakCheckServiceObserver> observer in _observers) {
+    [observer leakCheckService:self
+            didCheckCredential:credential
+                      isLeaked:isLeaked];
+  }
+}
+
+@end
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_service_internal.h b/ios/web_view/internal/passwords/cwv_leak_check_service_internal.h
new file mode 100644
index 0000000..1d6c252
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_service_internal.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_SERVICE_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_SERVICE_INTERNAL_H_
+
+#include "components/password_manager/core/browser/bulk_leak_check_service_interface.h"
+#import "ios/web_view/public/cwv_leak_check_service.h"
+
+@interface CWVLeakCheckService ()
+
+- (instancetype)initWithBulkLeakCheckService:
+    (password_manager::BulkLeakCheckServiceInterface*)service
+    NS_DESIGNATED_INITIALIZER;
+
+@end
+
+#endif  // IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_LEAK_CHECK_SERVICE_INTERNAL_H_
diff --git a/ios/web_view/internal/passwords/cwv_leak_check_service_unittest.mm b/ios/web_view/internal/passwords/cwv_leak_check_service_unittest.mm
new file mode 100644
index 0000000..35e7889
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_leak_check_service_unittest.mm
@@ -0,0 +1,225 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/strings/sys_string_conversions.h"
+#import "base/test/task_environment.h"
+#import "components/password_manager/core/browser/bulk_leak_check_service.h"
+#import "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
+#import "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
+#import "components/password_manager/core/browser/leak_detection/mock_leak_detection_check_factory.h"
+#import "components/signin/public/identity_manager/identity_test_environment.h"
+#import "ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h"
+#import "ios/web_view/internal/passwords/cwv_leak_check_service_internal.h"
+#import "ios/web_view/public/cwv_leak_check_service_observer.h"
+#import "services/network/test/test_shared_url_loader_factory.h"
+
+#import "testing/gtest/include/gtest/gtest.h"
+
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using password_manager::BulkLeakCheck;
+using password_manager::BulkLeakCheckDelegateInterface;
+using password_manager::BulkLeakCheckService;
+using password_manager::IsLeaked;
+using password_manager::LeakCheckCredential;
+using password_manager::MockLeakDetectionCheckFactory;
+
+namespace ios_web_view {
+
+namespace {
+constexpr char16_t kUsername1[] = u"bob";
+constexpr char16_t kPassword1[] = u"password";
+constexpr char16_t kUsername2[] = u"alice";
+constexpr char16_t kPassword2[] = u"secret";
+}  // namespace
+
+// A Fake BulkLeakCheck to avoid going to the network.
+class FakeBulkLeakCheck : public BulkLeakCheck {
+ public:
+  FakeBulkLeakCheck(BulkLeakCheckDelegateInterface* delegate)
+      : delegate_(delegate) {}
+
+  void CheckCredentials(std::vector<LeakCheckCredential> checks) override {
+    std::move(checks.begin(), checks.end(), std::back_inserter(queue_));
+  }
+
+  size_t GetPendingChecksCount() const override { return queue_.size(); }
+
+  // Test helper to see what credentials have been queued.
+  std::deque<LeakCheckCredential> const& queue() const { return queue_; }
+
+  // Test helper to finish and notify the next check has completed.
+  // |is_leaked| The fake leak check result for the check.
+  void FinishNext(IsLeaked is_leaked) {
+    DCHECK(!queue_.empty());
+    auto check = std::move(queue_.front());
+    queue_.pop_front();
+    delegate_->OnFinishedCredential(std::move(check), is_leaked);
+  }
+
+ private:
+  BulkLeakCheckDelegateInterface* delegate_;
+  std::deque<LeakCheckCredential> queue_;
+};
+
+// A stub for MockLeakDetectionCheckFactory::TryCreateBulkLeakCheck that creates
+// a FakeBulkLeakCheck and assigns its raw pointer to *out for the test to
+// control.
+ACTION_P(CreateFakeBulkLeakCheck, out) {
+  auto value = std::make_unique<FakeBulkLeakCheck>(arg0);
+  *out = value.get();
+  return std::move(value);
+}
+
+MATCHER_P(CredentialsAre, credentials, "") {
+  return std::equal(arg.begin(), arg.end(), credentials.get().begin(),
+                    credentials.get().end(),
+                    [](const auto& lhs, const auto& rhs) {
+                      return lhs.username() == rhs.username() &&
+                             lhs.password() == rhs.password();
+                    });
+}
+
+class CWVLeakCheckServiceTest : public PlatformTest {
+ public:
+  CWVLeakCheckServiceTest() {
+    // Use a mock leak detection factory so we can inject our fake.
+    auto mock_leak_factory = std::make_unique<MockLeakDetectionCheckFactory>();
+    mock_leak_factory_ = mock_leak_factory.get();
+    service_interface_.set_leak_factory(std::move(mock_leak_factory));
+    service_ = [[CWVLeakCheckService alloc]
+        initWithBulkLeakCheckService:&service_interface_];
+  }
+
+ protected:
+  MockLeakDetectionCheckFactory& mock_leak_factory() {
+    return *mock_leak_factory_;
+  }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  signin::IdentityTestEnvironment identity_test_env_;
+  BulkLeakCheckService service_interface_{
+      identity_test_env_.identity_manager(),
+      base::MakeRefCounted<network::TestSharedURLLoaderFactory>()};
+  raw_ptr<MockLeakDetectionCheckFactory> mock_leak_factory_ = nullptr;
+  CWVLeakCheckService* service_;
+};
+
+// Tests that state initializes to idle and changes to running
+// when processing credentials.
+TEST_F(CWVLeakCheckServiceTest, State) {
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername1,
+                                                               kPassword1)];
+
+  EXPECT_EQ(CWVLeakCheckServiceStateIdle, service_.state);
+
+  FakeBulkLeakCheck* leak_check;
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential ]];
+
+  EXPECT_EQ(CWVLeakCheckServiceStateRunning, service_.state);
+}
+
+// Tests that cancel changes the state to canceled.
+TEST_F(CWVLeakCheckServiceTest, Cancel) {
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername1,
+                                                               kPassword1)];
+
+  EXPECT_EQ(CWVLeakCheckServiceStateIdle, service_.state);
+
+  FakeBulkLeakCheck* leak_check;
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential ]];
+  [service_ cancel];
+
+  EXPECT_EQ(CWVLeakCheckServiceStateCanceled, service_.state);
+}
+
+// Tests that credentials are converted to internal LeakCheckCredential.
+TEST_F(CWVLeakCheckServiceTest, PreparesCredentials) {
+  CWVLeakCheckCredential* credential1 = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername1,
+                                                               kPassword1)];
+  CWVLeakCheckCredential* credential2 = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername2,
+                                                               kPassword2)];
+
+  std::vector<LeakCheckCredential> expected;
+  expected.emplace_back(kUsername1, kPassword1);
+  expected.emplace_back(kUsername2, kPassword2);
+
+  FakeBulkLeakCheck* leak_check;
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential1, credential2 ]];
+
+  EXPECT_THAT(leak_check->queue(), CredentialsAre(std::cref(expected)));
+}
+
+// Tests that observers are notified of state changes.
+TEST_F(CWVLeakCheckServiceTest, DidChangeState) {
+  CWVLeakCheckCredential* credential = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername1,
+                                                               kPassword1)];
+
+  id observer = OCMProtocolMock(@protocol(CWVLeakCheckServiceObserver));
+
+  [service_ addObserver:observer];
+  [[observer expect] leakCheckServiceDidChangeState:service_];
+  FakeBulkLeakCheck* leak_check;
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential ]];
+  [observer verify];
+
+  leak_check->FinishNext(IsLeaked(false));
+
+  [service_ removeObserver:observer];
+  [[observer reject] leakCheckServiceDidChangeState:service_];
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential ]];
+  [observer verify];
+}
+
+// Tests that observers are notified of completed credentials.
+TEST_F(CWVLeakCheckServiceTest, DidCheckCredential) {
+  CWVLeakCheckCredential* credential1 = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername1,
+                                                               kPassword1)];
+  CWVLeakCheckCredential* credential2 = [[CWVLeakCheckCredential alloc]
+      initWithCredential:std::make_unique<LeakCheckCredential>(kUsername2,
+                                                               kPassword2)];
+
+  id observer = OCMProtocolMock(@protocol(CWVLeakCheckServiceObserver));
+  [service_ addObserver:observer];
+  [[observer expect] leakCheckService:service_
+                   didCheckCredential:credential1
+                             isLeaked:false];
+  [[observer expect] leakCheckService:service_
+                   didCheckCredential:credential2
+                             isLeaked:true];
+
+  FakeBulkLeakCheck* leak_check;
+  EXPECT_CALL(mock_leak_factory(), TryCreateBulkLeakCheck)
+      .WillOnce(CreateFakeBulkLeakCheck(&leak_check));
+  [service_ checkCredentials:@[ credential1, credential2 ]];
+
+  leak_check->FinishNext(IsLeaked(false));
+  leak_check->FinishNext(IsLeaked(true));
+
+  [observer verify];
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h b/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h
new file mode 100644
index 0000000..bc1d1c2
--- /dev/null
+++ b/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_BULK_LEAK_CHECK_SERVICE_FACTORY_H_
+#define IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_BULK_LEAK_CHECK_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace password_manager {
+class BulkLeakCheckServiceInterface;
+}
+
+namespace ios_web_view {
+
+class WebViewBrowserState;
+
+// Singleton that owns all BulkLeakCheckServices and associates them with
+// WebViewBrowserState.
+class WebViewBulkLeakCheckServiceFactory
+    : public BrowserStateKeyedServiceFactory {
+ public:
+  static WebViewBulkLeakCheckServiceFactory* GetInstance();
+  static password_manager::BulkLeakCheckServiceInterface* GetForBrowserState(
+      WebViewBrowserState* browser_state);
+
+ private:
+  friend class base::NoDestructor<WebViewBulkLeakCheckServiceFactory>;
+
+  WebViewBulkLeakCheckServiceFactory();
+  ~WebViewBulkLeakCheckServiceFactory() override;
+
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+};
+
+}  // namespace ios_web_view
+
+#endif  // IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_BULK_LEAK_CHECK_SERVICE_FACTORY_H_
diff --git a/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.mm b/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.mm
new file mode 100644
index 0000000..ec9ab08
--- /dev/null
+++ b/ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.mm
@@ -0,0 +1,60 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h"
+
+#import <memory>
+#import <utility>
+
+#import "base/no_destructor.h"
+#import "components/keyed_service/ios/browser_state_dependency_manager.h"
+#import "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#import "components/password_manager/core/browser/bulk_leak_check_service.h"
+#import "components/password_manager/core/browser/bulk_leak_check_service_interface.h"
+#import "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
+#import "ios/web_view/internal/web_view_browser_state.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+// static
+WebViewBulkLeakCheckServiceFactory*
+WebViewBulkLeakCheckServiceFactory::GetInstance() {
+  static base::NoDestructor<WebViewBulkLeakCheckServiceFactory> instance;
+  return instance.get();
+}
+
+// static
+password_manager::BulkLeakCheckServiceInterface*
+WebViewBulkLeakCheckServiceFactory::GetForBrowserState(
+    WebViewBrowserState* browser_state) {
+  return static_cast<password_manager::BulkLeakCheckServiceInterface*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+WebViewBulkLeakCheckServiceFactory::WebViewBulkLeakCheckServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "BulkLeakCheckServiceFactory",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(WebViewIdentityManagerFactory::GetInstance());
+}
+
+WebViewBulkLeakCheckServiceFactory::~WebViewBulkLeakCheckServiceFactory() =
+    default;
+
+std::unique_ptr<KeyedService>
+WebViewBulkLeakCheckServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  WebViewBrowserState* browser_state =
+      WebViewBrowserState::FromBrowserState(context);
+  return std::make_unique<password_manager::BulkLeakCheckService>(
+      WebViewIdentityManagerFactory::GetForBrowserState(browser_state),
+      browser_state->GetSharedURLLoaderFactory());
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/web_view_browser_state.mm b/ios/web_view/internal/web_view_browser_state.mm
index a42c883..8ad7d9c 100644
--- a/ios/web_view/internal/web_view_browser_state.mm
+++ b/ios/web_view/internal/web_view_browser_state.mm
@@ -42,6 +42,7 @@
 #include "ios/web_view/internal/language/web_view_language_model_manager_factory.h"
 #include "ios/web_view/internal/language/web_view_url_language_histogram_factory.h"
 #include "ios/web_view/internal/passwords/web_view_account_password_store_factory.h"
+#import "ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h"
 #import "ios/web_view/internal/passwords/web_view_password_manager_log_router_factory.h"
 #import "ios/web_view/internal/passwords/web_view_password_requirements_service_factory.h"
 #include "ios/web_view/internal/passwords/web_view_password_store_factory.h"
@@ -203,6 +204,7 @@
   WebViewProfileInvalidationProviderFactory::GetInstance();
   WebViewSyncServiceFactory::GetInstance();
   WebViewModelTypeStoreServiceFactory::GetInstance();
+  WebViewBulkLeakCheckServiceFactory::GetInstance();
 
   BrowserStateDependencyManager::GetInstance()
       ->RegisterBrowserStatePrefsForServices(pref_registry);
diff --git a/ios/web_view/public/cwv_defines.h b/ios/web_view/public/cwv_defines.h
index 4a7283f..a31b44c 100644
--- a/ios/web_view/public/cwv_defines.h
+++ b/ios/web_view/public/cwv_defines.h
@@ -69,4 +69,7 @@
 // Supports -[CWVWebView evaluateJavaScript:completion:].
 #define IOS_WEB_VIEW_SUPPORTS_MODERN_JS_EVALUATION 1
 
+// Supports -[CWVWebViewConfiguration leakCheckService].
+#define IOS_WEB_VIEW_SUPPORTS_LEAK_CHECK_SERVICE 1
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_DEFINES_H_
diff --git a/ios/web_view/public/cwv_leak_check_credential.h b/ios/web_view/public/cwv_leak_check_credential.h
new file mode 100644
index 0000000..24da49c
--- /dev/null
+++ b/ios/web_view/public/cwv_leak_check_credential.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_CREDENTIAL_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_CREDENTIAL_H_
+
+#import <Foundation/Foundation.h>
+
+#import "cwv_export.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class CWVPassword;
+
+// CWVLeakCheckCredential represents a credential to be checked with a leak
+// check service. It implements isEqual/hash/NSCopying so identical credentials
+// can be deduplicated and stored in dictionaries easily. Use the helper
+// initializers to created canonical credentials for the most efficient
+// deduplication.
+CWV_EXPORT
+@interface CWVLeakCheckCredential : NSObject <NSCopying>
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Creates a canonical credential from a CWVPassword so objects that would be
+// seen as identical by a leak checking service are equivalent objects as well.
++ (CWVLeakCheckCredential*)canonicalLeakCheckCredentialWithPassword:
+    (CWVPassword*)password;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_CREDENTIAL_H_
diff --git a/ios/web_view/public/cwv_leak_check_service.h b/ios/web_view/public/cwv_leak_check_service.h
new file mode 100644
index 0000000..7830ba9f
--- /dev/null
+++ b/ios/web_view/public/cwv_leak_check_service.h
@@ -0,0 +1,66 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_H_
+
+#import <Foundation/Foundation.h>
+
+#import "cwv_export.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class CWVLeakCheckCredential;
+@protocol CWVLeakCheckServiceObserver;
+
+// The states the LeakCheckService can be in. This mirrors:
+// components/password_manager/core/browser/bulk_leak_check_service_interface.h
+typedef NS_ENUM(NSInteger, CWVLeakCheckServiceState) {
+  // The service is idle and there was no previous error.
+  CWVLeakCheckServiceStateIdle = 0,
+  // The service is checking some credentials.
+  CWVLeakCheckServiceStateRunning,
+
+  // Those below are error states. On any error the current job is aborted.
+  // The error is sticky until next checkCredentials call.
+
+  // A call to cancel aborted the running check.
+  CWVLeakCheckServiceStateCanceled,
+  // The user isn't signed-in to Chrome.
+  CWVLeakCheckServiceStateSignedOut,
+  // Error obtaining an access token.
+  CWVLeakCheckServiceStateTokenRequestFailure,
+  // Error in hashing/encrypting for the request.
+  CWVLeakCheckServiceStateHashingFailure,
+  // Error related to network.
+  CWVLeakCheckServiceStateNetworkError,
+  // Error related to the password leak Google service.
+  CWVLeakCheckServiceStateServiceError,
+  // Error related to the quota limit of the password leak Google service.
+  CWVLeakCheckServiceStateQuotaLimit,
+};
+
+// A service for checking whether a credential has been leaked.
+CWV_EXPORT
+@interface CWVLeakCheckService : NSObject
+
+// Current state of the service.
+@property(nonatomic, readonly) CWVLeakCheckServiceState state;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Adds an observer to be notified when credentials complete or state changed.
+- (void)addObserver:(id<CWVLeakCheckServiceObserver>)observer;
+// Removes an observer previously added via addObserver.
+- (void)removeObserver:(id<CWVLeakCheckServiceObserver>)observer;
+// Adds the credentials to be checked (does not dedupe).
+- (void)checkCredentials:(NSArray<CWVLeakCheckCredential*>*)credentials;
+// Stops all the current checks immediately.
+- (void)cancel;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_H_
diff --git a/ios/web_view/public/cwv_leak_check_service_observer.h b/ios/web_view/public/cwv_leak_check_service_observer.h
new file mode 100644
index 0000000..6234c1a2
--- /dev/null
+++ b/ios/web_view/public/cwv_leak_check_service_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_OBSERVER_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_OBSERVER_H_
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class CWVLeakCheckCredential;
+
+// Protocol to receive change notifications from CWVLeakCheckService.
+@protocol CWVLeakCheckServiceObserver <NSObject>
+
+// Called whenever CWVLeakCheckService changes state.
+- (void)leakCheckServiceDidChangeState:(CWVLeakCheckService*)leakCheckService;
+
+// Called whenever an individual leak check has been completed.
+- (void)leakCheckService:(CWVLeakCheckService*)leakCheckService
+      didCheckCredential:(CWVLeakCheckCredential*)credential
+                isLeaked:(BOOL)isLeaked;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_LEAK_CHECK_SERVICE_OBSERVER_H_
diff --git a/ios/web_view/public/cwv_web_view_configuration.h b/ios/web_view/public/cwv_web_view_configuration.h
index d9b4497..0b87917 100644
--- a/ios/web_view/public/cwv_web_view_configuration.h
+++ b/ios/web_view/public/cwv_web_view_configuration.h
@@ -13,6 +13,7 @@
 
 @class CWVAutofillDataManager;
 @class CWVPreferences;
+@class CWVLeakCheckService;
 @class CWVSyncController;
 @class CWVUserContentController;
 @class CWVWebsiteDataStore;
@@ -52,6 +53,10 @@
 @property(nonatomic, readonly, nullable)
     CWVAutofillDataManager* autofillDataManager;
 
+// This web view configuration's leak check service.
+// nil if -[CWVWebViewConfiguration isPersistent] is NO.
+@property(nonatomic, readonly, nullable) CWVLeakCheckService* leakCheckService;
+
 // YES if this is a configuration with a persistent data store which stores all
 // data on disk, for example cookies.
 @property(nonatomic, readonly, getter=isPersistent) BOOL persistent;
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index e7517a54..ea2c89f 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -25,6 +25,7 @@
 
 @interface ShellViewController () <CWVAutofillDataManagerObserver,
                                    CWVDownloadTaskDelegate,
+                                   CWVLeakCheckServiceObserver,
                                    CWVNavigationDelegate,
                                    CWVUIDelegate,
                                    CWVScriptCommandHandler,
@@ -65,6 +66,11 @@
 // The newly opened popup windows e.g., by JavaScript function "window.open()",
 // HTML "<a target='_blank'>".
 @property(nonatomic, strong) NSMutableArray<CWVWebView*>* popupWebViews;
+// A list of active leak checks. These map to a list of passwords since
+// you can have multiple passwords that map to the same canonical leak check.
+@property(nonatomic, strong)
+    NSMutableDictionary<CWVLeakCheckCredential*, NSMutableArray<CWVPassword*>*>*
+        pendingLeakChecks;
 
 - (void)back;
 - (void)forward;
@@ -94,6 +100,7 @@
 @synthesize authService = _authService;
 @synthesize trustedVaultProvider = _trustedVaultProvider;
 @synthesize popupWebViews = _popupWebViews;
+@synthesize pendingLeakChecks = _pendingLeakChecks;
 
 - (void)viewDidLoad {
   [super viewDidLoad];
@@ -274,10 +281,13 @@
       [[ShellTrustedVaultProvider alloc] initWithAuthService:_authService];
   CWVSyncController.trustedVaultProvider = _trustedVaultProvider;
 
+  _pendingLeakChecks = [NSMutableDictionary dictionary];
+
   CWVWebViewConfiguration* configuration =
       [CWVWebViewConfiguration defaultConfiguration];
   [configuration.autofillDataManager addObserver:self];
   configuration.syncController.delegate = self;
+  [configuration.leakCheckService addObserver:self];
   self.webView = [self createWebViewWithConfiguration:configuration];
 }
 
@@ -681,6 +691,12 @@
                                          [weakSelf showPasswordData];
                                        }]];
   [alertController
+      addAction:[UIAlertAction actionWithTitle:@"Check leaked passwords"
+                                         style:UIAlertActionStyleDefault
+                                       handler:^(UIAlertAction* action) {
+                                         [weakSelf checkLeakedPasswords];
+                                       }]];
+  [alertController
       addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                          style:UIAlertActionStyleCancel
                                        handler:nil]];
@@ -853,6 +869,35 @@
   [self presentViewController:alertController animated:YES completion:nil];
 }
 
+- (void)checkLeakedPasswords {
+  CWVAutofillDataManager* dataManager =
+      _webView.configuration.autofillDataManager;
+
+  // Request a check for any password in autofill data manager that is not
+  // currently being requested.
+  [dataManager fetchPasswordsWithCompletionHandler:^(
+                   NSArray<CWVPassword*>* _Nonnull passwords) {
+    NSMutableArray<CWVLeakCheckCredential*>* credentialsToCheck =
+        [NSMutableArray array];
+    for (CWVPassword* password in passwords) {
+      CWVLeakCheckCredential* credential = [CWVLeakCheckCredential
+          canonicalLeakCheckCredentialWithPassword:password];
+      NSMutableArray<CWVPassword*>* passwordsForCredential =
+          self.pendingLeakChecks[credential];
+      if (!passwordsForCredential) {
+        passwordsForCredential = [NSMutableArray array];
+        self.pendingLeakChecks[credential] = passwordsForCredential;
+        [credentialsToCheck addObject:credential];
+      }
+      [passwordsForCredential addObject:password];
+    }
+
+    NSLog(@"Checking leaks for %@ credentials.", @(credentialsToCheck.count));
+    [self.webView.configuration.leakCheckService
+        checkCredentials:credentialsToCheck];
+  }];
+}
+
 - (void)showEvaluateJavaScriptUI {
   UIAlertController* alertController =
       [UIAlertController alertControllerWithTitle:@"Evaluate JavaScript"
@@ -1437,6 +1482,39 @@
   NSLog(@"%@", NSStringFromSelector(_cmd));
 }
 
+#pragma mark CWVLeakCheckServiceObserver
+
+- (void)leakCheckServiceDidChangeState:(CWVLeakCheckService*)leakCheckService {
+  NSLog(@"%@:%d", NSStringFromSelector(_cmd), (int)leakCheckService.state);
+  if (leakCheckService.state != CWVLeakCheckServiceStateRunning) {
+    [self.pendingLeakChecks removeAllObjects];
+  }
+}
+
+- (void)leakCheckService:(CWVLeakCheckService*)leakCheckService
+      didCheckCredential:(CWVLeakCheckCredential*)credential
+                isLeaked:(BOOL)isLeaked {
+  NSMutableArray<CWVPassword*>* passwordsForCredential =
+      [self.pendingLeakChecks objectForKey:credential];
+  if (!passwordsForCredential) {
+    NSLog(@"No passwords for CWVLeakCheckCredential!");
+    return;
+  }
+
+  [self.pendingLeakChecks removeObjectForKey:credential];
+
+  NSMutableArray<NSString*>* passwordDescriptions = [passwordsForCredential
+      valueForKey:NSStringFromSelector(@selector(debugDescription))];
+  NSString* passwordsDescription =
+      [passwordDescriptions componentsJoinedByString:@"\n\n"];
+  NSString* message = [NSString
+      stringWithFormat:@"Leak check returned %@ for %@ passwords:\n%@",
+                       isLeaked ? @"LEAKED" : @"OK",
+                       @(passwordsForCredential.count), passwordsDescription];
+  NSLog(@"%@", message);
+  NSLog(@"%@ Leak checks remaining...", @(self.pendingLeakChecks.count));
+}
+
 #pragma mark UIScrollViewDelegate
 
 - (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
index 2779486..da5ca206 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -84,6 +84,10 @@
 
     // These are only supported on macOS 11+.
     HEVCPROFILE_MAIN, HEVCPROFILE_MAIN10, HEVCPROFILE_MAIN_STILL_PICTURE,
+    // This is partially supported on macOS 11+, Apple Silicon Mac only supports
+    // 8 ~ 10 bit 400, 420, 422, 444 HW decoding, and Intel Mac supports 8 ~ 12
+    // bit 400, 420, 422 SW decoding, 444 content is decodable but has a green
+    // stripe issue.
     HEVCPROFILE_REXT,
 
     // TODO(sandersd): Hi10p fails during
@@ -1335,9 +1339,8 @@
 
     // 8.1.2 We only want nuh_layer_id of zero.
     if (nalu.nuh_layer_id) {
-      WriteToMediaLog(MediaLogMessageLevel::kINFO,
-                      "Skipping NALU with nuh_layer_id=");
-      DVLOG(4) << "Skipping NALU with nuh_layer_id=" << nalu.nuh_layer_id;
+      MEDIA_LOG(INFO, media_log_)
+          << "Skipping NALU with nuh_layer_id=" << nalu.nuh_layer_id;
       continue;
     }
 
@@ -1424,6 +1427,22 @@
       case H265NALU::RASL_N:
       case H265NALU::RASL_R:
       case H265NALU::CRA_NUT: {
+        // The VT session will report a OsStatus=12909 kVTVideoDecoderBadDataErr
+        // if you send a RASL frame just after a CRA frame, so we wait until the
+        // total output count is enough
+        if (output_count_for_cra_rasl_workaround_ < kMinOutputsBeforeRASL &&
+            (nalu.nal_unit_type == H265NALU::RASL_N ||
+             nalu.nal_unit_type == H265NALU::RASL_R)) {
+          continue;
+        }
+        // Just like H264, only the first slice is examined. Other slices are at
+        // least one of: the same frame, not decoded, invalid so no need to
+        // parse again.
+        if (frame->has_slice) {
+          nalus.push_back(nalu);
+          data_size += kNALUHeaderLength + nalu.size;
+          break;
+        }
         curr_slice_hdr.reset(new H265SliceHeader());
         result = hevc_parser_.ParseSliceHeader(nalu, curr_slice_hdr.get(),
                                                last_slice_hdr.get());
@@ -1445,15 +1464,6 @@
           return;
         }
 
-        // The VT session will report a OsStatus=12909 kVTVideoDecoderBadDataErr
-        // if you send a RASL frame just after a CRA frame, so we wait until the
-        // total output count is enough
-        if (output_count_for_cra_rasl_workaround_ < kMinOutputsBeforeRASL &&
-            (nalu.nal_unit_type == H265NALU::RASL_N ||
-             nalu.nal_unit_type == H265NALU::RASL_R)) {
-          continue;
-        }
-
         const H265PPS* pps =
             hevc_parser_.GetPPS(curr_slice_hdr->slice_pic_parameter_set_id);
         if (!pps) {
diff --git a/media/gpu/test/video_player/decoder_listener.h b/media/gpu/test/video_player/decoder_listener.h
index 299ebbf..ebe7b74 100644
--- a/media/gpu/test/video_player/decoder_listener.h
+++ b/media/gpu/test/video_player/decoder_listener.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
+#include "media/gpu/test/video_frame_helpers.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
@@ -24,7 +25,6 @@
 
 class FrameRendererDummy;
 class Video;
-class VideoFrameProcessor;
 class DecoderWrapper;
 struct DecoderWrapperConfig;
 
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index f5c1054a..dbb107d7 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -564,7 +564,7 @@
   auto CAPTURE_queue = std::make_unique<V4L2Queue>(
       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, kUncompressedFourcc,
       gfx::Size(file_header.width, file_header.height), /*num_planes=*/2,
-      V4L2_MEMORY_MMAP, /*num_buffers=*/10);
+      V4L2_MEMORY_MMAP, /*num_buffers=*/kNumberOfBuffersInCaptureQueue);
 
   return base::WrapUnique(
       new Av1Decoder(std::move(ivf_parser), std::move(v4l2_ioctl),
@@ -780,48 +780,59 @@
                   frm_header.buffer_removal_time);
   v4l2_frame_params->refresh_frame_flags = frm_header.refresh_frame_flags;
 
+  // The first slot in |order_hints| is reserved for intra frame, so it is not
+  // used and will always be 0.
+  // Please reference more details in the below comment for this algorithm to
+  // compute |order_hints|. In summary, we are trying to get frame number here
+  // given a specific reference frame type (L0, L1, L2, G, B, A1, A2) in
+  // the reference frames list.
+  // https://b.corp.google.com/issues/242337166#comment24
   static_assert(std::size(decltype(v4l2_frame_params->order_hints){}) ==
                     libgav1::kNumReferenceFrameTypes,
                 "Invalid size of |order_hints| array");
-  for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; i++)
-    v4l2_frame_params->order_hints[i] =
-        base::checked_cast<__u32>(frm_header.reference_order_hint[i]);
+  if (frm_header.frame_type != libgav1::kFrameKey) {
+    for (size_t i = 0; i < libgav1::kNumInterReferenceFrameTypes; i++) {
+      v4l2_frame_params->order_hints[i + 1] =
+          ref_frames_[frm_header.reference_frame_index[i]]->frame_number();
+    }
+  }
 
+  // These params looks duplicated with |ref_frame_idx|, but they are required
+  // and used when |frame_refs_short_signaling| is set according to the AV1
+  // spec. https://aomediacodec.github.io/av1-spec/#uncompressed-header-syntax
   v4l2_frame_params->last_frame_idx =
       frm_header.reference_frame_index[libgav1::kReferenceFrameLast];
   v4l2_frame_params->gold_frame_idx =
       frm_header.reference_frame_index[libgav1::kReferenceFrameGolden];
 
+  // TODO(b/230891887): use uint64_t when v4l2_timeval_to_ns() function is used.
+  constexpr uint32_t kInvalidSurface = std::numeric_limits<uint32_t>::max();
+
+  // Note that only 7 slots in the reference frames list are used
+  // although 8 slots are available.
+  for (size_t i = 0; i < libgav1::kNumInterReferenceFrameTypes; ++i) {
+    constexpr size_t kTimestampToNanoSecs = 1000;
+
+    // |reference_frame_ts| is needed to use previously decoded frames
+    // from reference frames list.
+    const auto reference_frame_ts =
+        ref_frames_[i] ? ref_frames_[i]->frame_number() * kTimestampToNanoSecs
+                       : kInvalidSurface;
+
+    v4l2_frame_params->reference_frame_ts[i] = reference_frame_ts;
+  }
+
   static_assert(std::size(decltype(v4l2_frame_params->ref_frame_idx){}) ==
                     libgav1::kNumInterReferenceFrameTypes,
                 "Invalid size of |ref_frame_idx| array");
   for (size_t i = 0; i < libgav1::kNumInterReferenceFrameTypes; i++)
     v4l2_frame_params->ref_frame_idx[i] =
-        base::checked_cast<__u64>(frm_header.reference_frame_index[i]);
+        base::checked_cast<__u8>(frm_header.reference_frame_index[i]);
 
   v4l2_frame_params->skip_mode_frame[0] =
       base::checked_cast<__u8>(frm_header.skip_mode_frame[0]);
   v4l2_frame_params->skip_mode_frame[1] =
       base::checked_cast<__u8>(frm_header.skip_mode_frame[1]);
-
-  // TODO(b/230891887): use uint64_t when v4l2_timeval_to_ns() function is used.
-  constexpr uint32_t kInvalidSurface = std::numeric_limits<uint32_t>::max();
-
-  for (size_t i = 0; i < std::size(frm_header.reference_frame_index); ++i) {
-    const auto idx = frm_header.reference_frame_index[i];
-    LOG_ASSERT(idx < kAv1NumRefFrames) << "Invalid reference frame index.\n";
-
-    constexpr size_t kTimestampToNanoSecs = 1000;
-
-    // |reference_frame_ts| is needed to use previously decoded frames
-    // from reference frames list.
-    const auto reference_frame_ts =
-        ref_frames_[idx]
-            ? ref_frames_[idx]->frame_number() * kTimestampToNanoSecs
-            : kInvalidSurface;
-
-    v4l2_frame_params->reference_frame_ts[idx] = reference_frame_ts;
-  }
 }
 
 std::set<int> Av1Decoder::RefreshReferenceSlots(
diff --git a/media/gpu/vaapi/test/decode.cc b/media/gpu/vaapi/test/decode.cc
index b0fdde52..992a465 100644
--- a/media/gpu/vaapi/test/decode.cc
+++ b/media/gpu/vaapi/test/decode.cc
@@ -48,34 +48,42 @@
 #endif
 using media_gpu_vaapi::StubPathMap;
 
-#define fourcc(a, b, c, d)                                             \
-  ((static_cast<uint32_t>(a) << 0) | (static_cast<uint32_t>(b) << 8) | \
-   (static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24))
-
 namespace {
 
 constexpr char kUsageMsg[] =
     "usage: decode_test\n"
     "           --video=<video path>\n"
+    "           --codec=<codec name>\n"
     "           [--frames=<number of frames to decode>]\n"
     "           [--fetch=<derive|get>]\n"
     "           [--out-prefix=<path prefix of decoded frame PNGs>]\n"
     "           [--md5]\n"
     "           [--visible]\n"
     "           [--loop]\n"
-#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
-    "           [--h265]\n"
-#endif
     "           [--v=<log verbosity>]\n"
     "           [--help]\n";
 
 constexpr char kHelpMsg[] =
     "This binary decodes the IVF video in <video> path with specified video\n"
     "<profile> via thinly wrapped libva calls.\n"
-    "Supported codecs: VP8, VP9 (profiles 0, 2), AV1 (profile 0), and H265\n"
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+    "Supported codecs: VP8, VP9 (profiles 0, 2), AV1 (profile 0), H264, and\n"
+    "H265.\n"
+#else
+    "Supported codecs: VP8, VP9 (profiles 0, 2), AV1 (profile 0), H264.\n"
+#endif
     "\nThe following arguments are supported:\n"
     "    --video=<path>\n"
     "        Required. Path to IVF-formatted or HEVC-formatted video.\n"
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+    "    --codec=<codec name>\n"
+    "        Required. The case-insensitive name of the codec to be used for\n"
+    "        decoding. Valid codec names are VP8, VP9, AV1, H264, and H265.\n"
+#else
+    "    --codec=<codec name>\n"
+    "        Required. The case-insensitive name of the codec to be used for\n"
+    "        decoding. Valid codec names are VP8, VP9, AV1, and H264.\n"
+#endif
     "    --frames=<int>\n"
     "        Optional. Number of frames to decode, defaults to all.\n"
     "        Override with a positive integer to decode at most that many.\n"
@@ -110,50 +118,28 @@
     "        If specified with --frames, loops decoding that number of\n"
     "        leading frames. If specified with --out-prefix, loops decoding,\n"
     "        but only saves the first iteration of decoded frames.\n"
-#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
-    "    --h265\n"
-    "        Required for H.265 files. If specified, assumes the input video\n"
-    "        is in H.265 format and selects the H.265 decoder. This is used\n"
-    "        to distinguish H.264 and H.265 videos (b/239719493).\n"
-#endif
     "    --help\n"
     "        Display this help message and exit.\n";
 
-// Returns string representation of |fourcc|.
-std::string FourccStr(uint32_t fourcc) {
-  std::stringstream s;
-  s << static_cast<char>(fourcc & 0xFF)
-    << static_cast<char>((fourcc >> 8) & 0xFF)
-    << static_cast<char>((fourcc >> 16) & 0xFF)
-    << static_cast<char>((fourcc >> 24) & 0xFF);
-  return s.str();
-}
-
-// Creates the appropriate decoder for |stream_data| which is expected to point
-// to H264 Annex B data of length |stream_len|. The decoder will use
-// |va_device| to issue VAAPI calls. Returns nullptr on failure.
+// Creates the decoder for |stream_data| based on the user-provided
+// value. If the user requests a valid codec that is not suitable
+// for decoding |stream_data| the behavior will be undefined. Returns
+// nullptr on failure.
 std::unique_ptr<VideoDecoder> CreateDecoder(
+    const std::string& codec,
     const VaapiDevice& va_device,
     SharedVASurface::FetchPolicy fetch_policy,
     const uint8_t* stream_data,
     size_t stream_len) {
-  // TODO(b/239719493): Update how we are selecting the correct decoder.
-#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
-  const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
-  if (cmd->HasSwitch("h265")) {
-    return std::make_unique<H265Decoder>(stream_data, stream_len, va_device,
-                                         fetch_policy);
-  }
-#endif
-
-  if (*reinterpret_cast<const uint32_t*>(stream_data) == fourcc(0, 0, 0, 1) ||
-      ((*reinterpret_cast<const uint32_t*>(stream_data)) & 0x00FFFFFF) ==
-          fourcc(0, 0, 1, 0)) {
+  if (codec == "H264")
     return std::make_unique<H264Decoder>(stream_data, stream_len, va_device,
                                          fetch_policy);
-  }
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+  if (codec == "H265")
+    return std::make_unique<H265Decoder>(stream_data, stream_len, va_device,
+                                         fetch_policy);
+#endif
 
-  // Set up video parser.
   auto ivf_parser = std::make_unique<media::IvfParser>();
   media::IvfFileHeader file_header{};
   if (!ivf_parser->Initialize(stream_data, stream_len, &file_header)) {
@@ -161,22 +147,17 @@
     return nullptr;
   }
 
-  // Create appropriate decoder for codec.
-  VLOG(1) << "Creating decoder with codec " << FourccStr(file_header.fourcc);
-  // When adding a new format, keep fourccs alphabetical.
-  if (file_header.fourcc == fourcc('A', 'V', '0', '1')) {
+  if (codec == "AV1")
     return std::make_unique<Av1Decoder>(std::move(ivf_parser), va_device,
                                         fetch_policy);
-  } else if (file_header.fourcc == fourcc('V', 'P', '8', '0')) {
+  if (codec == "VP8")
     return std::make_unique<Vp8Decoder>(std::move(ivf_parser), va_device,
                                         fetch_policy);
-  } else if (file_header.fourcc == fourcc('V', 'P', '9', '0')) {
+  if (codec == "VP9")
     return std::make_unique<Vp9Decoder>(std::move(ivf_parser), va_device,
                                         fetch_policy);
-  }
 
-  LOG(ERROR) << "Codec " << FourccStr(file_header.fourcc) << " not supported.\n"
-             << kUsageMsg;
+  LOG(ERROR) << "Invalid codec requested: " << codec;
   return nullptr;
 }
 
@@ -226,6 +207,13 @@
     return EXIT_FAILURE;
   }
 
+  const std::string codec =
+      base::ToUpperASCII(cmd->GetSwitchValueASCII("codec"));
+  if (codec.empty()) {
+    std::cout << "No codec string was provided.\n" << kUsageMsg;
+    return EXIT_FAILURE;
+  }
+
   std::string output_prefix = cmd->GetSwitchValueASCII("out-prefix");
 
   const std::string frames = cmd->GetSwitchValueASCII("frames");
@@ -270,8 +258,8 @@
   }
 
   do {
-    const std::unique_ptr<VideoDecoder> dec =
-        CreateDecoder(va_device, *fetch_policy, stream.data(), stream.length());
+    const std::unique_ptr<VideoDecoder> dec = CreateDecoder(
+        codec, va_device, *fetch_policy, stream.data(), stream.length());
     if (!dec) {
       LOG(ERROR) << "Failed to create decoder for file: " << video_path;
       return EXIT_FAILURE;
diff --git a/media/video/h265_poc.cc b/media/video/h265_poc.cc
index d2e798e4..8ac3c70 100644
--- a/media/video/h265_poc.cc
+++ b/media/video/h265_poc.cc
@@ -75,4 +75,4 @@
   return pic_order_cnt;
 }
 
-}  // namespace media
\ No newline at end of file
+}  // namespace media
diff --git a/media/video/h265_poc.h b/media/video/h265_poc.h
index 11ba0d4..43a7e952 100644
--- a/media/video/h265_poc.h
+++ b/media/video/h265_poc.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include "media/base/media_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
@@ -40,4 +41,4 @@
 
 }  // namespace media
 
-#endif  // MEDIA_VIDEO_H265_POC_H_
\ No newline at end of file
+#endif  // MEDIA_VIDEO_H265_POC_H_
diff --git a/net/dns/opt_record_rdata.cc b/net/dns/opt_record_rdata.cc
index cb4fe256..43768287 100644
--- a/net/dns/opt_record_rdata.cc
+++ b/net/dns/opt_record_rdata.cc
@@ -181,11 +181,11 @@
 
 OptRecordRdata::~OptRecordRdata() = default;
 
-bool OptRecordRdata::operator==(const RecordRdata& other) const {
+bool OptRecordRdata::operator==(const OptRecordRdata& other) const {
   return IsEqual(&other);
 }
 
-bool OptRecordRdata::operator!=(const RecordRdata& other) const {
+bool OptRecordRdata::operator!=(const OptRecordRdata& other) const {
   return !IsEqual(&other);
 }
 
diff --git a/net/dns/opt_record_rdata.h b/net/dns/opt_record_rdata.h
index 9c7c4bfb3..ba26e870 100644
--- a/net/dns/opt_record_rdata.h
+++ b/net/dns/opt_record_rdata.h
@@ -181,8 +181,8 @@
 
   ~OptRecordRdata() override;
 
-  bool operator==(const RecordRdata& other) const;
-  bool operator!=(const RecordRdata& other) const;
+  bool operator==(const OptRecordRdata& other) const;
+  bool operator!=(const OptRecordRdata& other) const;
 
   // Checks whether two OptRecordRdata objects are equal. This comparison takes
   // into account the order of insertion. Two OptRecordRdata objects with
diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc
index 33d8034..1e83a068 100644
--- a/net/http/http_auth_handler_negotiate.cc
+++ b/net/http/http_auth_handler_negotiate.cc
@@ -121,12 +121,12 @@
 #elif BUILDFLAG(IS_POSIX)
   if (is_unsupported_)
     return ERR_UNSUPPORTED_AUTH_SCHEME;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // Note: Don't set is_unsupported_ = true here. AllowGssapiLibraryLoad()
   // might change to true during a session.
   if (!http_auth_preferences()->AllowGssapiLibraryLoad())
     return ERR_UNSUPPORTED_AUTH_SCHEME;
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
   if (!auth_library_->Init(net_log)) {
     is_unsupported_ = true;
     return ERR_UNSUPPORTED_AUTH_SCHEME;
diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc
index c3a6d44..310b218 100644
--- a/net/http/http_auth_handler_negotiate_unittest.cc
+++ b/net/http/http_auth_handler_negotiate_unittest.cc
@@ -471,8 +471,8 @@
 }
 #endif  // BUILDFLAG(USE_EXTERNAL_GSSAPI)
 
-// AllowGssapiLibraryLoad() is only supported on Chrome OS.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+// AllowGssapiLibraryLoad() is only supported on ChromeOS.
+#if BUILDFLAG(IS_CHROMEOS)
 TEST_F(HttpAuthHandlerNegotiateTest, AllowGssapiLibraryLoad) {
   // Disabling allow_gssapi_library_load should prevent handler creation.
   SetupMocks(AuthLibrary());
@@ -488,7 +488,7 @@
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(auth_handler);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #endif  // BUILDFLAG(IS_POSIX)
 
diff --git a/net/http/http_auth_preferences.cc b/net/http/http_auth_preferences.cc
index 4fa3e69d..fac4cde5 100644
--- a/net/http/http_auth_preferences.cc
+++ b/net/http/http_auth_preferences.cc
@@ -31,19 +31,19 @@
 bool HttpAuthPreferences::NtlmV2Enabled() const {
   return ntlm_v2_enabled_;
 }
-#endif
+#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
 #if BUILDFLAG(IS_ANDROID)
 std::string HttpAuthPreferences::AuthAndroidNegotiateAccountType() const {
   return auth_android_negotiate_account_type_;
 }
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 bool HttpAuthPreferences::AllowGssapiLibraryLoad() const {
   return allow_gssapi_library_load_;
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 bool HttpAuthPreferences::CanUseDefaultCredentials(
     const url::SchemeHostPort& auth_scheme_host_port) const {
diff --git a/net/http/http_auth_preferences.h b/net/http/http_auth_preferences.h
index 894ffefe..5f7d46c 100644
--- a/net/http/http_auth_preferences.h
+++ b/net/http/http_auth_preferences.h
@@ -46,13 +46,13 @@
   virtual bool NegotiateEnablePort() const;
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
   virtual bool NtlmV2Enabled() const;
-#endif
+#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 #if BUILDFLAG(IS_ANDROID)
   virtual std::string AuthAndroidNegotiateAccountType() const;
 #endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   virtual bool AllowGssapiLibraryLoad() const;
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
   virtual bool CanUseDefaultCredentials(
       const url::SchemeHostPort& auth_scheme_host_port) const;
   virtual HttpAuth::DelegationType GetDelegationType(
@@ -84,13 +84,13 @@
   void set_ntlm_v2_enabled(bool ntlm_v2_enabled) {
     ntlm_v2_enabled_ = ntlm_v2_enabled;
   }
-#endif
+#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   void set_allow_gssapi_library_load(bool allow_gssapi_library_load) {
     allow_gssapi_library_load_ = allow_gssapi_library_load;
   }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   const absl::optional<std::set<std::string>>& allowed_schemes() const {
     return allowed_schemes_;
@@ -119,7 +119,7 @@
       const std::string& account_type) {
     auth_android_negotiate_account_type_ = account_type;
   }
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
  private:
   bool delegate_by_kdc_policy_ = false;
@@ -131,15 +131,15 @@
 
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
   bool ntlm_v2_enabled_ = true;
-#endif
+#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
 #if BUILDFLAG(IS_ANDROID)
   std::string auth_android_negotiate_account_type_;
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   bool allow_gssapi_library_load_ = true;
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   absl::optional<std::set<std::string>> allowed_schemes_;
   std::unique_ptr<URLSecurityManager> security_manager_;
diff --git a/net/http/http_auth_preferences_unittest.cc b/net/http/http_auth_preferences_unittest.cc
index c349b72..e95f888 100644
--- a/net/http/http_auth_preferences_unittest.cc
+++ b/net/http/http_auth_preferences_unittest.cc
@@ -41,10 +41,10 @@
   http_auth_preferences.set_ntlm_v2_enabled(false);
   EXPECT_FALSE(http_auth_preferences.NtlmV2Enabled());
 }
-#endif
+#endif  // BUILDFLAG(IS_POSIX)
 
 #if BUILDFLAG(IS_ANDROID)
-TEST(HttpAuthPreferencesTest, AuthAndroidhNegotiateAccountType) {
+TEST(HttpAuthPreferencesTest, AuthAndroidNegotiateAccountType) {
   HttpAuthPreferences http_auth_preferences;
   EXPECT_EQ(std::string(),
             http_auth_preferences.AuthAndroidNegotiateAccountType());
@@ -52,16 +52,16 @@
   EXPECT_EQ(std::string("foo"),
             http_auth_preferences.AuthAndroidNegotiateAccountType());
 }
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 TEST(HttpAuthPreferencesTest, AllowGssapiLibraryLoad) {
   HttpAuthPreferences http_auth_preferences;
   EXPECT_TRUE(http_auth_preferences.AllowGssapiLibraryLoad());
   http_auth_preferences.set_allow_gssapi_library_load(false);
   EXPECT_FALSE(http_auth_preferences.AllowGssapiLibraryLoad());
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 TEST(HttpAuthPreferencesTest, AuthServerAllowlist) {
   HttpAuthPreferences http_auth_preferences;
diff --git a/net/nqe/throughput_analyzer.cc b/net/nqe/throughput_analyzer.cc
index 489c1841..fa0fa62 100644
--- a/net/nqe/throughput_analyzer.cc
+++ b/net/nqe/throughput_analyzer.cc
@@ -265,6 +265,9 @@
   if (params_->use_small_responses())
     return false;
 
+  if (!duration.is_positive())
+    return false;
+
   // Initial congestion window size for TCP connections.
   static constexpr size_t kCwndSizeKilobytes = 10 * 1.5;
   static constexpr size_t kCwndSizeBits = kCwndSizeKilobytes * 1000 * 8;
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 0cfad5f4f..4f0dc7c7 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -487,6 +487,8 @@
       "desktop_resizer_x11.cc",
       "desktop_resizer_x11.h",
       "input_injector_x11.cc",
+      "x11_crtc_resizer.cc",
+      "x11_crtc_resizer.h",
     ]
     deps += [
       "//remoting/host/linux:x11",
diff --git a/remoting/host/desktop_resizer_x11.cc b/remoting/host/desktop_resizer_x11.cc
index 6c196310..fc03770 100644
--- a/remoting/host/desktop_resizer_x11.cc
+++ b/remoting/host/desktop_resizer_x11.cc
@@ -14,14 +14,14 @@
 #include "base/strings/string_number_conversions.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/linux/x11_util.h"
+#include "remoting/host/x11_crtc_resizer.h"
 #include "ui/gfx/x/future.h"
 #include "ui/gfx/x/randr.h"
 #include "ui/gfx/x/scoped_ignore_errors.h"
 
 // On Linux, we use the xrandr extension to change the desktop resolution. In
-// curtain mode, we do exact resize where supported (currently only using a
-// patched Xvfb server). Otherwise, we try to pick the best resolution from the
-// existing modes.
+// curtain mode, we do exact resize where supported. Otherwise, we try to pick
+// the best resolution from the existing modes.
 //
 // Xrandr has a number of restrictions that make exact resize more complex:
 //
@@ -30,14 +30,28 @@
 //   2. It's not possible to delete a mode that's in use.
 //   3. Errors are communicated via Xlib's spectacularly unhelpful mechanism
 //      of terminating the process unless you install an error handler.
+//   4. The root window size must always enclose any enabled Outputs (that is,
+//      any output which is attached to a CRTC).
+//   5. An Output cannot be given properties (xy-offsets, mode) which would
+//      extend its rectangle beyond the root window size.
 //
-// Since we want the current mode name to be consistent, the approach is as
-// follows:
+// Since we want the current mode name to be consistent (for each Output), the
+// approach is as follows:
 //
-//   1. Disable the RANDR Output.
-//   2. Delete the CRD mode, if it exists.
-//   3. Create the CRD mode at the new resolution.
-//   4. Set the Output to the CRD mode (which re-enables it).
+//   1. Fetch information about all the active (enabled) CRTCs.
+//   2. Disable the RANDR Output being resized.
+//   3. Delete the CRD mode, if it exists.
+//   4. Create the CRD mode at the new resolution, and add it to the Output's
+//      list of modes.
+//   5. Adjust the properties (in memory) of any CRTCs to be modified:
+//      * Width/height (mode) of the CRTC being resized.
+//      * xy-offsets to avoid overlapping CRTCs.
+//   6. Disable any CRTCs that might prevent changing the root window size.
+//   7. Compute the bounding rectangle of all CRTCs (after adjustment), and set
+//      it as the new root window size.
+//   8. Apply all adjusted CRTC properties to the CRTCs. This will set the
+//      Output being resized to the new CRD mode (which re-enables it), and it
+//      will re-enable any other CRTCs that were disabled.
 
 namespace {
 
@@ -237,20 +251,59 @@
   HOST_LOG << "Changing desktop size to " << resolution.dimensions().width()
            << "x" << resolution.dimensions().height();
 
-  // TODO(lambroslambrou): Use the DPI from client size information.
-  uint32_t width_mm =
-      PixelsToMillimeters(resolution.dimensions().width(), kDefaultDPI);
-  uint32_t height_mm =
-      PixelsToMillimeters(resolution.dimensions().height(), kDefaultDPI);
-  SwitchToMode(output, std::string());
-  randr_->SetScreenSize(
-      {root_, static_cast<uint16_t>(resolution.dimensions().width()),
-       static_cast<uint16_t>(resolution.dimensions().height()), width_mm,
-       height_mm});
+  X11CrtcResizer resizer(resources_.get(), randr_);
+
+  resizer.FetchActiveCrtcs();
+  auto crtc = resizer.GetCrtcForOutput(output);
+
+  if (crtc == kDisabledCrtc) {
+    // This is not expected to happen. Disabled Outputs are not expected to
+    // have any Monitor, but |output| was found in the RRGetMonitors response,
+    // so it should have a CRTC attached.
+    LOG(ERROR) << "No CRTC found for output: " << base::to_underlying(output);
+    return;
+  }
+
+  // Disable the output now, so that the old mode can be deleted and the new
+  // mode created and added to the output's available modes. The previous size
+  // and offsets will be stored in |resizer|.
+  resizer.DisableCrtc(crtc);
+
   DeleteMode(output, mode_name);
-  CreateMode(output, mode_name, resolution.dimensions().width(),
-             resolution.dimensions().height());
-  SwitchToMode(output, mode_name);
+  auto mode = CreateMode(output, mode_name, resolution.dimensions().width(),
+                         resolution.dimensions().height());
+  if (mode == kInvalidMode) {
+    // The CRTC is disabled, but there's no easy way to recover it here
+    // (the mode it was attached to has gone).
+    LOG(ERROR) << "Failed to create new mode.";
+    return;
+  }
+
+  // Update |active_crtcs_| with new sizes and offsets.
+  resizer.UpdateActiveCrtcs(crtc, mode, resolution.dimensions());
+
+  // Disable any CRTCs that have been changed, so that the root window can be
+  // safely resized to the bounding-box of the new CRTCs.
+  // This is non-optimal: the only CRTCs that need disabling are those whose
+  // original rectangles don't fit into the new root window - they are the ones
+  // that would prevent resizing the root window. But figuring these out would
+  // involve keeping track of all the original rectangles as well as the new
+  // ones. So, to keep the implementation simple (and working for any arbitrary
+  // layout algorithm), all changed CRTCs are disabled here.
+  resizer.DisableChangedCrtcs();
+
+  // Get the dimensions to resize the root window to.
+  auto dimensions = resizer.GetBoundingBox();
+
+  // TODO(lambroslambrou): Use the DPI from client size information.
+  uint32_t width_mm = PixelsToMillimeters(dimensions.width(), kDefaultDPI);
+  uint32_t height_mm = PixelsToMillimeters(dimensions.height(), kDefaultDPI);
+  randr_->SetScreenSize({root_, static_cast<uint16_t>(dimensions.width()),
+                         static_cast<uint16_t>(dimensions.height()), width_mm,
+                         height_mm});
+
+  // Apply the new CRTCs, which will re-enable any that were disabled.
+  resizer.ApplyActiveCrtcs();
 }
 
 void DesktopResizerX11::SetResolutionExistingMode(
@@ -275,10 +328,10 @@
   }
 }
 
-void DesktopResizerX11::CreateMode(x11::RandR::Output output,
-                                   const std::string& name,
-                                   int width,
-                                   int height) {
+x11::RandR::Mode DesktopResizerX11::CreateMode(x11::RandR::Output output,
+                                               const std::string& name,
+                                               int width,
+                                               int height) {
   x11::RandR::ModeInfo mode;
   mode.width = width;
   mode.height = height;
@@ -286,16 +339,17 @@
   randr_->CreateMode({root_, mode, name.c_str()});
 
   if (!resources_.Refresh(randr_, root_))
-    return;
+    return kInvalidMode;
   x11::RandR::Mode mode_id = resources_.GetIdForMode(name);
   if (mode_id == kInvalidMode) {
     LOG(ERROR) << "No ID found for mode: " << name;
-    return;
+    return kInvalidMode;
   }
   randr_->AddOutputMode({
       output,
       mode_id,
   });
+  return mode_id;
 }
 
 void DesktopResizerX11::DeleteMode(x11::RandR::Output output,
@@ -308,60 +362,6 @@
   }
 }
 
-void DesktopResizerX11::SwitchToMode(x11::RandR::Output output,
-                                     const std::string& name) {
-  auto mode_id = kInvalidMode;
-  std::vector<x11::RandR::Output> outputs;
-  if (!name.empty()) {
-    mode_id = resources_.GetIdForMode(name);
-    if (mode_id == kInvalidMode) {
-      LOG(ERROR) << "No ID found for mode: " << name;
-      return;
-    }
-
-    // The case where a CRTC has multiple outputs is unsupported here. With
-    // Xvfb, there exists only 1 output. With Xorg+video-dummy, there are
-    // several outputs, but each one has a separate CRTC it can attach to.
-    outputs = {output};
-  }
-
-  x11::Time config_timestamp = resources_.get()->config_timestamp;
-  auto output_info = randr_->GetOutputInfo({output, config_timestamp}).Sync();
-  if (!output_info)
-    return;
-
-  x11::RandR::Crtc crtc = output_info->crtc;
-  if (crtc == kDisabledCrtc) {
-    // The output is disabled. If |name| is empty, the caller requested the
-    // output be disabled, so there is nothing further to do.
-    if (name.empty())
-      return;
-
-    // |crtcs| is the set of CRTCs that this output is allowed to attach to.
-    if (output_info->crtcs.size() != 1) {
-      // |crtcs| should always be non-empty. To properly handle the size() > 1
-      // case, the code should step through |crtcs| and fetch the outputs using
-      // GetCrtcInfo(), stopping when a CRTC is found with empty outputs. With
-      // Xorg+video-dummy, this case never happens - the driver allocates a
-      // single CRTC for each output.
-      LOG(ERROR) << "Unexpected #crtcs: " << output_info->crtcs.size();
-      return;
-    }
-    crtc = output_info->crtcs[0];
-  }
-
-  randr_->SetCrtcConfig({
-      .crtc = crtc,
-      .timestamp = x11::Time::CurrentTime,
-      .config_timestamp = config_timestamp,
-      .x = 0,
-      .y = 0,
-      .mode = mode_id,
-      .rotation = x11::RandR::Rotation::Rotate_0,
-      .outputs = outputs,
-  });
-}
-
 // static
 std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
   return std::make_unique<DesktopResizerX11>();
diff --git a/remoting/host/desktop_resizer_x11.h b/remoting/host/desktop_resizer_x11.h
index a57f0b7..4f627a9 100644
--- a/remoting/host/desktop_resizer_x11.h
+++ b/remoting/host/desktop_resizer_x11.h
@@ -60,20 +60,16 @@
   void SetResolutionExistingMode(const ScreenResolution& resolution);
 
   // Create a mode, and attach it to the output. If the mode already exists, it
-  // is left unchanged.
-  void CreateMode(x11::RandR::Output output,
-                  const std::string& name,
-                  int width,
-                  int height);
+  // is left unchanged. Returns the new mode ID, or None (0) on failure.
+  x11::RandR::Mode CreateMode(x11::RandR::Output output,
+                              const std::string& name,
+                              int width,
+                              int height);
 
   // Remove the specified mode from the output, and delete it. If the mode is in
   // use, it is not deleted.
   void DeleteMode(x11::RandR::Output output, const std::string& name);
 
-  // Switch the output to the specified mode. If name is empty, the output is
-  // disabled instead, which is required before changing its resolution.
-  void SwitchToMode(x11::RandR::Output output, const std::string& name);
-
   raw_ptr<x11::Connection> connection_;
   const raw_ptr<x11::RandR> randr_ = nullptr;
   const raw_ptr<const x11::Screen> screen_ = nullptr;
diff --git a/remoting/host/x11_crtc_resizer.cc b/remoting/host/x11_crtc_resizer.cc
new file mode 100644
index 0000000..50e7d46
--- /dev/null
+++ b/remoting/host/x11_crtc_resizer.cc
@@ -0,0 +1,158 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/x11_crtc_resizer.h"
+
+#include <utility>
+
+#include "base/containers/contains.h"
+#include "base/ranges/algorithm.h"
+#include "ui/gfx/x/future.h"
+
+namespace {
+
+constexpr auto kInvalidMode = static_cast<x11::RandR::Mode>(0);
+constexpr auto kDisabledCrtc = static_cast<x11::RandR::Crtc>(0);
+
+}  // namespace
+
+namespace remoting {
+
+X11CrtcResizer::CrtcInfo::CrtcInfo() = default;
+X11CrtcResizer::CrtcInfo::CrtcInfo(x11::RandR::Crtc crtc,
+                                   int16_t x,
+                                   int16_t y,
+                                   uint16_t width,
+                                   uint16_t height,
+                                   x11::RandR::Mode mode,
+                                   x11::RandR::Rotation rotation,
+                                   std::vector<x11::RandR::Output>&& outputs)
+    : crtc(crtc),
+      x(x),
+      y(y),
+      width(width),
+      height(height),
+      mode(mode),
+      rotation(rotation),
+      outputs(outputs) {}
+X11CrtcResizer::CrtcInfo::CrtcInfo(const X11CrtcResizer::CrtcInfo&) = default;
+X11CrtcResizer::CrtcInfo::CrtcInfo(X11CrtcResizer::CrtcInfo&&) = default;
+X11CrtcResizer::CrtcInfo& X11CrtcResizer::CrtcInfo::operator=(
+    const X11CrtcResizer::CrtcInfo&) = default;
+X11CrtcResizer::CrtcInfo& X11CrtcResizer::CrtcInfo::operator=(
+    X11CrtcResizer::CrtcInfo&&) = default;
+X11CrtcResizer::CrtcInfo::~CrtcInfo() = default;
+
+X11CrtcResizer::X11CrtcResizer(
+    x11::RandR::GetScreenResourcesCurrentReply* resources,
+    x11::RandR* randr)
+    : resources_(resources), randr_(randr) {}
+
+X11CrtcResizer::~X11CrtcResizer() = default;
+
+void X11CrtcResizer::FetchActiveCrtcs() {
+  active_crtcs_.clear();
+  x11::Time config_timestamp = resources_->config_timestamp;
+  for (const auto& crtc : resources_->crtcs) {
+    auto response = randr_->GetCrtcInfo({crtc, config_timestamp}).Sync();
+    if (!response)
+      continue;
+    if (response->outputs.empty())
+      continue;
+
+    active_crtcs_.emplace_back(
+        crtc, response->x, response->y, response->width, response->height,
+        response->mode, response->rotation, std::move(response->outputs));
+  }
+}
+
+x11::RandR::Crtc X11CrtcResizer::GetCrtcForOutput(
+    x11::RandR::Output output) const {
+  // This implementation assumes an output is attached to only one CRTC. If
+  // there are multiple CRTCs for the output, only the first will be returned,
+  // but this should never occur with Xorg+video-dummy.
+  auto iter =
+      base::ranges::find_if(active_crtcs_, [output](const CrtcInfo& crtc_info) {
+        return base::Contains(crtc_info.outputs, output);
+      });
+  if (iter == active_crtcs_.end()) {
+    return kDisabledCrtc;
+  }
+  return iter->crtc;
+}
+
+void X11CrtcResizer::DisableCrtc(x11::RandR::Crtc crtc) {
+  x11::Time config_timestamp = resources_->config_timestamp;
+  randr_->SetCrtcConfig({
+      .crtc = crtc,
+      .timestamp = x11::Time::CurrentTime,
+      .config_timestamp = config_timestamp,
+      .x = 0,
+      .y = 0,
+      .mode = kInvalidMode,
+      .rotation = x11::RandR::Rotation::Rotate_0,
+      .outputs = {},
+  });
+}
+
+void X11CrtcResizer::UpdateActiveCrtcs(x11::RandR::Crtc crtc,
+                                       x11::RandR::Mode mode,
+                                       const webrtc::DesktopSize& new_size) {
+  // Find |crtc| in |active_crtcs_| and adjust its mode and size.
+  auto iter = base::ranges::find(active_crtcs_, crtc, &CrtcInfo::crtc);
+
+  // |crtc| was returned by GetCrtcForOutput() so it should definitely be in
+  // the list.
+  DCHECK(iter != active_crtcs_.end());
+
+  iter->mode = mode;
+  iter->width = new_size.width();
+  iter->height = new_size.height();
+
+  // Mark it as changed so that ApplyActiveCrtcs() will apply the new |mode|.
+  // The |width| and |height| are only used for computing the bounding-box,
+  // they are not used by ApplyActiveCrtcs().
+  iter->changed = true;
+
+  // TODO(crbug.com/1326339): Adjust the xy-offsets to avoid overlaps.
+}
+
+void X11CrtcResizer::DisableChangedCrtcs() {
+  base::ranges::for_each(active_crtcs_, [this](const CrtcInfo& crtc_info) {
+    if (crtc_info.changed) {
+      DisableCrtc(crtc_info.crtc);
+    }
+  });
+}
+
+webrtc::DesktopSize X11CrtcResizer::GetBoundingBox() const {
+  webrtc::DesktopSize result;
+  base::ranges::for_each(active_crtcs_, [&result](const CrtcInfo& crtc_info) {
+    int32_t width = crtc_info.x + crtc_info.width;
+    int32_t height = crtc_info.y + crtc_info.height;
+    result.set(std::max(result.width(), width),
+               std::max(result.height(), height));
+  });
+  return result;
+}
+
+void X11CrtcResizer::ApplyActiveCrtcs() {
+  base::ranges::for_each(active_crtcs_, [this](const CrtcInfo& crtc_info) {
+    if (crtc_info.changed) {
+      x11::Time config_timestamp = resources_->config_timestamp;
+      randr_->SetCrtcConfig({
+          .crtc = crtc_info.crtc,
+          .timestamp = x11::Time::CurrentTime,
+          .config_timestamp = config_timestamp,
+          .x = crtc_info.x,
+          .y = crtc_info.y,
+          .mode = crtc_info.mode,
+          .rotation = crtc_info.rotation,
+          .outputs = crtc_info.outputs,
+      });
+    }
+  });
+}
+
+}  // namespace remoting
diff --git a/remoting/host/x11_crtc_resizer.h b/remoting/host/x11_crtc_resizer.h
new file mode 100644
index 0000000..9e606d5
--- /dev/null
+++ b/remoting/host/x11_crtc_resizer.h
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_X11_CRTC_RESIZER_H_
+#define REMOTING_HOST_X11_CRTC_RESIZER_H_
+
+#include <list>
+
+#include "base/memory/raw_ptr.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
+#include "ui/gfx/x/randr.h"
+
+namespace remoting {
+
+// Helper class for DesktopResizerX11 which handles much of the logic
+// for arranging and resizing a set of active CRTCs.
+class X11CrtcResizer {
+ public:
+  X11CrtcResizer(x11::RandR::GetScreenResourcesCurrentReply* resources,
+                 x11::RandR* randr);
+  X11CrtcResizer(const X11CrtcResizer&) = delete;
+  X11CrtcResizer& operator=(const X11CrtcResizer&) = delete;
+  ~X11CrtcResizer();
+
+  // Queries the server for all active CRTCs and stores them in |active_crtcs_|.
+  void FetchActiveCrtcs();
+
+  // Searches |active_crts_| to find the one for the given output. If none is
+  // found, this returns kDisabledCrtc. Since the information on all CRTCs is
+  // already fetched, this method avoids a server round-trip from using
+  // RRGetOutputInfo.
+  x11::RandR::Crtc GetCrtcForOutput(x11::RandR::Output output) const;
+
+  // Disables a CRTC. A disabled CRTC no longer has a mode selected (allowing
+  // the CRD mode to be removed). It also no longer occupies space in the root
+  // window, which may allow the root window to be resized. This does not
+  // modify |active_crtcs_|, so the stored information can be used to enable
+  // the CRTC again.
+  void DisableCrtc(x11::RandR::Crtc crtc);
+
+  // This operates only on |active_crtcs_| without making any X server calls.
+  // It sets the new mode and width/height for the given CRTC. And it changes
+  // any xy-offsets as needed, to avoid overlaps between CRTCs. Every modified
+  // CRTC is marked by setting its |changed| flag.
+  void UpdateActiveCrtcs(x11::RandR::Crtc crtc,
+                         x11::RandR::Mode mode,
+                         const webrtc::DesktopSize& new_size);
+
+  // Disables any CRTCs whose |changed| flag is true.
+  void DisableChangedCrtcs();
+
+  // Returns the bounding box of |active_crtcs_| from their current xy-offsets
+  // and sizes.
+  webrtc::DesktopSize GetBoundingBox() const;
+
+  // Applies any changed CRTCs back to the X server. This will re-enable any
+  // outputs/CRTCs that were disabled.
+  void ApplyActiveCrtcs();
+
+ private:
+  // Information for an active CRTC, from RRGetCrtcInfo response. When
+  // modifying a CRTC, the information here can reconstruct the original
+  // properties that should not be changed.
+  struct CrtcInfo {
+    CrtcInfo();
+    CrtcInfo(x11::RandR::Crtc crtc,
+             int16_t x,
+             int16_t y,
+             uint16_t width,
+             uint16_t height,
+             x11::RandR::Mode mode,
+             x11::RandR::Rotation rotation,
+             std::vector<x11::RandR::Output>&& outputs);
+    CrtcInfo(const CrtcInfo&);
+    CrtcInfo(CrtcInfo&&);
+    CrtcInfo& operator=(const CrtcInfo&);
+    CrtcInfo& operator=(CrtcInfo&&);
+    ~CrtcInfo();
+
+    x11::RandR::Crtc crtc;
+    int16_t x;
+    int16_t y;
+    uint16_t width;
+    uint16_t height;
+    x11::RandR::Mode mode;
+    x11::RandR::Rotation rotation;
+    std::vector<x11::RandR::Output> outputs;
+
+    // True if any values are different from the response from RRGetCrtcInfo.
+    bool changed = false;
+  };
+
+  raw_ptr<x11::RandR::GetScreenResourcesCurrentReply> resources_;
+  raw_ptr<x11::RandR> randr_;
+
+  // Information on all CRTCs that are currently enabled (including the CRTC
+  // being resized). This is only used during a resize operation, while the X
+  // server is grabbed. Some of these xy-positions may be adjusted. At the end,
+  // the root window size will be set to the bounding rectangle of all these
+  // CRTCs. If a CRTC needs to be disabled temporarily, the list entry will be
+  // preserved so that the CRTC can be re-enabled with the original and new
+  // properties.
+  std::list<CrtcInfo> active_crtcs_;
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_X11_CRTC_RESIZER_H_
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 73627a4..89653c1 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -233,11 +233,12 @@
 // is also loaded in this process.
 void BlocklistAddOneDll(const wchar_t* module_name,
                         bool check_in_browser,
-                        TargetPolicy* policy) {
+                        TargetConfig* config) {
+  DCHECK(!config->IsConfigured());
   if (check_in_browser) {
     HMODULE module = ::GetModuleHandleW(module_name);
     if (module) {
-      policy->AddDllToUnload(module_name);
+      config->AddDllToUnload(module_name);
       DVLOG(1) << "dll to unload found: " << module_name;
     } else {
       for (const auto& alt_name : GetShortNameVariants(module_name)) {
@@ -246,28 +247,20 @@
         // want to make sure it is the right one.
         if (module && IsExpandedModuleName(module, module_name)) {
           // Found a match. We add both forms to the policy.
-          policy->AddDllToUnload(alt_name.c_str());
-          policy->AddDllToUnload(module_name);
+          config->AddDllToUnload(alt_name.c_str());
+          config->AddDllToUnload(module_name);
           return;
         }
       }
     }
   } else {
-    policy->AddDllToUnload(module_name);
+    config->AddDllToUnload(module_name);
     for (const auto& alt_name : GetShortNameVariants(module_name)) {
-      policy->AddDllToUnload(alt_name.c_str());
+      config->AddDllToUnload(alt_name.c_str());
     }
   }
 }
 
-// Adds policy rules for unloaded the known dlls that cause chrome to crash.
-// Eviction of injected DLLs is done by the sandbox so that the injected module
-// does not get a chance to execute any code.
-void AddGenericDllEvictionPolicy(TargetPolicy* policy) {
-  for (int ix = 0; ix != std::size(kTroublesomeDlls); ++ix)
-    BlocklistAddOneDll(kTroublesomeDlls[ix], true, policy);
-}
-
 DWORD GetSessionId() {
   DWORD session_id;
   CHECK(::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id));
@@ -325,8 +318,8 @@
   return false;
 }
 
-// Adds the generic policy rules to a sandbox TargetConfig.
-ResultCode AddGenericPolicy(sandbox::TargetConfig* config) {
+// Adds the generic config rules to a sandbox TargetConfig.
+ResultCode AddGenericConfig(sandbox::TargetConfig* config) {
   DCHECK(!config->IsConfigured());
   ResultCode result;
 
@@ -379,6 +372,12 @@
   }
 #endif
 
+  // Adds policy rules for unloading the known dlls that cause Chrome to crash.
+  // Eviction of injected DLLs is done by the sandbox so that the injected
+  // module does not get a chance to execute any code.
+  for (int ix = 0; ix != std::size(kTroublesomeDlls); ++ix)
+    BlocklistAddOneDll(kTroublesomeDlls[ix], true, config);
+
   return SBOX_ALL_OK;
 }
 
@@ -1077,15 +1076,13 @@
 #endif
 
   if (!config->IsConfigured()) {
-    result = AddGenericPolicy(config);
+    result = AddGenericConfig(config);
     if (result != SBOX_ALL_OK) {
       NOTREACHED();
       return result;
     }
   }
 
-  AddGenericDllEvictionPolicy(policy);
-
   std::string appcontainer_id;
   if (IsAppContainerEnabledForSandbox(cmd_line, sandbox_type) &&
       delegate->GetAppContainerId(&appcontainer_id)) {
@@ -1241,8 +1238,8 @@
 
 void BlocklistAddOneDllForTesting(const wchar_t* module_name,
                                   bool check_in_browser,
-                                  TargetPolicy* policy) {
-  BlocklistAddOneDll(module_name, check_in_browser, policy);
+                                  TargetConfig* config) {
+  BlocklistAddOneDll(module_name, check_in_browser, config);
 }
 
 // static
diff --git a/sandbox/policy/win/sandbox_win.h b/sandbox/policy/win/sandbox_win.h
index ec9cb8b1..33a49a4 100644
--- a/sandbox/policy/win/sandbox_win.h
+++ b/sandbox/policy/win/sandbox_win.h
@@ -26,6 +26,7 @@
 
 namespace sandbox {
 class BrokerServices;
+class TargetConfig;
 class TargetPolicy;
 class TargetServices;
 
@@ -123,7 +124,7 @@
 SANDBOX_POLICY_EXPORT
 void BlocklistAddOneDllForTesting(const wchar_t* module_name,
                                   bool check_in_browser,
-                                  TargetPolicy* policy);
+                                  TargetConfig* config);
 
 }  // namespace policy
 }  // namespace sandbox
diff --git a/sandbox/policy/win/sandbox_win_unittest.cc b/sandbox/policy/win/sandbox_win_unittest.cc
index f3bce25b..2175b93 100644
--- a/sandbox/policy/win/sandbox_win_unittest.cc
+++ b/sandbox/policy/win/sandbox_win_unittest.cc
@@ -53,6 +53,16 @@
                      const wchar_t* pattern) override {
     return SBOX_ALL_OK;
   }
+  ResultCode AddDllToUnload(const wchar_t* dll_name) override {
+    blocklisted_dlls_.push_back(dll_name);
+    return SBOX_ALL_OK;
+  }
+  const std::vector<std::wstring>& blocklisted_dlls() const {
+    return blocklisted_dlls_;
+  }
+
+ private:
+  std::vector<std::wstring> blocklisted_dlls_;
 };
 
 class TestTargetPolicy : public TargetPolicy {
@@ -104,10 +114,6 @@
   void SetStrictInterceptions() override {}
   ResultCode SetStdoutHandle(HANDLE handle) override { return SBOX_ALL_OK; }
   ResultCode SetStderrHandle(HANDLE handle) override { return SBOX_ALL_OK; }
-  ResultCode AddDllToUnload(const wchar_t* dll_name) override {
-    blocklisted_dlls_.push_back(dll_name);
-    return SBOX_ALL_OK;
-  }
   ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
                                     const wchar_t* handle_name) override {
     return SBOX_ALL_OK;
@@ -139,16 +145,11 @@
 
   void SetEffectiveToken(HANDLE token) override {}
 
-  const std::vector<std::wstring>& blocklisted_dlls() const {
-    return blocklisted_dlls_;
-  }
-
   void SetAllowNoSandboxJob() override { NOTREACHED(); }
   bool GetAllowNoSandboxJob() override { return false; }
 
  private:
   TestTargetConfig config_;
-  std::vector<std::wstring> blocklisted_dlls_;
   scoped_refptr<AppContainerBase> app_container_;
 };
 
@@ -298,15 +299,19 @@
 TEST_F(SandboxWinTest, DISABLED_BlocklistAddOneDllCheckInBrowser) {
   {  // Block loaded module.
     TestTargetPolicy policy;
-    BlocklistAddOneDllForTesting(L"kernel32.dll", true, &policy);
-    EXPECT_EQ(policy.blocklisted_dlls(),
+    TestTargetConfig* config =
+        static_cast<TestTargetConfig*>(policy.GetConfig());
+    BlocklistAddOneDllForTesting(L"kernel32.dll", true, config);
+    EXPECT_EQ(config->blocklisted_dlls(),
               std::vector<std::wstring>({L"kernel32.dll"}));
   }
 
   {  // Block module which is not loaded.
     TestTargetPolicy policy;
-    BlocklistAddOneDllForTesting(L"notloaded.dll", true, &policy);
-    EXPECT_TRUE(policy.blocklisted_dlls().empty());
+    TestTargetConfig* config =
+        static_cast<TestTargetConfig*>(policy.GetConfig());
+    BlocklistAddOneDllForTesting(L"notloaded.dll", true, config);
+    EXPECT_TRUE(config->blocklisted_dlls().empty());
   }
 
   {  // Block module loaded by short name.
@@ -330,8 +335,10 @@
     EXPECT_TRUE(library.is_valid());
 
     TestTargetPolicy policy;
-    BlocklistAddOneDllForTesting(full_dll_name.c_str(), true, &policy);
-    EXPECT_EQ(policy.blocklisted_dlls(),
+    TestTargetConfig* config =
+        static_cast<TestTargetConfig*>(policy.GetConfig());
+    BlocklistAddOneDllForTesting(full_dll_name.c_str(), true, config);
+    EXPECT_EQ(config->blocklisted_dlls(),
               std::vector<std::wstring>({short_dll_name, full_dll_name}));
   }
 }
@@ -339,15 +346,19 @@
 TEST_F(SandboxWinTest, BlocklistAddOneDllDontCheckInBrowser) {
   {  // Block module with short name.
     TestTargetPolicy policy;
-    BlocklistAddOneDllForTesting(L"short.dll", false, &policy);
-    EXPECT_EQ(policy.blocklisted_dlls(),
+    TestTargetConfig* config =
+        static_cast<TestTargetConfig*>(policy.GetConfig());
+    BlocklistAddOneDllForTesting(L"short.dll", false, config);
+    EXPECT_EQ(config->blocklisted_dlls(),
               std::vector<std::wstring>({L"short.dll"}));
   }
 
   {  // Block module with long name.
     TestTargetPolicy policy;
-    BlocklistAddOneDllForTesting(L"thelongname.dll", false, &policy);
-    EXPECT_EQ(policy.blocklisted_dlls(),
+    TestTargetConfig* config =
+        static_cast<TestTargetConfig*>(policy.GetConfig());
+    BlocklistAddOneDllForTesting(L"thelongname.dll", false, config);
+    EXPECT_EQ(config->blocklisted_dlls(),
               std::vector<std::wstring>({L"thelongname.dll", L"thelon~1.dll",
                                          L"thelon~2.dll", L"thelon~3.dll"}));
   }
diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h
index 1505e06b..b9fc5d9 100644
--- a/sandbox/win/src/sandbox_policy.h
+++ b/sandbox/win/src/sandbox_policy.h
@@ -78,6 +78,11 @@
   virtual ResultCode AddRule(SubSystem subsystem,
                              Semantics semantics,
                              const wchar_t* pattern) = 0;
+
+  // Adds a dll that will be unloaded in the target process before it gets
+  // a chance to initialize itself. Typically, dlls that cause the target
+  // to crash go here.
+  virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
 };
 
 // We need [[clang::lto_visibility_public]] because instances of this class are
@@ -233,11 +238,6 @@
   virtual ResultCode SetStdoutHandle(HANDLE handle) = 0;
   virtual ResultCode SetStderrHandle(HANDLE handle) = 0;
 
-  // Adds a dll that will be unloaded in the target process before it gets
-  // a chance to initialize itself. Typically, dlls that cause the target
-  // to crash go here.
-  virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
-
   // Adds a handle that will be closed in the target process after lockdown.
   // A nullptr value for handle_name indicates all handles of the specified
   // type. An empty string for handle_name indicates the handle is unnamed.
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
index 62651688..58aa3ff 100644
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -130,6 +130,11 @@
   return policy_;
 }
 
+std::vector<std::wstring>& ConfigBase::blocklisted_dlls() {
+  DCHECK(configured_);
+  return blocklisted_dlls_;
+}
+
 ConfigBase::~ConfigBase() {
   delete policy_;  // Allocated by MakeBrokerPolicyMemory.
 }
@@ -228,6 +233,11 @@
   return SBOX_ALL_OK;
 }
 
+ResultCode ConfigBase::AddDllToUnload(const wchar_t* dll_name) {
+  blocklisted_dlls_.push_back(dll_name);
+  return SBOX_ALL_OK;
+}
+
 PolicyBase::PolicyBase(base::StringPiece tag)
     : tag_(tag),
       config_(),
@@ -505,11 +515,6 @@
   return SBOX_ALL_OK;
 }
 
-ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
-  blocklisted_dlls_.push_back(dll_name);
-  return SBOX_ALL_OK;
-}
-
 ResultCode PolicyBase::AddKernelObjectToClose(const wchar_t* handle_type,
                                               const wchar_t* handle_name) {
   return handle_closer_.AddHandle(handle_type, handle_name);
@@ -814,7 +819,7 @@
     }
   }
 
-  for (const std::wstring& dll : blocklisted_dlls_)
+  for (const std::wstring& dll : config()->blocklisted_dlls())
     manager.AddToUnloadModules(dll.c_str());
 
   if (!SetupBasicInterceptions(&manager, is_csrss_connected_))
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h
index 85465c2..a11ed081 100644
--- a/sandbox/win/src/sandbox_policy_base.h
+++ b/sandbox/win/src/sandbox_policy_base.h
@@ -56,6 +56,7 @@
   ResultCode AddRule(SubSystem subsystem,
                      Semantics semantics,
                      const wchar_t* pattern) override;
+  ResultCode AddDllToUnload(const wchar_t* dll_name) override;
 
  private:
   // Can call Freeze()
@@ -86,12 +87,15 @@
 
   // Should only be called once the object is configured.
   PolicyGlobal* policy();
+  std::vector<std::wstring>& blocklisted_dlls();
 
   // Object in charge of generating the low level policy. Will be reset() when
   // Freeze() is called.
   std::unique_ptr<LowLevelPolicy> policy_maker_;
   // Memory structure that stores the low level policy rules for proxied calls.
   raw_ptr<PolicyGlobal> policy_;
+  // The list of dlls to unload in the target process.
+  std::vector<std::wstring> blocklisted_dlls_;
 };
 
 class PolicyBase final : public TargetPolicy {
@@ -126,7 +130,6 @@
   void SetStrictInterceptions() override;
   ResultCode SetStdoutHandle(HANDLE handle) override;
   ResultCode SetStderrHandle(HANDLE handle) override;
-  ResultCode AddDllToUnload(const wchar_t* dll_name) override;
   ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
                                     const wchar_t* handle_name) override;
   void AddHandleToShare(HANDLE handle) override;
@@ -226,8 +229,6 @@
   MitigationFlags mitigations_;
   MitigationFlags delayed_mitigations_;
   bool is_csrss_connected_;
-  // The list of dlls to unload in the target process.
-  std::vector<std::wstring> blocklisted_dlls_;
   // This is a map of handle-types to names that we need to close in the
   // target process. A null set means we need to close all handles of the
   // given type.
diff --git a/sandbox/win/src/unload_dll_test.cc b/sandbox/win/src/unload_dll_test.cc
index cbcb7f09..9b6dfd9 100644
--- a/sandbox/win/src/unload_dll_test.cc
+++ b/sandbox/win/src/unload_dll_test.cc
@@ -72,7 +72,7 @@
   auto runner = std::make_unique<TestRunner>();
   runner->SetTestState(BEFORE_REVERT);
   runner->SetTimeout(2000);
-  runner->GetPolicy()->AddDllToUnload(L"avicap32.dll");
+  runner->GetPolicy()->GetConfig()->AddDllToUnload(L"avicap32.dll");
   return runner;
 }
 
@@ -87,7 +87,7 @@
   auto runner = std::make_unique<TestRunner>();
   runner->SetTestState(BEFORE_REVERT);
   runner->SetTimeout(2000);
-  runner->GetPolicy()->AddDllToUnload(L"avicap32.dll");
+  runner->GetPolicy()->GetConfig()->AddDllToUnload(L"avicap32.dll");
   // Add a couple of rules that ensures that the interception agent add EAT
   // patching on the client which makes sure that the unload dll record does
   // not interact badly with them.
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 41cb92c..f42edc9 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -166,11 +166,11 @@
 
 #if BUILDFLAG(IS_P2P_ENABLED)
 #include "services/network/p2p/socket_manager.h"
-#endif
+#endif  // BUILDFLAG(IS_P2P_ENABLED)
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/application_status_listener.h"
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
 namespace network {
 
@@ -317,7 +317,7 @@
  private:
   ApplicationStateChangeCallback callback_;
 };
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
 struct TestVerifyCertState {
   net::CertVerifyResult result;
@@ -468,13 +468,13 @@
       url_request_context_(nullptr),
 #if BUILDFLAG(ENABLE_REPORTING)
       is_observing_reporting_service_(false),
-#endif
+#endif  // BUILDFLAG(ENABLE_REPORTING)
       params_(std::move(params)),
       on_connection_close_callback_(std::move(on_connection_close_callback)),
 #if BUILDFLAG(IS_ANDROID)
       app_status_listener_(
           std::make_unique<NetworkContextApplicationStatusListener>()),
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
       receiver_(this, std::move(receiver)),
       first_party_sets_access_delegate_(
           std::move(params_->first_party_sets_access_delegate_receiver),
@@ -563,12 +563,12 @@
   sct_auditing_handler_ =
       std::make_unique<SCTAuditingHandler>(this, sct_auditing_path);
   sct_auditing_handler()->SetMode(params_->sct_auditing_mode);
-#endif
+#endif  // BUILDFLAG(IS_CT_SUPPORTED)
 
 #if BUILDFLAG(IS_ANDROID)
   if (params_->cookie_manager)
     GetCookieManager(std::move(params_->cookie_manager));
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
   CreateURLLoaderFactoryForCertNetFetcher(
       std::move(url_loader_factory_for_cert_net_fetcher_receiver));
@@ -591,11 +591,11 @@
       url_request_context_(url_request_context),
 #if BUILDFLAG(ENABLE_REPORTING)
       is_observing_reporting_service_(false),
-#endif
+#endif  // BUILDFLAG(ENABLE_REPORTING)
 #if BUILDFLAG(IS_ANDROID)
       app_status_listener_(
           std::make_unique<NetworkContextApplicationStatusListener>()),
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
       receiver_(this, std::move(receiver)),
       first_party_sets_access_delegate_(
           /*receiver=*/mojo::NullReceiver(),
@@ -671,7 +671,7 @@
           this);
     }
   }
-#endif
+#endif  // BUILDFLAG(ENABLE_REPORTING)
 
 #if BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
   if (!dismount_closures_.empty()) {
@@ -943,7 +943,7 @@
   return params_ && params_->skip_reporting_send_permission_check;
 #else
   return false;
-#endif
+#endif  // BUILDFLAG(ENABLE_REPORTING)
 }
 
 void NetworkContext::ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
@@ -2196,7 +2196,7 @@
   else
     std::move(callback).Run(absl::nullopt);
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 const net::HttpAuthPreferences* NetworkContext::GetHttpAuthPreferences() const {
   return &http_auth_merged_preferences_;
@@ -2231,17 +2231,17 @@
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
   http_auth_merged_preferences_.set_ntlm_v2_enabled(
       http_auth_dynamic_network_service_params->ntlm_v2_enabled);
-#endif
+#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 
 #if BUILDFLAG(IS_ANDROID)
   http_auth_merged_preferences_.set_auth_android_negotiate_account_type(
       http_auth_dynamic_network_service_params->android_negotiate_account_type);
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   http_auth_merged_preferences_.set_allow_gssapi_library_load(
       http_auth_dynamic_network_service_params->allow_gssapi_library_load);
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
   if (http_auth_dynamic_network_service_params->allowed_schemes.has_value()) {
     http_auth_merged_preferences_.set_allowed_schemes(std::set<std::string>(
         http_auth_dynamic_network_service_params->allowed_schemes->begin(),
@@ -2433,7 +2433,7 @@
     builder.SetMojoWindowsSystemProxyResolver(
         std::move(params_->windows_system_proxy_resolver));
   }
-#endif
+#endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (params_->dhcp_wpad_url_client) {
@@ -2462,7 +2462,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
     cache_params.app_status_listener = app_status_listener();
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
     builder.EnableHttpCache(cache_params);
   }
 
@@ -2850,7 +2850,7 @@
         *pending_cert_verify->result, *pending_cert_verify->certificate,
         net::HostPortPair::FromURL(pending_cert_verify->url),
         pending_cert_verify->network_isolation_key);
-#endif
+#endif  // BUILDFLAG(IS_CT_SUPPORTED)
     net::TransportSecurityState::PKPStatus pin_validity =
         url_request_context_->transport_security_state()->CheckPublicKeyPins(
             net::HostPortPair::FromURL(pending_cert_verify->url),
@@ -2877,7 +2877,7 @@
     if (result != net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN &&
         ct_result != net::OK)
       result = ct_result;
-#endif
+#endif  // BUILDFLAG(IS_CT_SUPPORTED)
   }
 
   std::move(pending_cert_verify->callback)
@@ -2889,7 +2889,7 @@
 void NetworkContext::TrustAnchorUsed() {
   client_->OnTrustAnchorUsed();
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
 void NetworkContext::EnsureMounted(network::TransferableDirectory* directory) {
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 024f8f5..8ad38ad 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -1762,7 +1762,7 @@
         // We expect that every cache operation below is done synchronously
         // because we're using an in-memory backend.
 
-        // The disk cache is lazily instanitated, force it and ensure it's
+        // The disk cache is lazily instantiated, force it and ensure it's
         // valid.
         ASSERT_EQ(cache->GetBackend(&backend, base::BindOnce([](int rv) {})),
                   net::OK);
@@ -1837,7 +1837,7 @@
       "domain3",
   };
 
-  // Each bit correponds to one of the 4 domains above.
+  // Each bit corresponds to one of the 4 domains above.
   enum Domains {
     NO_DOMAINS = 0x0,
     DOMAIN0 = 0x1,
@@ -5362,7 +5362,7 @@
   // mock ProxyResolverFactory which doesn't actually evaluate it. It just
   // needs to be a data: URL to ensure the network fetch doesn't fail.
   //
-  // That said, the mock PAC evalulator being used behaves similarly to the
+  // That said, the mock PAC evaluator being used behaves similarly to the
   // script embedded in the data URL below.
   net::ProxyConfig proxy_config = net::ProxyConfig::CreateFromCustomPacURL(
       GURL("data:,function FindProxyForURL(url,host){throw url}"));
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 1c4075d9..1a1cdb61 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5108,9 +5108,6 @@
         "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5278,9 +5275,6 @@
         "test_id_prefix": "ninja://components:components_browsertests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5317,9 +5311,6 @@
         "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5633,9 +5624,6 @@
         "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6543,9 +6531,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6600,9 +6585,6 @@
         "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 331a27a6..846b55a2 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -98706,9 +98706,6 @@
         "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -98830,9 +98827,6 @@
         "test_id_prefix": "ninja://components:components_browsertests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -98859,9 +98853,6 @@
         "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -99090,9 +99081,6 @@
         "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -99795,9 +99783,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -99837,9 +99822,6 @@
         "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter"
-        ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -100161,9 +100143,6 @@
         "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -100330,9 +100309,6 @@
         "test_id_prefix": "ninja://components:components_browsertests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -100369,9 +100345,6 @@
         "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -100685,9 +100658,6 @@
         "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -101595,9 +101565,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -101652,9 +101619,6 @@
         "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter"
-        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/filters/fuchsia.browser_tests.filter b/testing/buildbot/filters/fuchsia.browser_tests.filter
index f330dff..79dcb58 100644
--- a/testing/buildbot/filters/fuchsia.browser_tests.filter
+++ b/testing/buildbot/filters/fuchsia.browser_tests.filter
@@ -449,10 +449,6 @@
 -RssLinksFetcherTest.FetchSuccessfulFromHead
 -StartupMetricsTest.ReportsValues
 
-# TODO(crbug.com/1322341): Desktop capture not yet supported
--DesktopCaptureApiTest.ChooseDesktopMedia
--DesktopCaptureApiTest.Delegation
-
 # TODO(crbug.com/1251347): Bluetooth not yet supported
 -BluetoothApiTest.DeviceEvents
 -BluetoothApiTest.DeviceInfo
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 465fc9b..ab87d0e 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1019,20 +1019,7 @@
           'shards': 50,
         },
       },
-      'linux-lacros-code-coverage': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter',
-        ],
-      },
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter',
-        ],
-      },
       'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.browser_tests.filter',
-        ],
         'swarming': {
           'quickrun_shards': 20,
         },
@@ -1514,24 +1501,6 @@
           '--gtest_filter=-FieldFormatterTest.DifferentLocales',
         ],
       },
-      # https://crbug.com/1111979,
-      'linux-lacros-code-coverage': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter',
-        ],
-      },
-      # https://crbug.com/1111979,
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter',
-        ],
-      },
-      # https://crbug.com/1111979,
-      'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.components_unittests.filter',
-        ],
-      },
     },
   },
   'compositor_unittests': {
@@ -1734,28 +1703,10 @@
           'shards': 12,
         },
       },
-      # https://crbug.com/1111979,
-      'linux-lacros-code-coverage': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter',
-        ],
-      },
-      # https://crbug.com/1111979,
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter',
-        ],
-      },
       # https://crbug.com/1111979
       'linux-lacros-tester-fyi-rel': {
         'experiment_percentage': 100,
       },
-      # https://crbug.com/1111979,
-      'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter',
-        ],
-      },
       'mac-code-coverage': {
         'args': [
           '--coverage-continuous-mode=1',
@@ -2417,24 +2368,6 @@
           'shards': 10,
         },
       },
-      # https://crbug.com/1111979
-      'linux-lacros-code-coverage': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter',
-        ],
-      },
-      # https://crbug.com/1111979
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter',
-        ],
-      },
-      # https://crbug.com/1111979
-      'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter',
-        ],
-      },
       'mac-code-coverage': {
         'args': [
           '--coverage-continuous-mode=1',
@@ -3263,22 +3196,6 @@
           'shards': 4,
         },
       },
-      # https://crbug.com/1199909,
-      'linux-lacros-code-coverage': {
-        'args': [
-           '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter',
-        ]
-      },
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter',
-        ],
-      },
-      'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.sync_integration_tests.filter',
-        ],
-      },
       'mac-code-coverage': {
         'args': [
           '--coverage-continuous-mode=1',
@@ -3517,24 +3434,6 @@
           'shards': 2,
         },
       },
-      # https://crbug.com/1111979,
-      'linux-lacros-code-coverage': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter',
-        ],
-      },
-      # https://crbug.com/1111979,
-      'linux-lacros-dbg-tests-fyi': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter',
-        ],
-      },
-      # https://crbug.com/1111979,
-      'linux-lacros-tester-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.unit_tests.filter',
-        ],
-      },
     },
   },
   'variations_smoke_tests': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 23747e6e..4a8622d9 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4796,6 +4796,26 @@
             ]
         }
     ],
+    "HTMLPopupAttribute": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "HTMLPopupAttribute"
+                    ]
+                }
+            ]
+        }
+    ],
     "HTTP2": [
         {
             "platforms": [
@@ -6210,6 +6230,21 @@
             ]
         }
     ],
+    "MojoAvoidRandomPipeId": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MojoAvoidRandomPipeId"
+                    ]
+                }
+            ]
+        }
+    ],
     "MojoInlineMessagePayloads": [
         {
             "platforms": [
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 78e731f..bdf2e4c 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -201,6 +201,7 @@
     "permissions/permission_utils.cc",
     "permissions_policy/document_policy.cc",
     "permissions_policy/permissions_policy.cc",
+    "permissions_policy/permissions_policy_declaration.cc",
     "permissions_policy/permissions_policy_mojom_traits.cc",
     "permissions_policy/policy_value.cc",
     "renderer_preferences/renderer_preferences.cc",
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index f8b833c5..b48e323f 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1105,13 +1105,6 @@
     "BrowsingTopicsBypassIPIsPubliclyRoutableCheck",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// When <dialog>s are closed, this focuses the "previously focused" element
-// which had focus when the <dialog> was first opened.
-// TODO(crbug.com/649162): Remove DialogFocusNewSpecBehavior after
-// the feature is in stable with no issues.
-const base::Feature kDialogFocusNewSpecBehavior{
-    "DialogFocusNewSpecBehavior", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Makes autofill look across shadow boundaries when collecting form controls to
 // fill.
 const base::Feature kAutofillShadowDOM{"AutofillShadowDOM",
@@ -1168,6 +1161,9 @@
 const base::Feature kHTMLParamElementUrlSupport{
     "HTMLParamElementUrlSupport", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kHTMLPopupAttribute{"HTMLPopupAttribute",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::FeatureParam<base::TimeDelta> kHttpRttThreshold{
     &kDelayLowPriorityRequestsAccordingToNetworkState, "HttpRttThreshold",
     base::Milliseconds(450)};
diff --git a/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
index 38b45df1..b7faa6e 100644
--- a/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
+++ b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
@@ -19,6 +19,8 @@
 
 namespace blink {
 
+bool operator==(const AuctionConfig& a, const AuctionConfig& b);
+
 bool operator==(const AuctionConfig::NonSharedParams& a,
                 const AuctionConfig::NonSharedParams& b) {
   return std::tie(a.interest_group_buyers, a.auction_signals, a.seller_signals,
diff --git a/third_party/blink/common/permissions_policy/permissions_policy.cc b/third_party/blink/common/permissions_policy/permissions_policy.cc
index 6d450a4..bad1255 100644
--- a/third_party/blink/common/permissions_policy/permissions_policy.cc
+++ b/third_party/blink/common/permissions_policy/permissions_policy.cc
@@ -33,41 +33,6 @@
 
 }  // namespace
 
-ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration() =
-    default;
-
-ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
-    mojom::PermissionsPolicyFeature feature)
-    : feature(feature) {}
-
-ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
-    mojom::PermissionsPolicyFeature feature,
-    const std::vector<url::Origin>& allowed_origins,
-    bool matches_all_origins,
-    bool matches_opaque_src)
-    : feature(feature),
-      allowed_origins(allowed_origins),
-      matches_all_origins(matches_all_origins),
-      matches_opaque_src(matches_opaque_src) {}
-
-ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
-    const ParsedPermissionsPolicyDeclaration& rhs) = default;
-
-ParsedPermissionsPolicyDeclaration&
-ParsedPermissionsPolicyDeclaration::operator=(
-    const ParsedPermissionsPolicyDeclaration& rhs) = default;
-
-ParsedPermissionsPolicyDeclaration::~ParsedPermissionsPolicyDeclaration() =
-    default;
-
-bool operator==(const ParsedPermissionsPolicyDeclaration& lhs,
-                const ParsedPermissionsPolicyDeclaration& rhs) {
-  return std::tie(lhs.feature, lhs.matches_all_origins, lhs.matches_opaque_src,
-                  lhs.allowed_origins) ==
-         std::tie(rhs.feature, rhs.matches_all_origins, rhs.matches_opaque_src,
-                  rhs.allowed_origins);
-}
-
 PermissionsPolicy::Allowlist::Allowlist() = default;
 
 PermissionsPolicy::Allowlist::Allowlist(const Allowlist& rhs) = default;
diff --git a/third_party/blink/common/permissions_policy/permissions_policy_declaration.cc b/third_party/blink/common/permissions_policy/permissions_policy_declaration.cc
new file mode 100644
index 0000000..8135a65
--- /dev/null
+++ b/third_party/blink/common/permissions_policy/permissions_policy_declaration.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h"
+
+#include <tuple>
+#include <vector>
+
+#include "url/origin.h"
+
+namespace blink {
+
+ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration() =
+    default;
+
+ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
+    mojom::PermissionsPolicyFeature feature)
+    : feature(feature) {}
+
+ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
+    mojom::PermissionsPolicyFeature feature,
+    const std::vector<url::Origin>& allowed_origins,
+    bool matches_all_origins,
+    bool matches_opaque_src)
+    : feature(feature),
+      allowed_origins(allowed_origins),
+      matches_all_origins(matches_all_origins),
+      matches_opaque_src(matches_opaque_src) {}
+
+ParsedPermissionsPolicyDeclaration::ParsedPermissionsPolicyDeclaration(
+    const ParsedPermissionsPolicyDeclaration& rhs) = default;
+
+ParsedPermissionsPolicyDeclaration&
+ParsedPermissionsPolicyDeclaration::operator=(
+    const ParsedPermissionsPolicyDeclaration& rhs) = default;
+
+ParsedPermissionsPolicyDeclaration::~ParsedPermissionsPolicyDeclaration() =
+    default;
+
+bool operator==(const ParsedPermissionsPolicyDeclaration& lhs,
+                const ParsedPermissionsPolicyDeclaration& rhs) {
+  return std::tie(lhs.feature, lhs.matches_all_origins, lhs.matches_opaque_src,
+                  lhs.allowed_origins) ==
+         std::tie(rhs.feature, rhs.matches_all_origins, rhs.matches_opaque_src,
+                  rhs.allowed_origins);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 96c0e812..869b9bf9b 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -250,8 +250,8 @@
     "permissions_policy/document_policy.h",
     "permissions_policy/document_policy_features.h",
     "permissions_policy/permissions_policy.h",
+    "permissions_policy/permissions_policy_declaration.h",
     "permissions_policy/permissions_policy_features.h",
-    "permissions_policy/permissions_policy_forward.h",
     "permissions_policy/policy_helper_public.h",
     "permissions_policy/policy_value.h",
     "renderer_preferences/renderer_preferences.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 7eb7d951..65cfc79a 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -501,12 +501,6 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kBrowsingTopicsBypassIPIsPubliclyRoutableCheck;
 
-// When <dialog>s are closed, this focuses the "previously focused" element
-// which had focus when the <dialog> was first opened.
-// TODO(crbug.com/649162): Remove DialogFocusNewSpecBehavior after
-// the feature is in stable with no issues.
-BLINK_COMMON_EXPORT extern const base::Feature kDialogFocusNewSpecBehavior;
-
 // Makes autofill look across shadow boundaries when collecting form controls to
 // fill.
 BLINK_COMMON_EXPORT extern const base::Feature kAutofillShadowDOM;
@@ -561,6 +555,9 @@
 // blink::features::kHTMLParamElementUrlSupport.
 BLINK_COMMON_EXPORT extern const base::Feature kHTMLParamElementUrlSupport;
 
+// TODO(crbug.com/1307772): Enables the Pop-up API.
+BLINK_COMMON_EXPORT extern const base::Feature kHTMLPopupAttribute;
+
 // The HTTP RTT threshold: decide whether the
 // `kDelayLowPriorityRequestsAccordingToNetworkState` feature can take effect
 // practically according to the network connection state.
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy.h b/third_party/blink/public/common/permissions_policy/permissions_policy.h
index abdc5e9..cb7d4fcd 100644
--- a/third_party/blink/public/common/permissions_policy/permissions_policy.h
+++ b/third_party/blink/public/common/permissions_policy/permissions_policy.h
@@ -11,6 +11,7 @@
 #include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy_features.h"
 #include "third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom-shared.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-forward.h"
@@ -90,44 +91,6 @@
 // |PermissionsPolicyFeatureDefault| in permissions_policy_features.h for
 // details)
 
-// This struct holds permissions policy allowlist data that needs to be
-// replicated between a RenderFrame and any of its associated
-// RenderFrameProxies. A list of these form a ParsedPermissionsPolicy. NOTE:
-// These types are used for replication frame state between processes.
-struct BLINK_COMMON_EXPORT ParsedPermissionsPolicyDeclaration {
-  ParsedPermissionsPolicyDeclaration();
-  explicit ParsedPermissionsPolicyDeclaration(
-      mojom::PermissionsPolicyFeature feature);
-  ParsedPermissionsPolicyDeclaration(mojom::PermissionsPolicyFeature feature,
-                                     const std::vector<url::Origin>& values,
-                                     bool matches_all_origins,
-                                     bool matches_opaque_src);
-  ParsedPermissionsPolicyDeclaration(
-      const ParsedPermissionsPolicyDeclaration& rhs);
-  ParsedPermissionsPolicyDeclaration& operator=(
-      const ParsedPermissionsPolicyDeclaration& rhs);
-  ~ParsedPermissionsPolicyDeclaration();
-
-  mojom::PermissionsPolicyFeature feature;
-
-  // An alphabetically sorted list of all the origins allowed.
-  std::vector<url::Origin> allowed_origins;
-  // Fallback value is used when feature is enabled for all or disabled for all.
-  bool matches_all_origins{false};
-  // This flag is set true for a declared policy on an <iframe sandbox>
-  // container, for a feature which is supposed to be allowed in the sandboxed
-  // document. Usually, the 'src' keyword in a declaration will cause the origin
-  // of the iframe to be present in |origins|, but for sandboxed iframes, this
-  // flag is set instead.
-  bool matches_opaque_src{false};
-};
-
-using ParsedPermissionsPolicy = std::vector<ParsedPermissionsPolicyDeclaration>;
-
-bool BLINK_COMMON_EXPORT
-operator==(const ParsedPermissionsPolicyDeclaration& lhs,
-           const ParsedPermissionsPolicyDeclaration& rhs);
-
 class BLINK_COMMON_EXPORT PermissionsPolicy {
  public:
   // Represents a collection of origins which make up an allowlist in a
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h b/third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h
new file mode 100644
index 0000000..9459956
--- /dev/null
+++ b/third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_DECLARATION_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_DECLARATION_H_
+
+#include <vector>
+
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
+#include "url/origin.h"
+
+namespace blink {
+
+// This struct holds permissions policy allowlist data that needs to be
+// replicated between a RenderFrame and any of its associated
+// RenderFrameProxies. A list of these form a ParsedPermissionsPolicy. NOTE:
+// These types are used for replication frame state between processes.
+struct BLINK_COMMON_EXPORT ParsedPermissionsPolicyDeclaration {
+  ParsedPermissionsPolicyDeclaration();
+  explicit ParsedPermissionsPolicyDeclaration(
+      mojom::PermissionsPolicyFeature feature);
+  ParsedPermissionsPolicyDeclaration(mojom::PermissionsPolicyFeature feature,
+                                     const std::vector<url::Origin>& values,
+                                     bool matches_all_origins,
+                                     bool matches_opaque_src);
+  ParsedPermissionsPolicyDeclaration(
+      const ParsedPermissionsPolicyDeclaration& rhs);
+  ParsedPermissionsPolicyDeclaration& operator=(
+      const ParsedPermissionsPolicyDeclaration& rhs);
+  ~ParsedPermissionsPolicyDeclaration();
+
+  mojom::PermissionsPolicyFeature feature;
+
+  // An alphabetically sorted list of all the origins allowed.
+  std::vector<url::Origin> allowed_origins;
+  // Fallback value is used when feature is enabled for all or disabled for all.
+  bool matches_all_origins{false};
+  // This flag is set true for a declared policy on an <iframe sandbox>
+  // container, for a feature which is supposed to be allowed in the sandboxed
+  // document. Usually, the 'src' keyword in a declaration will cause the origin
+  // of the iframe to be present in |origins|, but for sandboxed iframes, this
+  // flag is set instead.
+  bool matches_opaque_src{false};
+};
+
+using ParsedPermissionsPolicy = std::vector<ParsedPermissionsPolicyDeclaration>;
+
+bool BLINK_COMMON_EXPORT
+operator==(const ParsedPermissionsPolicyDeclaration& lhs,
+           const ParsedPermissionsPolicyDeclaration& rhs);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_DECLARATION_H_
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy_forward.h b/third_party/blink/public/common/permissions_policy/permissions_policy_forward.h
deleted file mode 100644
index 43516a3b..0000000
--- a/third_party/blink/public/common/permissions_policy/permissions_policy_forward.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FORWARD_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FORWARD_H_
-
-namespace blink {
-
-struct ParsedPermissionsPolicyDeclaration;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FORWARD_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 2dc05ed..e440af9 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -357,7 +357,7 @@
           cpp = "::blink::ParsedPermissionsPolicyDeclaration"
         },
       ]
-      traits_headers = [ "//third_party/blink/public/common/permissions_policy/permissions_policy_forward.h" ]
+      traits_headers = [ "//third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h" ]
       traits_private_headers = [ "//third_party/blink/common/permissions_policy/permissions_policy_mojom_traits.h" ]
     },
     {
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index 513b90bc..aca33c45 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -262,7 +262,6 @@
   EXPECT_TRUE(classic_script->Streamer());
   v8::TryCatch try_catch(scope.GetIsolate());
   v8::Local<v8::Script> script;
-  v8::Local<v8::Data> host_defined_options;
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -270,8 +269,9 @@
       V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
                                      *classic_script);
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
-                  scope.GetScriptState(), *classic_script, compile_options,
-                  no_cache_reason, host_defined_options)
+                  scope.GetScriptState(), *classic_script,
+                  classic_script->CreateScriptOrigin(scope.GetIsolate()),
+                  compile_options, no_cache_reason)
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -297,7 +297,6 @@
   EXPECT_TRUE(classic_script->Streamer());
   v8::TryCatch try_catch(scope.GetIsolate());
   v8::Local<v8::Script> script;
-  v8::Local<v8::Data> host_defined_options;
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -305,8 +304,9 @@
       V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
                                      *classic_script);
   EXPECT_FALSE(V8ScriptRunner::CompileScript(
-                   scope.GetScriptState(), *classic_script, compile_options,
-                   no_cache_reason, host_defined_options)
+                   scope.GetScriptState(), *classic_script,
+                   classic_script->CreateScriptOrigin(scope.GetIsolate()),
+                   compile_options, no_cache_reason)
                    .ToLocal(&script));
   EXPECT_TRUE(try_catch.HasCaught());
 }
@@ -449,7 +449,6 @@
   EXPECT_TRUE(classic_script->Streamer());
   v8::TryCatch try_catch(scope.GetIsolate());
   v8::Local<v8::Script> script;
-  v8::Local<v8::Data> host_defined_options;
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -457,8 +456,9 @@
       V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
                                      *classic_script);
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
-                  scope.GetScriptState(), *classic_script, compile_options,
-                  no_cache_reason, host_defined_options)
+                  scope.GetScriptState(), *classic_script,
+                  classic_script->CreateScriptOrigin(scope.GetIsolate()),
+                  compile_options, no_cache_reason)
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -483,7 +483,6 @@
   EXPECT_TRUE(classic_script->Streamer());
   v8::TryCatch try_catch(scope.GetIsolate());
   v8::Local<v8::Script> script;
-  v8::Local<v8::Data> host_defined_options;
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -491,8 +490,9 @@
       V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
                                      *classic_script);
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
-                  scope.GetScriptState(), *classic_script, compile_options,
-                  no_cache_reason, host_defined_options)
+                  scope.GetScriptState(), *classic_script,
+                  classic_script->CreateScriptOrigin(scope.GetIsolate()),
+                  compile_options, no_cache_reason)
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -518,7 +518,6 @@
   EXPECT_TRUE(classic_script->Streamer());
   v8::TryCatch try_catch(scope.GetIsolate());
   v8::Local<v8::Script> script;
-  v8::Local<v8::Data> host_defined_options;
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -526,8 +525,9 @@
       V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
                                      *classic_script);
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
-                  scope.GetScriptState(), *classic_script, compile_options,
-                  no_cache_reason, host_defined_options)
+                  scope.GetScriptState(), *classic_script,
+                  classic_script->CreateScriptOrigin(scope.GetIsolate()),
+                  compile_options, no_cache_reason)
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
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 d4e9a74..e66de82 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
@@ -226,9 +226,9 @@
 v8::MaybeLocal<v8::Script> V8ScriptRunner::CompileScript(
     ScriptState* script_state,
     const ClassicScript& classic_script,
+    v8::ScriptOrigin origin,
     v8::ScriptCompiler::CompileOptions compile_options,
-    v8::ScriptCompiler::NoCacheReason no_cache_reason,
-    v8::Local<v8::Data> host_defined_options) {
+    v8::ScriptCompiler::NoCacheReason no_cache_reason) {
   v8::Isolate* isolate = script_state->GetIsolate();
   if (classic_script.SourceText().length() >= v8::String::kMaxLength) {
     V8ThrowException::ThrowError(isolate, "Source file too large.");
@@ -246,22 +246,6 @@
                          script_start_position.line_.ZeroBasedInt(),
                          script_start_position.column_.ZeroBasedInt());
 
-  const SanitizeScriptErrors sanitize_script_errors =
-      classic_script.GetSanitizeScriptErrors();
-
-  // NOTE: For compatibility with WebCore, ClassicScript's line starts at
-  // 1, whereas v8 starts at 0.
-  v8::ScriptOrigin origin(
-      isolate, V8String(isolate, file_name),
-      script_start_position.line_.ZeroBasedInt(),
-      script_start_position.column_.ZeroBasedInt(),
-      sanitize_script_errors == SanitizeScriptErrors::kDoNotSanitize, -1,
-      V8String(isolate, classic_script.SourceMapUrl()),
-      sanitize_script_errors == SanitizeScriptErrors::kSanitize,
-      false,  // is_wasm
-      false,  // is_module
-      host_defined_options);
-
   if (!*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(kTraceEventCategoryGroup)) {
     return CompileScriptInternal(isolate, script_state, classic_script, origin,
                                  compile_options, no_cache_reason, nullptr);
@@ -510,9 +494,6 @@
       try_catch.SetVerbose(true);
     }
 
-    const ReferrerScriptInfo referrer_info(classic_script->BaseUrl(),
-                                           classic_script->FetchOptions());
-
     v8::Local<v8::Script> script;
 
     SingleCachedMetadataHandler* cache_handler = classic_script->CacheHandler();
@@ -521,9 +502,6 @@
           ExecutionContext::GetCodeCacheHostFromContext(execution_context),
           classic_script->SourceText());
     }
-    v8::Local<v8::Data> host_defined_options =
-        referrer_info.ToV8HostDefinedOptions(isolate,
-                                             classic_script->SourceUrl());
     v8::ScriptCompiler::CompileOptions compile_options;
     V8CodeCache::ProduceCacheOptions produce_cache_options;
     v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -531,13 +509,13 @@
         V8CodeCache::GetCompileOptions(execution_context->GetV8CacheOptions(),
                                        *classic_script);
 
+    v8::ScriptOrigin origin = classic_script->CreateScriptOrigin(isolate);
     v8::MaybeLocal<v8::Value> maybe_result;
-    if (V8ScriptRunner::CompileScript(script_state, *classic_script,
-                                      compile_options, no_cache_reason,
-                                      host_defined_options)
+    if (V8ScriptRunner::CompileScript(script_state, *classic_script, origin,
+                                      compile_options, no_cache_reason)
             .ToLocal(&script)) {
       maybe_result = V8ScriptRunner::RunCompiledScript(
-          isolate, script, host_defined_options, execution_context);
+          isolate, script, origin.GetHostDefinedOptions(), execution_context);
       probe::DidProduceCompilationCache(
           probe::ToCoreProbeSink(execution_context), *classic_script, script);
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 3d96451..dc88e42 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -111,9 +111,9 @@
   static v8::MaybeLocal<v8::Script> CompileScript(
       ScriptState*,
       const ClassicScript&,
+      v8::ScriptOrigin,
       v8::ScriptCompiler::CompileOptions,
-      v8::ScriptCompiler::NoCacheReason,
-      v8::Local<v8::Data> host_defined_options);
+      v8::ScriptCompiler::NoCacheReason);
   static v8::MaybeLocal<v8::Module> CompileModule(
       v8::Isolate*,
       const ModuleScriptCreationParams&,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index 39fb116f..c1b2888cdc 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -84,12 +84,12 @@
     v8::ScriptCompiler::CompileOptions compile_options;
     V8CodeCache::ProduceCacheOptions produce_cache_options;
     v8::ScriptCompiler::NoCacheReason no_cache_reason;
-    v8::Local<v8::Data> host_defined_options;
     std::tie(compile_options, produce_cache_options, no_cache_reason) =
         V8CodeCache::GetCompileOptions(cache_options, classic_script);
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
-        script_state, classic_script, compile_options, no_cache_reason,
-        host_defined_options);
+        script_state, classic_script,
+        classic_script.CreateScriptOrigin(isolate), compile_options,
+        no_cache_reason);
     if (compiled_script.IsEmpty()) {
       return false;
     }
@@ -114,10 +114,10 @@
           ExecutionContext::GetCodeCacheHostFromContext(execution_context),
           classic_script.SourceText());
     }
-    v8::Local<v8::Data> host_defined_options;
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
-        script_state, classic_script, compile_options, no_cache_reason,
-        host_defined_options);
+        script_state, classic_script,
+        classic_script.CreateScriptOrigin(isolate), compile_options,
+        no_cache_reason);
     if (compiled_script.IsEmpty()) {
       return false;
     }
diff --git a/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc b/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
index dec4baf..c5ebede 100644
--- a/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
+++ b/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
@@ -12,8 +12,8 @@
 #include "base/time/default_tick_clock.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 
 namespace blink {
@@ -113,13 +113,15 @@
   DCHECK(!std::isinf(memory_threshold_mb_));
 
   MemoryUsageMonitor::Instance().AddObserver(this);
-  ThreadScheduler::Current()->AddRAILModeObserver(this);
+  ThreadScheduler::Current()->ToMainThreadScheduler()->AddRAILModeObserver(
+      this);
 }
 
 UserLevelMemoryPressureSignalGenerator::
     ~UserLevelMemoryPressureSignalGenerator() {
   MemoryUsageMonitor::Instance().RemoveObserver(this);
-  ThreadScheduler::Current()->RemoveRAILModeObserver(this);
+  ThreadScheduler::Current()->ToMainThreadScheduler()->RemoveRAILModeObserver(
+      this);
 }
 
 void UserLevelMemoryPressureSignalGenerator::SetTickClockForTesting(
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index 4afb220..5a461bbd 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
 #include "third_party/blink/renderer/core/css/rule_set.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
@@ -324,7 +325,8 @@
 
   if (!popup_style_sheet_ && element.HasValidPopupAttribute()) {
     // TODO: We should assert that this sheet only contains rules for popups.
-    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+        element.GetDocument().GetExecutionContext()));
     popup_style_sheet_ =
         ParseUASheet(UncompressResourceAsASCIIString(IDR_UASTYLE_POPUP_CSS));
     AddRulesToDefaultStyleSheets(popup_style_sheet_, NamespaceType::kHTML);
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index ae5560e..2c0e249 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -510,8 +511,10 @@
     {"where", CSSSelector::kPseudoWhere},
 };
 
-CSSSelector::PseudoType CSSSelector::NameToPseudoType(const AtomicString& name,
-                                                      bool has_arguments) {
+CSSSelector::PseudoType CSSSelector::NameToPseudoType(
+    const AtomicString& name,
+    bool has_arguments,
+    const Document* document) {
   if (name.IsNull() || !name.Is8Bit())
     return CSSSelector::kPseudoUnknown;
 
@@ -564,12 +567,17 @@
       !RuntimeEnabledFeatures::CSSPseudoPlayingPausedEnabled())
     return CSSSelector::kPseudoUnknown;
 
-  if (match->type == CSSSelector::kPseudoTopLayer &&
-      !RuntimeEnabledFeatures::HTMLPopupAttributeEnabled())
+  // We enable parsing of the pop-up pseudo classes in the case that we *don't*
+  // have a document, since that mostly/always occurs when parsing UA
+  // stylesheets.
+  bool popup_attribute_enabled =
+      !document || RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+                       document->GetExecutionContext());
+  if (match->type == CSSSelector::kPseudoTopLayer && !popup_attribute_enabled)
     return CSSSelector::kPseudoUnknown;
 
   if (match->type == CSSSelector::kPseudoPopupHidden &&
-      !RuntimeEnabledFeatures::HTMLPopupAttributeEnabled())
+      !popup_attribute_enabled)
     return CSSSelector::kPseudoUnknown;
 
   if (match->type == CSSSelector::kPseudoHighlight &&
@@ -630,10 +638,11 @@
 }
 #endif
 
-void CSSSelector::UpdatePseudoPage(const AtomicString& value) {
+void CSSSelector::UpdatePseudoPage(const AtomicString& value,
+                                   const Document* document) {
   DCHECK_EQ(Match(), kPagePseudoClass);
   SetValue(value);
-  PseudoType type = CSSSelectorParser::ParsePseudoType(value, false);
+  PseudoType type = CSSSelectorParser::ParsePseudoType(value, false, document);
   if (type != kPseudoFirstPage && type != kPseudoLeftPage &&
       type != kPseudoRightPage) {
     type = kPseudoUnknown;
@@ -647,8 +656,8 @@
                                    CSSParserMode mode) {
   DCHECK(match_ == kPseudoClass || match_ == kPseudoElement);
   AtomicString lower_value = value.LowerASCII();
-  PseudoType pseudo_type =
-      CSSSelectorParser::ParsePseudoType(lower_value, has_arguments);
+  PseudoType pseudo_type = CSSSelectorParser::ParsePseudoType(
+      lower_value, has_arguments, context.GetDocument());
   SetPseudoType(pseudo_type);
   SetValue(pseudo_type == kPseudoState ? value : lower_value);
 
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index 6085371..a8a6a7f4 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -37,6 +37,7 @@
 
 class CSSParserContext;
 class CSSSelectorList;
+class Document;
 
 // This class represents a simple selector for a StyleRule.
 
@@ -322,8 +323,10 @@
                         const CSSParserContext&,
                         bool has_arguments,
                         CSSParserMode);
-  void UpdatePseudoPage(const AtomicString&);
-  static PseudoType NameToPseudoType(const AtomicString&, bool has_arguments);
+  void UpdatePseudoPage(const AtomicString&, const Document*);
+  static PseudoType NameToPseudoType(const AtomicString&,
+                                     bool has_arguments,
+                                     const Document* document);
   static PseudoId GetPseudoId(PseudoType);
 
   // Selectors are kept in an array by CSSSelectorList. The next component of
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index a9a9fec..27329b999 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -642,11 +642,8 @@
                     media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
@@ -661,11 +658,8 @@
                     media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
@@ -681,11 +675,8 @@
     TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
@@ -699,11 +690,8 @@
     TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
@@ -716,11 +704,8 @@
     TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
@@ -733,11 +718,8 @@
     TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
 
     // Test again with the feature disabled
-    ScopedCSSDynamicRangeMediaQueriesForTest const disable_feature{false};
     ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
         false};
-    TestMQEvaluator(g_dynamic_range_feature_disabled_cases,
-                    media_query_evaluator);
     TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
                     media_query_evaluator);
   }
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index f5c33a5..716ea8be 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenized_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
@@ -95,10 +96,8 @@
   if (media_feature == media_feature_names::kPrefersReducedMotionMediaFeature)
     return ident == CSSValueID::kNoPreference || ident == CSSValueID::kReduce;
 
-  if (RuntimeEnabledFeatures::CSSDynamicRangeMediaQueriesEnabled()) {
-    if (media_feature == media_feature_names::kDynamicRangeMediaFeature)
-      return ident == CSSValueID::kStandard || ident == CSSValueID::kHigh;
-  }
+  if (media_feature == media_feature_names::kDynamicRangeMediaFeature)
+    return ident == CSSValueID::kStandard || ident == CSSValueID::kHigh;
 
   if (RuntimeEnabledFeatures::CSSVideoDynamicRangeMediaQueriesEnabled()) {
     if (media_feature == media_feature_names::kVideoDynamicRangeMediaFeature)
@@ -326,8 +325,12 @@
   CSSParserContext::ParserModeOverridingScope scope(context, kHTMLStandardMode);
 
   if (CSSVariableParser::IsValidVariableName(media_feature)) {
-    if (const CSSValue* value =
-            CSSVariableParser::ParseDeclarationValue({range}, false, context)) {
+    CSSTokenizedValue tokenized_value{range};
+    if (CSSParserImpl::RemoveImportantAnnotationIfPresent(tokenized_value)) {
+      return absl::nullopt;
+    }
+    if (const CSSValue* value = CSSVariableParser::ParseDeclarationValue(
+            tokenized_value, false, context)) {
       while (!range.AtEnd())
         range.Consume();
       return MediaQueryExpValue(*value);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index 90334bf..e0e0fb4 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -60,7 +60,7 @@
   CSSTokenizer tokenizer(selector);
   const auto tokens = tokenizer.TokenizeToEOF();
   return CSSParserImpl::ParsePageSelector(CSSParserTokenRange(tokens),
-                                          style_sheet_contents);
+                                          style_sheet_contents, context);
 }
 
 StyleRuleBase* CSSParser::ParseRule(const CSSParserContext* context,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 26f9230..25097ac 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -336,9 +336,11 @@
   return result;
 }
 
+// static
 CSSSelectorList CSSParserImpl::ParsePageSelector(
     CSSParserTokenRange range,
-    StyleSheetContents* style_sheet) {
+    StyleSheetContents* style_sheet,
+    const CSSParserContext& context) {
   // We only support a small subset of the css-page spec.
   range.ConsumeWhitespace();
   AtomicString type_selector;
@@ -365,7 +367,7 @@
     selector = std::make_unique<CSSParserSelector>();
     if (!pseudo.IsNull()) {
       selector->SetMatch(CSSSelector::kPagePseudoClass);
-      selector->UpdatePseudoPage(pseudo.LowerASCII());
+      selector->UpdatePseudoPage(pseudo.LowerASCII(), context.GetDocument());
       if (selector->GetPseudoType() == CSSSelector::kPseudoUnknown)
         return CSSSelectorList();
     }
@@ -993,7 +995,8 @@
     return nullptr;
   CSSParserTokenStream::BlockGuard guard(stream);
 
-  CSSSelectorList selector_list = ParsePageSelector(prelude, style_sheet_);
+  CSSSelectorList selector_list =
+      ParsePageSelector(prelude, style_sheet_, *context_);
   if (!selector_list.IsValid())
     return nullptr;  // Parse error, invalid @page selector
 
@@ -1571,13 +1574,14 @@
       tokenized_value.range = tokenized_value.range.MakeSubRange(first, last);
 
       // Truncate the text to remove the delimiter and everything after it.
-      DCHECK_NE(tokenized_value.text.ToString().find('!'), kNotFound);
-      unsigned truncated_length = tokenized_value.text.length() - 1;
-      while (tokenized_value.text[truncated_length] != '!')
-        --truncated_length;
-      tokenized_value.text =
-          StringView(tokenized_value.text, 0, truncated_length);
-
+      if (!tokenized_value.text.IsEmpty()) {
+        DCHECK_NE(tokenized_value.text.ToString().find('!'), kNotFound);
+        unsigned truncated_length = tokenized_value.text.length() - 1;
+        while (tokenized_value.text[truncated_length] != '!')
+          --truncated_length;
+        tokenized_value.text =
+            StringView(tokenized_value.text, 0, truncated_length);
+      }
       return true;
     }
   }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
index c8673032..6987a48 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -122,7 +122,8 @@
       bool allow_import_rules = true,
       std::unique_ptr<CSSTokenizerBase> tokenizer = nullptr);
   static CSSSelectorList ParsePageSelector(CSSParserTokenRange,
-                                           StyleSheetContents*);
+                                           StyleSheetContents*,
+                                           const CSSParserContext& context);
 
   static std::unique_ptr<Vector<double>> ParseKeyframeKeyList(const String&);
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_selector.h b/third_party/blink/renderer/core/css/parser/css_parser_selector.h
index c16b03d..0e6d26e 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_selector.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_selector.h
@@ -83,8 +83,8 @@
                         CSSParserMode mode) const {
     selector_->UpdatePseudoType(value, context, has_arguments, mode);
   }
-  void UpdatePseudoPage(const AtomicString& value) {
-    selector_->UpdatePseudoPage(value);
+  void UpdatePseudoPage(const AtomicString& value, const Document* document) {
+    selector_->UpdatePseudoPage(value, document);
   }
 
   void AdoptSelectorVector(CSSSelectorVector& selector_vector);
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 4ba7e02..ddb4a3e8 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -394,11 +394,13 @@
   return selector;
 }
 
+// static
 CSSSelector::PseudoType CSSSelectorParser::ParsePseudoType(
     const AtomicString& name,
-    bool has_arguments) {
+    bool has_arguments,
+    const Document* document) {
   CSSSelector::PseudoType pseudo_type =
-      CSSSelector::NameToPseudoType(name, has_arguments);
+      CSSSelector::NameToPseudoType(name, has_arguments, document);
 
   if (!RuntimeEnabledFeatures::WebKitScrollbarStylingEnabled()) {
     // Don't convert ::-webkit-scrollbar into webkit custom element pseudos -
@@ -428,6 +430,7 @@
   return CSSSelector::PseudoType::kPseudoUnknown;
 }
 
+// static
 PseudoId CSSSelectorParser::ParsePseudoElement(const String& selector_string,
                                                const Node* parent) {
   CSSTokenizer tokenizer(selector_string);
@@ -448,7 +451,8 @@
     CSSParserToken selector_name_token = range.Consume();
     PseudoId pseudo_id = CSSSelector::GetPseudoId(
         ParsePseudoType(selector_name_token.Value().ToAtomicString(),
-                        selector_name_token.GetType() == kFunctionToken));
+                        selector_name_token.GetType() == kFunctionToken,
+                        parent ? &parent->GetDocument() : nullptr));
 
     if (PseudoElement::IsWebExposed(pseudo_id, parent) &&
         ((PseudoElementHasArguments(pseudo_id) &&
@@ -463,6 +467,7 @@
   return kPseudoIdNone;
 }
 
+// static
 AtomicString CSSSelectorParser::ParsePseudoElementArgument(
     const String& selector_string) {
   CSSTokenizer tokenizer(selector_string);
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.h b/third_party/blink/renderer/core/css/parser/css_selector_parser.h
index 25a43a8..3ddee28 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.h
@@ -50,7 +50,8 @@
                                       const CSSParserContext*);
 
   static CSSSelector::PseudoType ParsePseudoType(const AtomicString&,
-                                                 bool has_arguments);
+                                                 bool has_arguments,
+                                                 const Document*);
   static PseudoId ParsePseudoElement(const String&, const Node*);
   // Returns the argument of a parameterized pseudo-element. For example, for
   // '::highlight(foo)' it returns 'foo'.
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 474ec10..ba364cb 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -67,6 +68,7 @@
   DetermineIfSubtreeHasFocus();
   DetermineIfSubtreeHasSelection();
   DetermineIfSubtreeHasTopLayerElement();
+  DetermineIfInSharedElementTransitionChain();
 }
 
 void DisplayLockContext::SetRequestedState(EContentVisibility state) {
@@ -363,6 +365,9 @@
 
 bool DisplayLockContext::ShouldPrePaintChildren() const {
   return !is_locked_ || forced_info_.is_forced(ForcedPhase::kPrePaint) ||
+         (document_->GetDisplayLockDocumentState()
+              .ActivatableDisplayLocksForced() &&
+          IsActivatable(DisplayLockActivationReason::kAny)) ||
          (element_->GetLayoutObject() &&
           element_->GetLayoutObject()->IsShapingDeferred());
 }
@@ -828,6 +833,7 @@
   DetermineIfSubtreeHasFocus();
   DetermineIfSubtreeHasSelection();
   DetermineIfSubtreeHasTopLayerElement();
+  DetermineIfInSharedElementTransitionChain();
 }
 
 void DisplayLockContext::WillStartLifecycleUpdate(const LocalFrameView& view) {
@@ -1120,6 +1126,56 @@
   }
 }
 
+void DisplayLockContext::DetermineIfInSharedElementTransitionChain() {
+  ResetAndDetermineIfAncestorIsSharedElement();
+  if (ConnectedToView())
+    document_->GetDisplayLockDocumentState().UpdateSharedElementAncestorLocks();
+}
+
+void DisplayLockContext::ResetInSharedElementTransitionChain() {
+  SetRenderAffectingState(RenderAffectingState::kSharedElementTransitionChain,
+                          false);
+}
+
+void DisplayLockContext::SetInSharedElementTransitionChain() {
+  SetRenderAffectingState(RenderAffectingState::kSharedElementTransitionChain,
+                          true);
+}
+
+bool DisplayLockContext::IsInSharedElementAncestorChain() const {
+  return render_affecting_state_[static_cast<int>(
+      RenderAffectingState::kSharedElementTransitionChain)];
+}
+
+void DisplayLockContext::ResetAndDetermineIfAncestorIsSharedElement() {
+  ResetInSharedElementTransitionChain();
+  if (!ConnectedToView())
+    return;
+
+  auto* supplement = DocumentTransitionSupplement::FromIfExists(*document_);
+  if (!supplement)
+    return;
+
+  auto* transition = supplement->GetTransition();
+  bool has_shared_element_ancestor = false;
+  for (auto* candidate = element_.Get(); candidate;
+       candidate = FlatTreeTraversal::ParentElement(*candidate)) {
+    // We don't care about document element as the ancestor, since it's common
+    // to have one and it will be clipped by viewport anyway.
+    if (candidate->IsDocumentElement())
+      continue;
+
+    if (auto* layout_object = candidate->GetLayoutObject();
+        layout_object &&
+        transition->IsRepresentedViaPseudoElements(*layout_object)) {
+      has_shared_element_ancestor = true;
+      break;
+    }
+  }
+  SetRenderAffectingState(RenderAffectingState::kSharedElementTransitionChain,
+                          has_shared_element_ancestor);
+}
+
 void DisplayLockContext::ClearHasTopLayerElement() {
   // Note that this is asynchronous because it can happen during a layout detach
   // which is a bad time to relock a content-visibility auto element (since it
@@ -1241,7 +1297,8 @@
         !state(RenderAffectingState::kSubtreeHasSelection) &&
         !state(RenderAffectingState::kAutoStateUnlockedUntilLifecycle) &&
         !state(RenderAffectingState::kAutoUnlockedForPrint) &&
-        !state(RenderAffectingState::kSubtreeHasTopLayerElement)));
+        !state(RenderAffectingState::kSubtreeHasTopLayerElement) &&
+        !state(RenderAffectingState::kSharedElementTransitionChain)));
 
   // For shaping-deferred boxes, we'd like to unlock permanently.
   if (IsShapingDeferred() && state_ != EContentVisibility::kVisible &&
@@ -1285,6 +1342,8 @@
       return "AutoUnlockedForPrint";
     case RenderAffectingState::kSubtreeHasTopLayerElement:
       return "SubtreeHasTopLayerElement";
+    case RenderAffectingState::kSharedElementTransitionChain:
+      return "SharedElementTransitionChain";
     case RenderAffectingState::kNumRenderAffectingStates:
       break;
   }
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index be6364a..15285dc 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -180,6 +180,7 @@
     if (IsLocked() && IsActivatable(DisplayLockActivationReason::kAny)) {
       MarkForStyleRecalcIfNeeded();
       MarkForLayoutIfNeeded();
+      MarkAncestorsForPrePaintIfNeeded();
     }
   }
 
@@ -220,6 +221,30 @@
   bool IsShapingDeferred() const;
   bool IsInclusiveDescendantOf(const LayoutObject& ancestor) const;
 
+  // This updates the rendering state to account for the fact that one of the
+  // ancestor may be a non-root shared element, which should cause the
+  // content-visibility: auto locks to be unlocked.
+  // This function is called anytime a descendant or ancestor shared element may
+  // change. Note that to determine the descendants, this function uses a
+  // document level function to mark all ancestors of shared elements. This
+  // updates all display locks on such ancestor chains, but it should be a no-op
+  // for any lock except this one. This is the most optimal way to do this and
+  // not a necessary component of the function.
+  // Note that this function also does not consider the root as a shared element
+  // (even though it might be). The reason for this is that root is treated
+  // different in SET: it is clipped by a viewport or some margin around, and
+  // it's captured by default. This means that it will frequently be in the
+  // chain of all display locks, and we want to avoid unnecessary unlocks.
+  void DetermineIfInSharedElementTransitionChain();
+  // Note that the following only checks the ancestor chain, and does not
+  // consider shared descendants. This is an optimization to be used by the
+  // document state.
+  void ResetAndDetermineIfAncestorIsSharedElement();
+  // State control for shared element render affecting state.
+  void ResetInSharedElementTransitionChain();
+  void SetInSharedElementTransitionChain();
+  bool IsInSharedElementAncestorChain() const;
+
  private:
   // Give access to |NotifyForcedUpdateScopeStarted()| and
   // |NotifyForcedUpdateScopeEnded()|.
@@ -465,6 +490,7 @@
     kAutoStateUnlockedUntilLifecycle,
     kAutoUnlockedForPrint,
     kSubtreeHasTopLayerElement,
+    kSharedElementTransitionChain,
     kNumRenderAffectingStates
   };
   void SetRenderAffectingState(RenderAffectingState state, bool flag);
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
index caaf933..dfa07d90 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
@@ -6,6 +6,7 @@
 
 #include "base/trace_event/trace_event.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
@@ -249,6 +250,45 @@
   return had_locked_ancestor;
 }
 
+void DisplayLockDocumentState::NotifySharedElementPseudoTreeChanged() {
+  // Note that this function doesn't use
+  // DisplayLockContext::DetermineIfInSharedElementTransitionChain, since that
+  // would mean we have to call UpdateSharedElementAncestorLocks for each lock.
+  // This function only calls it once by hoisting it out of the context calls.
+
+  // Reset the flag and determine if the ancestor is shared element.
+  for (auto context : display_lock_contexts_)
+    context->ResetAndDetermineIfAncestorIsSharedElement();
+
+  // Also process the shared elements to check if the shared element's ancestors
+  // are locks. These two parts give us the full chain (either locks are
+  // ancestors of shared or shared are ancestor of locks).
+  UpdateSharedElementAncestorLocks();
+}
+
+void DisplayLockDocumentState::UpdateSharedElementAncestorLocks() {
+  auto* supplement = DocumentTransitionSupplement::FromIfExists(*document_);
+  if (!supplement)
+    return;
+
+  const auto& shared_elements =
+      supplement->GetTransition()->GetTransitioningElements();
+  for (auto element : shared_elements) {
+    auto* ancestor = element.Get();
+    // When the element which has c-v:auto is itself a shared element, marking
+    // it as such could go in either walk (from the function naming) but it
+    // happens in the ancestor chain check and skipped here. This DCHECK
+    // verifies this.
+    DCHECK(!element->GetDisplayLockContext() ||
+           element->GetDisplayLockContext()->IsInSharedElementAncestorChain());
+
+    while ((ancestor = FlatTreeTraversal::ParentElement(*ancestor))) {
+      if (auto* context = ancestor->GetDisplayLockContext())
+        context->SetInSharedElementTransitionChain();
+    }
+  }
+}
+
 void DisplayLockDocumentState::NotifySelectionRemoved() {
   for (auto context : display_lock_contexts_)
     context->NotifySubtreeLostSelection();
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
index da689c0..e5b526e 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
@@ -86,6 +86,13 @@
   // Notify the display locks that selection was removed.
   void NotifySelectionRemoved();
 
+  // Notify the display locks that shared elements have changed.
+  void NotifySharedElementPseudoTreeChanged();
+
+  // Updates only the ancestor locks of the shared element transition elements.
+  // This is an optimization to be used by the display lock context.
+  void UpdateSharedElementAncestorLocks();
+
   // This is called when the forced scope is created or destroyed in
   // |ScopedForcedUpdate::Impl|. This is used to ensure that we can create new
   // locks that are immediately forced by the existing forced scope.
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.h b/third_party/blink/renderer/core/document_transition/document_transition.h
index 0bfb69ae..70f8aa17 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition.h
@@ -124,6 +124,12 @@
   // LifecycleNotificationObserver overrides.
   void WillStartLifecycleUpdate(const LocalFrameView&) override;
 
+  // Return non-root transitioning elements.
+  VectorOf<Element> GetTransitioningElements() const {
+    return style_tracker_ ? style_tracker_->GetTransitioningElements()
+                          : VectorOf<Element>{};
+  }
+
  private:
   friend class DocumentTransitionTest;
 
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
index a6950a7..07e0511 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_content_element.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_pseudo_element_base.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_utils.h"
@@ -234,6 +235,19 @@
 void DocumentTransitionStyleTracker::AddSharedElementsFromCSS() {
   DCHECK(document_ && document_->View());
 
+  // TODO(vmpstr): This needs some thought :(
+  // From khushalsagar:
+  // We have to change this such that discovering of tags happens at the end of
+  // reaching the paint phase of the lifecycle update at the next frame. So the
+  // way this would be setup is:
+  // - At the next frame, acquire the scope before dispatching raf callbacks.
+  // - When we hit paint, discover all the tags and then release the scope.
+  // We can have recursive lifecycle updates after this to invalidate the pseudo
+  // DOM but the decision for which elements will be shared is not changeable
+  // after that point.
+  auto scope =
+      document_->GetDisplayLockDocumentState().GetScopedForceActivatableLocks();
+
   // We need our paint layers, and z-order lists which is done during
   // compositing inputs update.
   document_->View()->UpdateLifecycleToCompositingInputsClean(
@@ -427,6 +441,20 @@
   root_effect_node_ = nullptr;
 }
 
+VectorOf<Element> DocumentTransitionStyleTracker::GetTransitioningElements()
+    const {
+  // In stable states, we don't have shared elements.
+  if (state_ == State::kIdle || state_ == State::kCaptured)
+    return {};
+
+  VectorOf<Element> result;
+  for (auto& entry : element_data_map_) {
+    if (entry.value->target_element)
+      result.push_back(entry.value->target_element);
+  }
+  return result;
+}
+
 bool DocumentTransitionStyleTracker::Start() {
   DCHECK_EQ(state_, State::kCaptured);
 
@@ -940,11 +968,10 @@
     // means that we should update the paint properties to update the shared
     // element id.
     object->SetNeedsPaintPropertyUpdate();
-
-    auto* box = entry.value->target_element->GetLayoutBox();
-    if (!box || !box->HasSelfPaintingLayer())
-      continue;
   }
+
+  document_->GetDisplayLockDocumentState()
+      .NotifySharedElementPseudoTreeChanged();
 }
 
 HashSet<AtomicString> DocumentTransitionStyleTracker::AllRootTags() const {
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
index 589a3e1b6..a42d5b5 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
@@ -127,6 +127,8 @@
   // rules based on the current phase of the transition.
   StyleRequest::RulesToInclude StyleRulesToInclude() const;
 
+  VectorOf<Element> GetTransitioningElements() const;
+
  private:
   class ImageWrapperPseudoElement;
 
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 6877410..84eeefa4 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2394,7 +2394,8 @@
 
   if (params.reason == AttributeModificationReason::kByParser &&
       name == html_names::kDefaultopenAttr && HasValidPopupAttribute()) {
-    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+        GetDocument().GetExecutionContext()));
     DCHECK(!isConnected());
     GetPopupData()->setHadDefaultOpenWhenParsed(true);
   }
@@ -2414,7 +2415,8 @@
 }
 
 void Element::UpdatePopupAttribute(String value) {
-  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled()) {
+  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          GetDocument().GetExecutionContext())) {
     // If the feature flag isn't enabled, give a console warning about this
     // usage of the 'popup' attribute, which is likely to cause breakage when
     // the feature ships.
@@ -2486,7 +2488,8 @@
 
 // This should be true when :top-layer should match.
 bool Element::popupOpen() const {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   if (auto* popup_data = GetPopupData())
     return popup_data->visibilityState() == PopupVisibilityState::kShowing;
   return false;
@@ -2501,7 +2504,8 @@
 // 3. Set the :top-layer pseudo class.
 // 4. Update style. (Animations/transitions happen here.)
 void Element::showPopUp(ExceptionState& exception_state) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   if (!HasValidPopupAttribute()) {
     return exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
@@ -2536,7 +2540,7 @@
             HidePopupForcingLevel::kHideAfterAnimations);
       }
       // Then hide open pop-ups that aren't ancestors of this hint.
-      if (const Element* hint_ancestor = NearestOpenAncestralPopup(this)) {
+      if (const Element* hint_ancestor = NearestOpenAncestralPopup(*this)) {
         HideAllPopupsUntil(hint_ancestor, document,
                            HidePopupFocusBehavior::kNone,
                            HidePopupForcingLevel::kHideAfterAnimations,
@@ -2547,7 +2551,7 @@
       // stack, and hide any hint pop-ups. Because this pop-up isn't yet in the
       // stack, we call NearestOpenAncestralPopup to find this pop-up's
       // ancestor, if any.
-      const Element* auto_ancestor = NearestOpenAncestralPopup(this);
+      const Element* auto_ancestor = NearestOpenAncestralPopup(*this);
       HideAllPopupsUntil(auto_ancestor, document, HidePopupFocusBehavior::kNone,
                          HidePopupForcingLevel::kHideAfterAnimations,
                          HidePopupIndependence::kHideUnrelated);
@@ -2610,7 +2614,8 @@
                                  HidePopupFocusBehavior focus_behavior,
                                  HidePopupForcingLevel forcing_level,
                                  HidePopupIndependence popup_independence) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      document.GetExecutionContext()));
   DCHECK(!endpoint || endpoint->HasValidPopupAttribute());
 
   // If we're forcing a popup to hide immediately, first hide any other popups
@@ -2640,7 +2645,7 @@
       // If there is a hint showing that is a descendant of something on the
       // stack, then the hint should be hidden before that ancestor is hidden,
       // regardless of popup_independence.
-      hint_ancestor = NearestOpenAncestralPopup(document.PopupHintShowing());
+      hint_ancestor = NearestOpenAncestralPopup(*document.PopupHintShowing());
       if (!hint_ancestor &&
           popup_independence == HidePopupIndependence::kHideUnrelated) {
         document.PopupHintShowing()->HidePopUpInternal(focus_behavior,
@@ -2663,7 +2668,8 @@
 }
 
 void Element::hidePopUp(ExceptionState& exception_state) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   if (!HasValidPopupAttribute()) {
     return exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
@@ -2698,7 +2704,8 @@
 // 6. Update style.
 void Element::HidePopUpInternal(HidePopupFocusBehavior focus_behavior,
                                 HidePopupForcingLevel forcing_level) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   auto& document = GetDocument();
   if (PopupType() == PopupValueType::kAuto ||
@@ -2796,7 +2803,8 @@
 }
 
 void Element::PopupHideFinishIfNeeded() {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   GetDocument().PopupsWaitingToHide().erase(this);
   GetDocument().RemoveFromTopLayer(this);
   // Re-apply display:none.
@@ -2808,7 +2816,8 @@
 }
 
 void Element::SetPopupFocusOnShow() {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   // The layout must be updated here because we call Element::isFocusable,
   // which requires an up-to-date layout.
   GetDocument().UpdateStyleAndLayoutTreeForNode(this);
@@ -2854,7 +2863,8 @@
 // https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area
 // does not include dialogs or popups yet.
 Element* Element::GetPopupFocusableArea(bool autofocus_only) const {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   Node* next = nullptr;
   for (Node* node = FlatTreeTraversal::FirstChild(*this); node; node = next) {
     next = FlatTreeTraversal::Next(*node, this);
@@ -2950,10 +2960,10 @@
 // pop-up found during the tree-walk is included in the search. If it is false,
 // the |node| parameter must be a pop-up, and the highest pop-up *below* that
 // starting pop- up will be returned.
-const Element* Element::NearestOpenAncestralPopup(const Node* node,
+const Element* Element::NearestOpenAncestralPopup(const Node& node,
                                                   bool inclusive) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
-  DCHECK(node);
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      node.GetDocument().GetExecutionContext()));
   // popup_positions is a map from all showing (or about-to-show) pop-ups to
   // their position in the pop-up stack.
   HeapHashMap<Member<const Element>, int> popup_positions;
@@ -2961,12 +2971,12 @@
   // back to the pop-up itself.
   HeapHashMap<Member<const Element>, Member<const Element>> anchors_to_popups;
   int indx = 0;
-  for (auto popup : node->GetDocument().PopupStack()) {
+  for (auto popup : node.GetDocument().PopupStack()) {
     popup_positions.Set(popup, indx++);
     if (popup->anchorElement())
       anchors_to_popups.Set(popup->anchorElement(), popup);
   }
-  auto* hint_showing = node->GetDocument().PopupHintShowing();
+  auto* hint_showing = node.GetDocument().PopupHintShowing();
   if (hint_showing) {
     popup_positions.Set(hint_showing, indx++);
     if (hint_showing->anchorElement()) {
@@ -2991,7 +3001,7 @@
     // For inclusive mode, we need to walk up the tree until we find an open
     // pop-up, or an invoker for an open pop-up, and then modify the upper bound
     // to include the highest such pop-up found, if any.
-    for (const Node* current_node = node; current_node;
+    for (const Node* current_node = &node; current_node;
          current_node = FlatTreeTraversal::Parent(*current_node)) {
       if (auto* current_element = DynamicTo<Element>(current_node);
           current_element && current_element->HasValidPopupAttribute() &&
@@ -3013,12 +3023,11 @@
   }
   HashSet<Member<const Node>> seen;
   return NearestOpenAncestralPopupRecursive(
-      node, popup_positions, anchors_to_popups, upper_bound, seen);
+      &node, popup_positions, anchors_to_popups, upper_bound, seen);
 }
 
 // static
 void Element::HandlePopupLightDismiss(const Event& event) {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
   if (event.GetEventPath().IsEmpty())
     return;
   DCHECK_NE(Event::PhaseType::kNone, event.eventPhase());
@@ -3030,11 +3039,13 @@
   if (!target_node)
     return;
   auto& document = target_node->GetDocument();
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      document.GetExecutionContext()));
   DCHECK(document.TopmostPopupAutoOrHint());
   const AtomicString& event_type = event.type();
   if (event_type == event_type_names::kMousedown) {
     document.SetPopUpMousedownTarget(
-        NearestOpenAncestralPopup(target_node, /*inclusive*/ true));
+        NearestOpenAncestralPopup(*target_node, /*inclusive*/ true));
   } else if (event_type == event_type_names::kMouseup) {
     // Hide everything up to the clicked element. We do this on mouseup,
     // rather than mousedown or click, primarily for accessibility concerns.
@@ -3045,7 +3056,7 @@
     // mouse-drag on a pop-up, and finishes off the pop-up (to highlight text),
     // the ancestral pop-up is stored in mousedown and compared here.
     auto* ancestor_pop_up =
-        NearestOpenAncestralPopup(target_node, /*inclusive*/ true);
+        NearestOpenAncestralPopup(*target_node, /*inclusive*/ true);
     bool same_target = ancestor_pop_up == document.PopUpMousedownTarget();
     document.SetPopUpMousedownTarget(nullptr);
     if (same_target) {
@@ -3066,7 +3077,7 @@
     // If we focus an element, hide all pop-ups outside the that element's
     // pop-up tree, including unrelated pop-ups.
     HideAllPopupsUntil(
-        NearestOpenAncestralPopup(target_node, /*inclusive*/ true), document,
+        NearestOpenAncestralPopup(*target_node, /*inclusive*/ true), document,
         HidePopupFocusBehavior::kNone,
         HidePopupForcingLevel::kHideAfterAnimations,
         HidePopupIndependence::kHideUnrelated);
@@ -3075,14 +3086,16 @@
 
 void Element::InvokePopup(Element* invoker) {
   DCHECK(invoker);
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   GetPopupData()->setInvoker(invoker);
   showPopUp(ASSERT_NO_EXCEPTION);
 }
 
 Element* Element::anchorElement() const {
-  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled())
+  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          GetDocument().GetExecutionContext()))
     return nullptr;
   if (!HasValidPopupAttribute())
     return nullptr;
@@ -3095,7 +3108,8 @@
 }
 
 Element* Element::PopupHoverTargetElement() const {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   if (!FastHasAttribute(html_names::kPopuphovertargetAttr))
     return nullptr;
   Element* popup_element = GetTreeScope().getElementById(
@@ -3114,7 +3128,8 @@
 // property, which works for all pop-up types, and needs to keep pop-ups open
 // when a descendant is hovered.
 bool Element::IsNodePopUpDescendant(const Node& node) const {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   if (PopupType() == PopupValueType::kManual) {
     for (const Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
@@ -3123,8 +3138,8 @@
     }
   } else {
     for (const Element* ancestor =
-             NearestOpenAncestralPopup(&node, /*inclusive*/ true);
-         ancestor; ancestor = NearestOpenAncestralPopup(ancestor)) {
+             NearestOpenAncestralPopup(node, /*inclusive*/ true);
+         ancestor; ancestor = NearestOpenAncestralPopup(*ancestor)) {
       if (ancestor == this)
         return true;
     }
@@ -3133,7 +3148,8 @@
 }
 
 void Element::MaybeQueuePopupHideEvent() {
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   // If the pop-up isn't showing, or it has an infinite PopUpHideDelay, do
   // nothing.
@@ -3168,7 +3184,11 @@
 // static
 void Element::HoveredElementChanged(Element* old_element,
                                     Element* new_element) {
-  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled())
+  auto* document_for_ot =
+      old_element ? &old_element->GetDocument()
+                  : (new_element ? &new_element->GetDocument() : nullptr);
+  if (!document_for_ot || !RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+                              document_for_ot->GetExecutionContext()))
     return;
   if (old_element) {
     // For the previously-hovered element: loop through all showing popups
@@ -3194,7 +3214,8 @@
 }
 
 void Element::HandlePopupHovered(bool hovered) {
-  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled())
+  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          GetDocument().GetExecutionContext()))
     return;
   if (!IsInTreeScope())
     return;
@@ -3263,7 +3284,8 @@
 
 void Element::SetNeedsRepositioningForSelectMenu(bool flag) {
   DCHECK(RuntimeEnabledFeatures::HTMLSelectMenuElementEnabled());
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   auto& popup_data = EnsureElementRareData().EnsurePopupData();
   if (popup_data.needsRepositioningForSelectMenu() == flag)
@@ -3279,7 +3301,8 @@
 
 void Element::SetOwnerSelectMenuElement(HTMLSelectMenuElement* element) {
   DCHECK(RuntimeEnabledFeatures::HTMLSelectMenuElementEnabled());
-  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+      GetDocument().GetExecutionContext()));
   DCHECK(HasValidPopupAttribute());
   EnsureElementRareData().EnsurePopupData().setOwnerSelectMenuElement(element);
 }
@@ -3584,7 +3607,8 @@
   if (GetPopupData() && GetPopupData()->hadDefaultOpenWhenParsed()) {
     // If a Popup element has the `defaultopen` attribute upon page
     // load, and it is the *first* such popup, show it.
-    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+        GetDocument().GetExecutionContext()));
     DCHECK(isConnected());
     GetPopupData()->setHadDefaultOpenWhenParsed(false);
     auto maybe_show_popup = [](Element* popup) {
@@ -6769,9 +6793,9 @@
 }
 
 void Element::SetShadowPseudoId(const AtomicString& id) {
-  DCHECK(CSSSelectorParser::ParsePseudoType(id, false) ==
+  DCHECK(CSSSelectorParser::ParsePseudoType(id, false, &GetDocument()) ==
              CSSSelector::kPseudoWebKitCustomElement ||
-         CSSSelectorParser::ParsePseudoType(id, false) ==
+         CSSSelectorParser::ParsePseudoType(id, false, &GetDocument()) ==
              CSSSelector::kPseudoBlinkInternalElement);
   setAttribute(html_names::kPseudoAttr, id);
 }
@@ -8032,7 +8056,8 @@
   if (HasValidPopupAttribute() &&
       GetPopupData()->needsRepositioningForSelectMenu()) {
     DCHECK(RuntimeEnabledFeatures::HTMLSelectMenuElementEnabled());
-    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
+    DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+        GetDocument().GetExecutionContext()));
     AdjustPopupPositionForSelectMenu(*style);
   }
   return style;
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 76c77410..4864571 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -588,7 +588,7 @@
   void HidePopUpInternal(HidePopupFocusBehavior focus_behavior,
                          HidePopupForcingLevel forcing_level);
   void PopupHideFinishIfNeeded();
-  static const Element* NearestOpenAncestralPopup(const Node* node,
+  static const Element* NearestOpenAncestralPopup(const Node& node,
                                                   bool inclusive = false);
   // Retrieves the element pointed to by this element's 'anchor' content
   // attribute, if that element exists, and if this element is a pop-up.
diff --git a/third_party/blink/renderer/core/dom/idle_deadline_test.cc b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
index af8a09e..080095b0 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline_test.cc
+++ b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
@@ -48,10 +48,6 @@
 
   void RemoveTaskObserver(Thread::TaskObserver* task_observer) override {}
 
-  void AddRAILModeObserver(RAILModeObserver*) override {}
-
-  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
-
   void SetV8Isolate(v8::Isolate* isolate) override {}
 };
 
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
index 2089e09..d96c168 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
@@ -59,10 +59,6 @@
 
   void RemoveTaskObserver(Thread::TaskObserver* task_observer) override {}
 
-  void AddRAILModeObserver(RAILModeObserver*) override {}
-
-  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
-
   void SetV8Isolate(v8::Isolate* isolate) override {}
 
   void RunIdleTask() { std::move(idle_task_).Run(base::TimeTicks()); }
diff --git a/third_party/blink/renderer/core/editing/build.gni b/third_party/blink/renderer/core/editing/build.gni
index b0c74aa..5f5bead 100644
--- a/third_party/blink/renderer/core/editing/build.gni
+++ b/third_party/blink/renderer/core/editing/build.gni
@@ -308,8 +308,6 @@
   "spellcheck/spell_checker.cc",
   "spellcheck/spell_checker.h",
   "spellcheck/text_checking.h",
-  "spellcheck/text_checking_paragraph.cc",
-  "spellcheck/text_checking_paragraph.h",
   "state_machines/backspace_state_machine.cc",
   "state_machines/backspace_state_machine.h",
   "state_machines/backward_code_point_state_machine.cc",
diff --git a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
index 98847d68..39b81ee6 100644
--- a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
+++ b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
@@ -47,7 +47,6 @@
 #include "third_party/blink/renderer/core/editing/selection_template.h"
 #include "third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller.h"
 #include "third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.h"
-#include "third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -66,19 +65,16 @@
 
 namespace {
 
-static bool IsWhiteSpaceOrPunctuation(UChar c) {
-  return IsSpaceOrNewline(c) || WTF::unicode::IsPunct(c);
+// Returns whether ranges [0, checking_range_length) and
+// [location, location + length) intersect
+bool CheckingRangeCovers(int checking_range_length, int location, int length) {
+  DCHECK_GE(checking_range_length, 0);
+  DCHECK_GE(length, 0);
+  return location + length > 0 && location < checking_range_length;
 }
 
-bool IsAmbiguousBoundaryCharacter(UChar character) {
-  // These are characters that can behave as word boundaries, but can appear
-  // within words. If they are just typed, i.e. if they are immediately followed
-  // by a caret, we want to delay text checking until the next character has
-  // been typed.
-  // FIXME: this is required until 6853027 is fixed and text checking can do
-  // this for us.
-  return character == '\'' || character == kRightSingleQuotationMarkCharacter ||
-         character == kHebrewPunctuationGershayimCharacter;
+bool IsWhiteSpaceOrPunctuation(UChar c) {
+  return IsSpaceOrNewline(c) || WTF::unicode::IsPunct(c);
 }
 
 }  // namespace
@@ -340,70 +336,38 @@
   if (!results.size())
     return;
 
-  TextCheckingParagraph paragraph(checking_range, checking_range);
-
-  // TODO(crbug.com/230387): The following comment does not match the current
-  // behavior and should be rewritten.
-  // Expand the range to encompass entire paragraphs, since text checking needs
-  // that much context.
-  int ambiguous_boundary_offset = -1;
-
-  if (GetFrame().Selection().ComputeVisibleSelectionInDOMTree().IsCaret()) {
-    // TODO(crbug.com/230387): The following comment does not match the current
-    // behavior and should be rewritten.
-    // Attempt to save the caret position so we can restore it later if needed
-    const Position& caret_position =
-        GetFrame().Selection().ComputeVisibleSelectionInDOMTree().End();
-    const Position& paragraph_start = checking_range.StartPosition();
-    const int selection_offset =
-        paragraph_start < caret_position
-            ? TextIterator::RangeLength(paragraph_start, caret_position)
-            : 0;
-    if (selection_offset > 0 &&
-        static_cast<unsigned>(selection_offset) <=
-            paragraph.GetText().length() &&
-        IsAmbiguousBoundaryCharacter(
-            paragraph.TextCharAt(selection_offset - 1))) {
-      ambiguous_boundary_offset = selection_offset - 1;
-    }
-  }
-
-  const int spelling_range_end_offset = paragraph.CheckingEnd();
+  const int checking_range_length = TextIterator::RangeLength(checking_range);
   for (const TextCheckingResult& result : results) {
-    const int result_location = result.location + paragraph.CheckingStart();
+    const int result_location = result.location;
     const int result_length = result.length;
-    const bool result_ends_at_ambiguous_boundary =
-        ambiguous_boundary_offset >= 0 &&
-        result_location + result_length == ambiguous_boundary_offset;
 
-    // Only mark misspelling if:
-    // 1. Result falls within spellingRange.
-    // 2. The word in question doesn't end at an ambiguous boundary. For
-    //    instance, we would not mark "wouldn'" as misspelled right after
-    //    apostrophe is typed.
+    // Only mark misspelling if result falls within checking range.
     switch (result.decoration) {
       case kTextDecorationTypeSpelling:
-        if (result_location < paragraph.CheckingStart() ||
-            result_location + result_length > spelling_range_end_offset ||
-            result_ends_at_ambiguous_boundary)
+        if (result_location < 0 ||
+            result_location + result_length > checking_range_length)
           continue;
-        AddMarker(GetFrame().GetDocument(), paragraph.CheckingRange(),
+        AddMarker(GetFrame().GetDocument(), checking_range,
                   DocumentMarker::kSpelling, result_location, result_length,
                   result.replacements);
         continue;
 
       case kTextDecorationTypeGrammar:
-        if (!paragraph.CheckingRangeCovers(result_location, result_length))
+        if (!CheckingRangeCovers(checking_range_length, result_location,
+                                 result_length)) {
           continue;
+        }
         DCHECK_GT(result_length, 0);
         DCHECK_GE(result_location, 0);
         for (const GrammarDetail& detail : result.details) {
           DCHECK_GT(detail.length, 0);
           DCHECK_GE(detail.location, 0);
-          if (!paragraph.CheckingRangeCovers(result_location + detail.location,
-                                             detail.length))
+          if (!CheckingRangeCovers(checking_range_length,
+                                   result_location + detail.location,
+                                   detail.length)) {
             continue;
-          AddMarker(GetFrame().GetDocument(), paragraph.CheckingRange(),
+          }
+          AddMarker(GetFrame().GetDocument(), checking_range,
                     DocumentMarker::kGrammar, result_location + detail.location,
                     detail.length, result.replacements);
         }
diff --git a/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.cc b/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.cc
deleted file mode 100644
index 1fe34f6..0000000
--- a/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.h"
-
-#include "third_party/blink/renderer/core/dom/range.h"
-#include "third_party/blink/renderer/core/editing/iterators/character_iterator.h"
-#include "third_party/blink/renderer/core/editing/visible_position.h"
-#include "third_party/blink/renderer/core/editing/visible_units.h"
-
-namespace blink {
-
-TextCheckingParagraph::TextCheckingParagraph(
-    const EphemeralRange& checking_range)
-    : checking_range_(checking_range),
-      checking_start_(-1),
-      checking_end_(-1),
-      checking_length_(-1) {}
-
-TextCheckingParagraph::TextCheckingParagraph(
-    const EphemeralRange& checking_range,
-    const EphemeralRange& paragraph_range)
-    : checking_range_(checking_range),
-      paragraph_range_(paragraph_range),
-      checking_start_(-1),
-      checking_end_(-1),
-      checking_length_(-1) {}
-
-TextCheckingParagraph::TextCheckingParagraph(Range* checking_range,
-                                             Range* paragraph_range)
-    : checking_range_(checking_range),
-      paragraph_range_(paragraph_range),
-      checking_start_(-1),
-      checking_end_(-1),
-      checking_length_(-1) {}
-
-TextCheckingParagraph::~TextCheckingParagraph() = default;
-
-void TextCheckingParagraph::ExpandRangeToNextEnd() {
-  DCHECK(checking_range_.IsNotNull());
-  SetParagraphRange(
-      EphemeralRange(ParagraphRange().StartPosition(),
-                     EndOfParagraph(StartOfNextParagraph(CreateVisiblePosition(
-                                        ParagraphRange().StartPosition())))
-                         .DeepEquivalent()));
-  InvalidateParagraphRangeValues();
-}
-
-void TextCheckingParagraph::InvalidateParagraphRangeValues() {
-  checking_start_ = checking_end_ = -1;
-  offset_as_range_ = EphemeralRange();
-  text_ = String();
-}
-
-int TextCheckingParagraph::RangeLength() const {
-  DCHECK(checking_range_.IsNotNull());
-  return TextIterator::RangeLength(ParagraphRange());
-}
-
-EphemeralRange TextCheckingParagraph::ParagraphRange() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (paragraph_range_.IsNull())
-    paragraph_range_ = ExpandToParagraphBoundary(CheckingRange());
-  return paragraph_range_;
-}
-
-void TextCheckingParagraph::SetParagraphRange(const EphemeralRange& range) {
-  paragraph_range_ = range;
-}
-
-EphemeralRange TextCheckingParagraph::Subrange(int character_offset,
-                                               int character_count) const {
-  DCHECK(checking_range_.IsNotNull());
-  return CalculateCharacterSubrange(ParagraphRange(), character_offset,
-                                    character_count);
-}
-
-bool TextCheckingParagraph::IsEmpty() const {
-  // Both predicates should have same result, but we check both just to be sure.
-  // We need to investigate to remove this redundancy.
-  return IsRangeEmpty() || IsTextEmpty();
-}
-
-EphemeralRange TextCheckingParagraph::OffsetAsRange() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (offset_as_range_.IsNotNull())
-    return offset_as_range_;
-  const Position& paragraph_start = ParagraphRange().StartPosition();
-  const Position& checking_start = CheckingRange().StartPosition();
-  if (paragraph_start <= checking_start) {
-    offset_as_range_ = EphemeralRange(paragraph_start, checking_start);
-    return offset_as_range_;
-  }
-  // editing/pasteboard/paste-table-001.html and more reach here.
-  offset_as_range_ = EphemeralRange(checking_start, paragraph_start);
-  return offset_as_range_;
-}
-
-const String& TextCheckingParagraph::GetText() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (text_.IsEmpty())
-    text_ = PlainText(ParagraphRange());
-  return text_;
-}
-
-int TextCheckingParagraph::CheckingStart() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (checking_start_ == -1)
-    checking_start_ = TextIterator::RangeLength(OffsetAsRange());
-  return checking_start_;
-}
-
-int TextCheckingParagraph::CheckingEnd() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (checking_end_ == -1) {
-    checking_end_ =
-        CheckingStart() + TextIterator::RangeLength(CheckingRange());
-  }
-  return checking_end_;
-}
-
-int TextCheckingParagraph::CheckingLength() const {
-  DCHECK(checking_range_.IsNotNull());
-  if (-1 == checking_length_)
-    checking_length_ = TextIterator::RangeLength(
-        CheckingRange().StartPosition(), CheckingRange().EndPosition());
-  return checking_length_;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.h b/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.h
deleted file mode 100644
index 451a423..0000000
--- a/third_party/blink/renderer/core/editing/spellcheck/text_checking_paragraph.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_SPELLCHECK_TEXT_CHECKING_PARAGRAPH_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_SPELLCHECK_TEXT_CHECKING_PARAGRAPH_H_
-
-#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-class Range;
-
-class TextCheckingParagraph {
-  STACK_ALLOCATED();
-
- public:
-  explicit TextCheckingParagraph(const EphemeralRange& checking_range);
-  TextCheckingParagraph(const EphemeralRange& checking_range,
-                        const EphemeralRange& paragraph_range);
-  TextCheckingParagraph(Range* checking_range, Range* paragraph_range);
-  ~TextCheckingParagraph();
-
-  int RangeLength() const;
-  EphemeralRange Subrange(int character_offset, int character_count) const;
-  void ExpandRangeToNextEnd();
-
-  const String& GetText() const;
-  // Why not let clients call these functions on text() themselves?
-  String TextSubstring(unsigned pos, unsigned len = INT_MAX) const {
-    return GetText().Substring(pos, len);
-  }
-  UChar TextCharAt(int index) const {
-    return GetText()[static_cast<unsigned>(index)];
-  }
-
-  bool IsEmpty() const;
-
-  int CheckingStart() const;
-  int CheckingEnd() const;
-  int CheckingLength() const;
-
-  bool CheckingRangeCovers(int location, int length) const {
-    return location < CheckingEnd() && location + length > CheckingStart();
-  }
-  EphemeralRange ParagraphRange() const;
-  void SetParagraphRange(const EphemeralRange&);
-
-  EphemeralRange CheckingRange() const { return checking_range_; }
-
- private:
-  void InvalidateParagraphRangeValues();
-  EphemeralRange OffsetAsRange() const;
-
-  bool IsTextEmpty() const { return GetText().IsEmpty(); }
-  bool IsRangeEmpty() const { return CheckingStart() >= CheckingEnd(); }
-
-  EphemeralRange checking_range_;
-  mutable EphemeralRange paragraph_range_;
-  mutable EphemeralRange offset_as_range_;
-  mutable String text_;
-  mutable int checking_start_;
-  mutable int checking_end_;
-  mutable int checking_length_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_SPELLCHECK_TEXT_CHECKING_PARAGRAPH_H_
diff --git a/third_party/blink/renderer/core/frame/scheduling.cc b/third_party/blink/renderer/core/frame/scheduling.cc
index e311411..f70d68a 100644
--- a/third_party/blink/renderer/core/frame/scheduling.cc
+++ b/third_party/blink/renderer/core/frame/scheduling.cc
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h"
 
 namespace blink {
 
@@ -37,7 +37,8 @@
     return false;
 
   auto* scheduler = ThreadScheduler::Current();
-  auto info = scheduler->GetPendingUserInputInfo(options->includeContinuous());
+  auto info = scheduler->ToMainThreadScheduler()->GetPendingUserInputInfo(
+      options->includeContinuous());
 
   for (const auto& attribution : info) {
     if (window->GetFrame()->CanAccessEvent(attribution)) {
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index fb9c2e1..b7f75d4f 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -204,7 +204,8 @@
     DCHECK(!HasFullscreenFlag(element));
 
   // If there are any open popups, close them immediately.
-  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled()) {
+  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          document.GetExecutionContext())) {
     Element::HideAllPopupsUntil(nullptr, document,
                                 HidePopupFocusBehavior::kNone,
                                 HidePopupForcingLevel::kHideImmediately,
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index c647146..bf6416eb 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/listed_element.h"
@@ -334,7 +335,8 @@
   const PopupTargetElement no_element{.element = nullptr,
                                       .action = PopupTriggerAction::kNone,
                                       .attribute_name = g_null_name};
-  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled() ||
+  if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          GetDocument().GetExecutionContext()) ||
       !IsInTreeScope() ||
       SupportsPopupTriggering() == PopupTriggerSupport::kNone) {
     return no_element;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index c55962b..12ae22bb 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/dom/synchronous_mutation_observer.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/form_controller.h"
@@ -184,8 +185,9 @@
 HTMLSelectMenuElement::HTMLSelectMenuElement(Document& document)
     : HTMLFormControlElementWithState(html_names::kSelectmenuTag, document) {
   DCHECK(RuntimeEnabledFeatures::HTMLSelectMenuElementEnabled());
-  DCHECK(RuntimeEnabledFeatures::RuntimeEnabledFeatures::
-             HTMLPopupAttributeEnabled());
+  DCHECK(
+      RuntimeEnabledFeatures::RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          document.GetExecutionContext()));
   UseCounter::Count(document, WebFeature::kSelectMenuElement);
 
   EnsureUserAgentShadowRoot();
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index 1f9d9bf..ec64a571a 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -153,8 +153,7 @@
 
   // We should call focus() last since it will fire a focus event which could
   // modify this element.
-  if (RuntimeEnabledFeatures::DialogFocusNewSpecBehaviorEnabled() &&
-      previously_focused_element_) {
+  if (previously_focused_element_) {
     FocusOptions* focus_options = FocusOptions::Create();
     focus_options->setPreventScroll(true);
     Element* previously_focused_element = previously_focused_element_;
@@ -186,7 +185,8 @@
   SetBooleanAttribute(html_names::kOpenAttr, true);
 
   // Showing a <dialog> should hide all open popups, immediately.
-  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled()) {
+  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          GetDocument().GetExecutionContext())) {
     Element::HideAllPopupsUntil(nullptr, GetDocument(),
                                 HidePopupFocusBehavior::kNone,
                                 HidePopupForcingLevel::kHideImmediately,
@@ -247,7 +247,8 @@
   }
 
   // Showing a <dialog> should hide all open popups, immediately.
-  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled()) {
+  if (RuntimeEnabledFeatures::HTMLPopupAttributeEnabled(
+          document.GetExecutionContext())) {
     Element::HideAllPopupsUntil(nullptr, document,
                                 HidePopupFocusBehavior::kNone,
                                 HidePopupForcingLevel::kHideImmediately,
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 7b3b366..91b0386 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -165,7 +165,10 @@
           &features::kThreadedPreloadScanner, "preload-processing-mode",
           PreloadProcessingMode::kImmediate, &kPreloadProcessingModeOptions};
 
-  return kPreloadProcessingModeParam.Get();
+  // Cache the value to avoid parsing the param string more than once.
+  static const PreloadProcessingMode kPreloadProcessingModeValue =
+      kPreloadProcessingModeParam.Get();
+  return kPreloadProcessingModeValue;
 }
 
 bool IsPreloadScanningEnabled(Document* document) {
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index aaeb6d9..86398497 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -863,6 +863,7 @@
   "layout_object_factory_test.cc",
   "layout_object_test.cc",
   "layout_progress_test.cc",
+  "layout_quote_test.cc",
   "layout_replaced_test.cc",
   "layout_shift_region_test.cc",
   "layout_shift_tracker_test.cc",
diff --git a/third_party/blink/renderer/core/layout/layout_quote_test.cc b/third_party/blink/renderer/core/layout/layout_quote_test.cc
new file mode 100644
index 0000000..724c8d6
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/layout_quote_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/layout_quote.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+
+namespace blink {
+
+class LayoutQuoteTest : public RenderingTest {
+ protected:
+  void CheckQuoteLayoutObjectChildrenLang(const char* id,
+                                          const char* lang,
+                                          const char* parent_lang) {
+    const LayoutObject* o = GetLayoutObjectByElementId(id);
+    EXPECT_STREQ(o->StyleRef().Locale().Ascii().c_str(), lang);
+
+    const LayoutObject* child_before = o->SlowFirstChild();
+    ASSERT_EQ(child_before->StyleRef().StyleType(), PseudoId::kPseudoIdBefore);
+    EXPECT_STREQ(
+        child_before->SlowFirstChild()->StyleRef().Locale().Ascii().c_str(),
+        parent_lang);
+
+    const LayoutObject* child_after = o->SlowLastChild();
+    ASSERT_EQ(child_after->StyleRef().StyleType(), PseudoId::kPseudoIdAfter);
+    EXPECT_STREQ(
+        child_after->SlowFirstChild()->StyleRef().Locale().Ascii().c_str(),
+        parent_lang);
+
+    const LayoutObject* child_text = child_before->NextSibling();
+    ASSERT_TRUE(child_text->IsText());
+    EXPECT_STREQ(child_text->StyleRef().Locale().Ascii().c_str(), lang);
+  }
+};
+
+// The `<q>` element delimiters should use the language from its parent.
+// crbug.com/1290851
+TEST_F(LayoutQuoteTest, Locale) {
+  SetBodyInnerHTML(R"HTML(
+    <div lang="en">
+      English
+      <q id="ja" lang="ja">
+        Japanese
+        <q id="fr" lang="fr">
+          French
+        </q>
+        <q id="nan">
+          Nan
+        </q>
+      </q>
+    </div>
+  )HTML");
+
+  // The "ja" element should be "ja".
+  // Its `::before`/`::after` pseudo elements should be parent lang "en".
+  // Its text child should be "ja".
+  LayoutQuoteTest::CheckQuoteLayoutObjectChildrenLang("ja", "ja", "en");
+
+  // The "fr" element should be "fr".
+  // Its pseudo elements should be parent lang "ja".
+  // Its text child should be "fr".
+  LayoutQuoteTest::CheckQuoteLayoutObjectChildrenLang("fr", "fr", "ja");
+
+  // When the lang is not defined, all lang should be dependent on parent "ja".
+  LayoutQuoteTest::CheckQuoteLayoutObjectChildrenLang("nan", "ja", "ja");
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
index 361d8070..d999163 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
@@ -10,11 +10,21 @@
 
 namespace blink {
 
+NGPhysicalAnchorReference::NGPhysicalAnchorReference(
+    const NGLogicalAnchorReference& logical_reference,
+    const WritingModeConverter& converter)
+    : rect(converter.ToPhysical(logical_reference.rect)),
+      fragment(logical_reference.fragment),
+      is_invalid(logical_reference.is_invalid) {}
+
 const NGPhysicalAnchorReference* NGPhysicalAnchorQuery::AnchorReference(
     const AtomicString& name) const {
   const auto& it = anchor_references_.find(name);
-  if (it != anchor_references_.end())
-    return it->value.Get();
+  if (it != anchor_references_.end()) {
+    const NGPhysicalAnchorReference& result = *it->value;
+    if (!result.is_invalid)
+      return &result;
+  }
   return nullptr;
 }
 
@@ -35,8 +45,11 @@
 const NGLogicalAnchorReference* NGLogicalAnchorQuery::AnchorReference(
     const AtomicString& name) const {
   const auto& it = anchor_references_.find(name);
-  if (it != anchor_references_.end())
-    return &it->value;
+  if (it != anchor_references_.end()) {
+    const NGLogicalAnchorReference& result = it->value;
+    if (!result.is_invalid)
+      return &result;
+  }
   return nullptr;
 }
 
@@ -54,6 +67,13 @@
 }
 
 void NGLogicalAnchorQuery::Set(const AtomicString& name,
+                               const NGPhysicalFragment& fragment,
+                               const LogicalRect& rect) {
+  DCHECK(fragment.GetLayoutObject());
+  Set(name, NGLogicalAnchorReference{rect, &fragment, fragment.IsPositioned()});
+}
+
+void NGLogicalAnchorQuery::Set(const AtomicString& name,
                                const NGLogicalAnchorReference& reference) {
   const auto result = anchor_references_.insert(name, reference);
   if (result.is_new_entry)
@@ -77,19 +97,21 @@
   for (const auto& it : logical_query.anchor_references_) {
     DCHECK_EQ(AnchorReference(it.key), nullptr);
     anchor_references_.Set(
-        it.key, MakeGarbageCollected<NGPhysicalAnchorReference>(
-                    converter.ToPhysical(it.value.rect), it.value.fragment));
+        it.key,
+        MakeGarbageCollected<NGPhysicalAnchorReference>(it.value, converter));
   }
 }
 
 void NGLogicalAnchorQuery::SetFromPhysical(
     const NGPhysicalAnchorQuery& physical_query,
     const WritingModeConverter& converter,
-    const LogicalOffset& additional_offset) {
+    const LogicalOffset& additional_offset,
+    bool is_positioned) {
   for (const auto& it : physical_query.anchor_references_) {
     LogicalRect rect = converter.ToLogical(it.value->rect);
     rect.offset += additional_offset;
-    Set(it.key, NGLogicalAnchorReference{rect, it.value->fragment.Get()});
+    Set(it.key, NGLogicalAnchorReference{rect, it.value->fragment.Get(),
+                                         is_positioned});
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
index f650a05..a998e50 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
@@ -22,18 +22,19 @@
 class NGLogicalAnchorQuery;
 class NGPhysicalFragment;
 class WritingModeConverter;
+struct NGLogicalAnchorReference;
 struct NGLogicalLink;
 
 struct CORE_EXPORT NGPhysicalAnchorReference
     : public GarbageCollected<NGPhysicalAnchorReference> {
-  NGPhysicalAnchorReference(PhysicalRect rect,
-                            const NGPhysicalFragment* fragment)
-      : rect(rect), fragment(fragment) {}
+  NGPhysicalAnchorReference(const NGLogicalAnchorReference& logical_reference,
+                            const WritingModeConverter& converter);
 
   void Trace(Visitor* visitor) const;
 
   PhysicalRect rect;
   Member<const NGPhysicalFragment> fragment;
+  bool is_invalid = false;
 };
 
 class CORE_EXPORT NGPhysicalAnchorQuery {
@@ -73,6 +74,7 @@
  public:
   LogicalRect rect;
   const NGPhysicalFragment* fragment;
+  bool is_invalid = false;
 };
 
 class CORE_EXPORT NGLogicalAnchorQuery {
@@ -86,10 +88,13 @@
   const LogicalRect* Rect(const AtomicString& name) const;
   const NGPhysicalFragment* Fragment(const AtomicString& name) const;
 
-  void Set(const AtomicString& name, const NGLogicalAnchorReference& reference);
+  void Set(const AtomicString& name,
+           const NGPhysicalFragment& fragment,
+           const LogicalRect& rect);
   void SetFromPhysical(const NGPhysicalAnchorQuery& physical_query,
                        const WritingModeConverter& converter,
-                       const LogicalOffset& additional_offset);
+                       const LogicalOffset& additional_offset,
+                       bool is_positioned);
   void SetAsStitched(base::span<const NGLogicalLink> children,
                      WritingDirectionMode writing_direction);
 
@@ -111,6 +116,8 @@
  private:
   friend class NGPhysicalAnchorQuery;
 
+  void Set(const AtomicString& name, const NGLogicalAnchorReference& reference);
+
   HashMap<AtomicString, NGLogicalAnchorReference> anchor_references_;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc b/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc
index d476942a..6677b223 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc
@@ -35,6 +35,17 @@
       return AnchorQuery(*element);
     return nullptr;
   }
+
+  Vector<AtomicString> ValidAnchorNames(const Element& element) const {
+    Vector<AtomicString> names;
+    if (const NGPhysicalAnchorQuery* anchor_query = AnchorQuery(element)) {
+      for (const auto& it : *anchor_query) {
+        if (!it.value->is_invalid)
+          names.push_back(it.key);
+      }
+    }
+    return names;
+  }
 };
 
 struct AnchorTestData {
@@ -165,6 +176,41 @@
   EXPECT_FALSE(anchor_query);
 }
 
+// https://tabatkins.github.io/specs/css-anchor-position/#determining
+TEST_F(NGAnchorQueryTest, AnchorNameValid) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="container" style="position: relative">
+      <div id="static1">
+        <div id="rel2" style="position: relative">
+          <div id="rel1" style="position: relative">
+            <div style="anchor-name: --static"></div>
+            <div style="anchor-name: --abspos; position: absolute"></div>
+          </div>
+      </div>
+    </div>
+  )HTML");
+  // For `rel1`, only `--static` is valid because "if el has the same containing
+  // block as the querying element, el is not positioned."
+  EXPECT_THAT(ValidAnchorNames(*GetElementById("rel1")),
+              testing::ElementsAre("--static"));
+  // For `rel2`, nothing is valid because "if el has a different containing
+  // block from the querying element, the last containing block in el's
+  // containing block chain before reaching the querying element's containing
+  // block is not positioned." The "last containing block" is `rel1`, which is
+  // positioned (has `position: relative`.)
+  EXPECT_THAT(ValidAnchorNames(*GetElementById("rel2")),
+              testing::ElementsAre());
+  // Same for `static1`. Its last containing block is `rel2`. It's not visible
+  // to the web though, as the `static1` can't be a containing block of
+  // positioned objects. This is to test the internal propagation mechanism.
+  EXPECT_THAT(ValidAnchorNames(*GetElementById("static1")),
+              testing::ElementsAre());
+  // For `container`, the last containing block is `static1`, which is not
+  // positioned, so all anchor names are valid.
+  EXPECT_THAT(ValidAnchorNames(*GetElementById("container")),
+              testing::ElementsAre("--abspos", "--static"));
+}
+
 TEST_F(NGAnchorQueryTest, BlockFlow) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index a6beb3f..f994bda 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -50,11 +50,9 @@
         !anchor_name.IsNull()) {
       DCHECK(RuntimeEnabledFeatures::CSSAnchorPositioningEnabled());
       anchor_query_.Set(
-          anchor_name,
-          NGLogicalAnchorReference{
-              LogicalRect{child_offset + relative_offset,
-                          child.Size().ConvertToLogical(GetWritingMode())},
-              &child});
+          anchor_name, child,
+          LogicalRect{child_offset + relative_offset,
+                      child.Size().ConvertToLogical(GetWritingMode())});
     }
   }
 
@@ -405,8 +403,10 @@
   }
 
   // Collect any anchor references.
-  if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery())
-    anchor_query_.SetFromPhysical(*anchor_query, converter, adjusted_offset);
+  if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery()) {
+    anchor_query_.SetFromPhysical(*anchor_query, converter, adjusted_offset,
+                                  fragment.IsPositioned());
+  }
 
   NGFragmentedOutOfFlowData* oof_data = fragment.FragmentedOutOfFlowData();
   if (!oof_data)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index ace9902..0d1e93ff 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -143,6 +143,11 @@
   bool IsFloatingOrOutOfFlowPositioned() const {
     return IsFloating() || IsOutOfFlowPositioned();
   }
+  bool IsPositioned() const {
+    if (const LayoutObject* layout_object = GetLayoutObject())
+      return layout_object->IsPositioned();
+    return false;
+  }
   // Return true if this is the legend child of a fieldset that gets special
   // treatment (i.e. placed over the block-start border).
   bool IsRenderedLegend() const {
diff --git a/third_party/blink/renderer/core/script/classic_script.cc b/third_party/blink/renderer/core/script/classic_script.cc
index b539f58..f009723 100644
--- a/third_party/blink/renderer/core/script/classic_script.cc
+++ b/third_party/blink/renderer/core/script/classic_script.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/script/classic_script.h"
 
 #include "third_party/blink/public/web/web_script_source.h"
+#include "third_party/blink/renderer/bindings/core/v8/referrer_script_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -180,6 +181,34 @@
   visitor->Trace(cache_consumer_);
 }
 
+v8::Local<v8::Data> ClassicScript::CreateHostDefinedOptions(
+    v8::Isolate* isolate) const {
+  const ReferrerScriptInfo referrer_info(BaseUrl(), FetchOptions());
+
+  v8::Local<v8::Data> host_defined_options =
+      referrer_info.ToV8HostDefinedOptions(isolate, SourceUrl());
+
+  return host_defined_options;
+}
+
+v8::ScriptOrigin ClassicScript::CreateScriptOrigin(v8::Isolate* isolate) const {
+  // NOTE: For compatibility with WebCore, ClassicScript's line starts at
+  // 1, whereas v8 starts at 0.
+  // NOTE(kouhei): Probably this comment is no longer relevant and Blink lines
+  // start at 1 only for historic reasons now. I guess we could change it, but
+  // there's not much benefit doing so.
+  return v8::ScriptOrigin(
+      isolate, V8String(isolate, SourceUrl()),
+      StartPosition().line_.ZeroBasedInt(),
+      StartPosition().column_.ZeroBasedInt(),
+      GetSanitizeScriptErrors() == SanitizeScriptErrors::kDoNotSanitize, -1,
+      V8String(isolate, SourceMapUrl()),
+      GetSanitizeScriptErrors() == SanitizeScriptErrors::kSanitize,
+      false,  // is_wasm
+      false,  // is_module
+      CreateHostDefinedOptions(isolate));
+}
+
 ScriptEvaluationResult ClassicScript::RunScriptOnScriptStateAndReturnValue(
     ScriptState* script_state,
     ExecuteScriptPolicy policy,
diff --git a/third_party/blink/renderer/core/script/classic_script.h b/third_party/blink/renderer/core/script/classic_script.h
index 8a07ec6..514bc05c 100644
--- a/third_party/blink/renderer/core/script/classic_script.h
+++ b/third_party/blink/renderer/core/script/classic_script.h
@@ -115,11 +115,15 @@
       LocalDOMWindow*,
       int32_t world_id);
 
+  v8::ScriptOrigin CreateScriptOrigin(v8::Isolate* isolate) const;
+
  private:
   mojom::blink::ScriptType GetScriptType() const override {
     return mojom::blink::ScriptType::kClassic;
   }
 
+  v8::Local<v8::Data> CreateHostDefinedOptions(v8::Isolate* isolate) const;
+
   const ParkableString source_text_;
 
   const ScriptSourceLocationType source_location_type_;
diff --git a/third_party/blink/renderer/core/style/content_data.cc b/third_party/blink/renderer/core/style/content_data.cc
index 505d26c..74dad75 100644
--- a/third_party/blink/renderer/core/style/content_data.cc
+++ b/third_party/blink/renderer/core/style/content_data.cc
@@ -114,11 +114,26 @@
     PseudoElement& pseudo,
     const ComputedStyle& pseudo_style,
     LegacyLayout legacy) const {
+  // For quote, pseudo_style should use parent's locale
+  // https://github.com/w3c/csswg-drafts/issues/5478
+  Element* quote = pseudo.ParentOrShadowHostElement();
+  Element* parent = quote->ParentOrShadowHostElement();
+  scoped_refptr<ComputedStyle> pseudo_style_copy =
+      ComputedStyle::Clone(pseudo_style);
+  FontDescription font_description = pseudo_style_copy->GetFontDescription();
+  if (parent) {
+    font_description.SetLocale(
+        LayoutLocale::Get(parent->ComputeInheritedLanguage()));
+  } else {
+    font_description.SetLocale(&LayoutLocale::GetDefault());
+  }
+  pseudo_style_copy->SetFontDescription(font_description);
+
   LayoutObject* layout_object =
       MakeGarbageCollected<LayoutQuote>(pseudo, quote_);
   if (legacy == LegacyLayout::kForce)
     layout_object->SetForceLegacyLayout();
-  layout_object->SetPseudoElementStyle(&pseudo_style);
+  layout_object->SetPseudoElementStyle(pseudo_style_copy);
   return layout_object;
 }
 
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
index 232be5f..b022b0b 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
@@ -367,11 +367,9 @@
     return;
   }
 
-  String format_name =
-      clipboard_item_data_[clipboard_representation_index_].first;
-
   ClipboardReader* clipboard_reader = ClipboardReader::Create(
-      GetLocalFrame()->GetSystemClipboard(), format_name, this);
+      GetLocalFrame()->GetSystemClipboard(),
+      clipboard_item_data_[clipboard_representation_index_].first, this);
   if (!clipboard_reader) {
     OnRead(nullptr);
     return;
@@ -566,7 +564,7 @@
   }
 
   auto permission_descriptor = CreateClipboardPermissionDescriptor(
-      permission, false, allow_without_sanitization);
+      permission, /*allow_without_gesture=*/false, allow_without_sanitization);
   if (permission == mojom::blink::PermissionName::CLIPBOARD_WRITE &&
       !allow_without_sanitization) {
     // Check permission (but do not query the user).
@@ -578,7 +576,7 @@
   // Check permission, and query if necessary.
   // See crbug.com/795929 for moving this check into the Browser process.
   permission_service_->RequestPermission(std::move(permission_descriptor),
-                                         /*user_gesture*/ false,
+                                         /*user_gesture=*/false,
                                          std::move(callback));
 }
 
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
index 19a3a84c..cc28b77 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
@@ -143,7 +143,7 @@
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
     LocalFrame* frame = promise_->GetLocalFrame();
-    if (html_string.IsEmpty()) {
+    if (!frame || html_string.IsEmpty()) {
       NextRead(Vector<uint8_t>());
       return;
     }
@@ -166,6 +166,7 @@
                             WrapCrossThreadPersistent(this),
                             std::move(clipboard_task_runner_)));
   }
+
   static void EncodeOnBackgroundThread(
       String plain_text,
       ClipboardHtmlReader* reader,
@@ -317,9 +318,10 @@
     return MakeGarbageCollected<ClipboardCustomFormatReader>(
         system_clipboard, promise, mime_type);
   }
-  if (mime_type == kMimeTypeImagePng) {
+
+  if (mime_type == kMimeTypeImagePng)
     return MakeGarbageCollected<ClipboardPngReader>(system_clipboard, promise);
-  }
+
   if (mime_type == kMimeTypeTextPlain)
     return MakeGarbageCollected<ClipboardTextReader>(system_clipboard, promise);
 
@@ -327,8 +329,9 @@
     return MakeGarbageCollected<ClipboardHtmlReader>(system_clipboard, promise);
 
   if (mime_type == kMimeTypeImageSvg &&
-      RuntimeEnabledFeatures::ClipboardSvgEnabled())
+      RuntimeEnabledFeatures::ClipboardSvgEnabled()) {
     return MakeGarbageCollected<ClipboardSvgReader>(system_clipboard, promise);
+  }
 
   NOTREACHED()
       << "IsValidType() and Create() have inconsistent implementations.";
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
index 478a1770..32f6de5 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
@@ -143,24 +143,20 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+    LocalFrame* local_frame = promise_->GetLocalFrame();
     auto* execution_context = promise_->GetExecutionContext();
-    if (!execution_context)
+    if (!local_frame || !execution_context)
       return;
     execution_context->CountUse(WebFeature::kHtmlClipboardApiWrite);
 
+    // Sanitizing on the main thread because HTML DOM nodes can only be used
+    // on the main thread.
     String html_string =
         String::FromUTF8(reinterpret_cast<const LChar*>(html_data->Data()),
                          html_data->ByteLength());
-
-    // Sanitizing on the main thread because HTML DOM nodes can only be used
-    // on the main thread.
     KURL url;
     unsigned fragment_start = 0;
     unsigned fragment_end = html_string.length();
-
-    LocalFrame* local_frame = promise_->GetLocalFrame();
-    if (!local_frame)
-      return;
     Document* document = local_frame->GetDocument();
     String sanitized_html = CreateSanitizedMarkupWithContext(
         *document, html_string, fragment_start, fragment_end, url, kIncludeNode,
@@ -271,10 +267,12 @@
     return MakeGarbageCollected<ClipboardCustomFormatWriter>(
         system_clipboard, promise, web_custom_format);
   }
+
   if (mime_type == kMimeTypeImagePng) {
     return MakeGarbageCollected<ClipboardImageWriter>(system_clipboard,
                                                       promise);
   }
+
   if (mime_type == kMimeTypeTextPlain)
     return MakeGarbageCollected<ClipboardTextWriter>(system_clipboard, promise);
 
@@ -282,8 +280,9 @@
     return MakeGarbageCollected<ClipboardHtmlWriter>(system_clipboard, promise);
 
   if (mime_type == kMimeTypeImageSvg &&
-      RuntimeEnabledFeatures::ClipboardSvgEnabled())
+      RuntimeEnabledFeatures::ClipboardSvgEnabled()) {
     return MakeGarbageCollected<ClipboardSvgWriter>(system_clipboard, promise);
+  }
 
   NOTREACHED()
       << "IsValidType() and Create() have inconsistent implementations.";
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.h b/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
index 9777696..98240e4 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
@@ -53,7 +53,7 @@
 class ClipboardWriter : public GarbageCollected<ClipboardWriter>,
                         public FileReaderLoaderClient {
  public:
-  // For writing sanitized MIME types.
+  // For writing sanitized and custom MIME types.
   // IsValidType() must return true on types passed into `mime_type`.
   static ClipboardWriter* Create(SystemClipboard* system_clipboard,
                                  const String& mime_type,
diff --git a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
index e659a35..0be10ed9 100644
--- a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/animation/path_interpolation_functions.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.h"
 #include "third_party/blink/renderer/core/css/cssom/paint_worklet_input.h"
@@ -162,6 +163,31 @@
   offsets.push_back(value.ToDouble());
 }
 
+bool ValidateClipPathValue(const Element* element,
+                           const CSSValue* value,
+                           const InterpolableValue* interpolable_value) {
+  if (value) {
+    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
+    // Don't try to composite animations with clip-path: none, as this is not
+    // compatible with the method used to paint composite clip path animations:
+    // A mask image would potentially clip content unless if it was the size of
+    // the entire viewport.
+    if (identifier_value &&
+        identifier_value->GetValueID() == CSSValueID::kNone) {
+      return false;
+    }
+
+    return true;
+  } else if (interpolable_value) {
+    // There is no need to check for clip-path: none here, as transitions are
+    // not defined for this non-interpolable value. See
+    // CSSAnimations::CalculateTransitionUpdateForPropertyHandle and
+    // basic_shape_interpolation_functions::MaybeConvertBasicShape
+    return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 template <>
@@ -181,7 +207,8 @@
 // background-color and clip-path animations.
 Animation* ClipPathPaintDefinition::GetAnimationIfCompositable(
     const Element* element) {
-  return GetAnimationForProperty(element, GetCSSPropertyClipPath());
+  return GetAnimationForProperty(element, GetCSSPropertyClipPath(),
+                                 ValidateClipPathValue);
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
index 02a141e9..6191b9c8 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -378,40 +378,10 @@
 
 NOINLINE void RawResourceClientStateChecker::NotifyFinished(
     Resource* resource) {
-  // TODO(https://crbug.com/1346074): Remove these once the investigation is
-  // done.
-  std::string url_string = resource->Url().StrippedForUseAsHref().Utf8();
-  const int32_t destination =
-      static_cast<int32_t>(
-          resource->GetResourceRequest().GetRequestDestination()) +
-      0x400;
-  const int32_t context =
-      static_cast<int32_t>(resource->GetResourceRequest().GetRequestContext()) +
-      0x800;
-  const int32_t mark1 = 0xabababab;
-  char url[128];
-  memset(url, 0x7e, sizeof(url));
-  // We keep the first and last four bytes to make it easy to search for the
-  // url in the memory dump.
-  base::strlcpy(url + 4, url_string.c_str(), sizeof(url) - 8);
-  const int32_t mark2 = 0xcdcdcdcd;
-  base::debug::Alias(&destination);
-  base::debug::Alias(&context);
-  base::debug::Alias(&mark1);
-  base::debug::Alias(url);
-  base::debug::Alias(&mark2);
-
   SECURITY_CHECK(state_ != kNotAddedAsClient);
   SECURITY_CHECK(state_ != kDetached);
   SECURITY_CHECK(state_ != kNotifyFinished);
 
-  // TODO(https://crbug.com/1346074): Remove these CHECKs once the investigation
-  // is done.
-  if (!resource->ErrorOccurred()) {
-    SECURITY_CHECK(state_ != kStarted);
-    SECURITY_CHECK(state_ != kRedirectBlocked);
-  }
-
   SECURITY_CHECK(resource->ErrorOccurred() ||
                  (state_ == kResponseReceived || state_ == kDataReceived ||
                   state_ == kDataDownloaded ||
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 081fb9d..61792d1 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -534,10 +534,6 @@
       implied_by: ["CSSContainerQueries"]
     },
     {
-      name: "CSSDynamicRangeMediaQueries",
-      status: "stable"
-    },
-    {
       // Include custom properties in CSSComputedStyleDeclaration::item/length.
       // https://crbug.com/949807
       name: "CSSEnumeratedCustomProperties",
@@ -820,10 +816,6 @@
       status: "experimental",
     },
     {
-      name: "DialogFocusNewSpecBehavior",
-      status: "stable",
-    },
-    {
       name: "DigitalGoods",
       origin_trial_feature_name: "DigitalGoodsV2",
       origin_trial_os: ["android", "chromeos"],
@@ -1193,6 +1185,7 @@
     {
       name: "HTMLPopupAttribute",
       status: "experimental",
+      origin_trial_feature_name: "HTMLPopupAttribute",
       implied_by: ["HTMLSelectMenuElement"],
     },
     {
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_base.h b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_base.h
index ab66ccd..a954d028 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_base.h
+++ b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_base.h
@@ -8,11 +8,9 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 
 #include "base/task/single_thread_task_runner.h"
-#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
-#include "third_party/blink/renderer/platform/scheduler/common/single_thread_idle_task_runner.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/virtual_time_controller.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
 
 namespace base {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 65566a2..04e3c54 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -379,11 +379,15 @@
 WebThreadScheduler* WebThreadScheduler::MainThreadScheduler() {
   auto* main_thread = Thread::MainThread();
   // Enforce that this is not called before the main thread is initialized.
-  DCHECK(main_thread && main_thread->Scheduler());
+  DCHECK(main_thread);
+  DCHECK(main_thread->Scheduler());
+  DCHECK(main_thread->Scheduler()->ToMainThreadScheduler());
 
   // This can return nullptr if the main thread scheduler is not a
   // MainThreadSchedulerImpl, which can happen in tests.
-  return main_thread->Scheduler()->GetWebMainThreadScheduler();
+  return main_thread->Scheduler()
+      ->ToMainThreadScheduler()
+      ->ToWebMainThreadScheduler();
 }
 
 MainThreadSchedulerImpl::MainThreadOnly::MainThreadOnly(
@@ -2246,7 +2250,7 @@
   main_thread_only().agent_group_scheduler_scope_stack.pop_back();
 }
 
-WebThreadScheduler* MainThreadSchedulerImpl::GetWebMainThreadScheduler() {
+WebThreadScheduler* MainThreadSchedulerImpl::ToWebMainThreadScheduler() {
   return this;
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index add64e7..01df9e6b 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -443,7 +443,7 @@
   };
 
   // WebThreadScheduler private implementation:
-  WebThreadScheduler* GetWebMainThreadScheduler() override;
+  WebThreadScheduler* ToWebMainThreadScheduler() override;
 
   // ThreadSchedulerBase overrides
   base::SequencedTaskRunner* GetVirtualTimeTaskRunner() override;
diff --git a/third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h
index dc00dc1..61f1e1c03 100644
--- a/third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h
@@ -5,10 +5,21 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_MAIN_THREAD_SCHEDULER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_MAIN_THREAD_SCHEDULER_H_
 
+#include <memory>
+
+#include "third_party/blink/public/common/input/web_input_event_attribution.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
+namespace scheduler {
+class WebAgentGroupScheduler;
+class WebThreadScheduler;
+}  // namespace scheduler
+
+class RAILModeObserver;
+
 // This class is used to submit tasks and pass other information from Blink to
 // the platform's main thread scheduler.
 class PLATFORM_EXPORT MainThreadScheduler : public ThreadScheduler {
@@ -47,6 +58,29 @@
   // returns nullptr in task observers.
   virtual scheduler::WebAgentGroupScheduler*
   GetCurrentAgentGroupScheduler() = 0;
+
+  virtual void AddRAILModeObserver(RAILModeObserver* observer) = 0;
+
+  virtual void RemoveRAILModeObserver(RAILModeObserver const* observer) = 0;
+
+  // Returns a list of all unique attributions that are marked for event
+  // dispatch. If |include_continuous| is true, include event types from
+  // "continuous" sources (see PendingUserInput::IsContinuousEventTypes).
+  virtual Vector<WebInputEventAttribution> GetPendingUserInputInfo(
+      bool include_continuous) const {
+    return {};
+  }
+
+ private:
+  // For ToWebMainThreadScheduler().
+  friend class scheduler::WebThreadScheduler;
+
+  // Return a reference to an underlying main thread WebThreadScheduler object.
+  // Can be null if there is no underlying main thread WebThreadScheduler
+  // (e.g. worker threads).
+  virtual scheduler::WebThreadScheduler* ToWebMainThreadScheduler() {
+    return nullptr;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
index 38399c7..e5657b61 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -9,13 +9,8 @@
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
-#include "third_party/blink/public/common/input/web_input_event.h"
-#include "third_party/blink/public/common/input/web_input_event_attribution.h"
-#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
-#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace v8 {
 class Isolate;
@@ -29,12 +24,9 @@
 
 class CompositorThreadScheduler;
 class MainThreadScheduler;
-class RAILModeObserver;
 
 // This class is used to submit tasks and pass other information from Blink to
 // the platform's scheduler.
-// TODO(dtapuska): Move methods that are intended only for the main thread
-// scheduler into MainThreadScheduler.
 class PLATFORM_EXPORT ThreadScheduler {
  public:
   // Return the current thread's ThreadScheduler.
@@ -75,10 +67,6 @@
   virtual void PostNonNestableIdleTask(const base::Location&,
                                        Thread::IdleTask) = 0;
 
-  virtual void AddRAILModeObserver(RAILModeObserver* observer) = 0;
-
-  virtual void RemoveRAILModeObserver(RAILModeObserver const* observer) = 0;
-
   // Returns a task runner for kV8 tasks. Can be called from any thread.
   virtual scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() = 0;
 
@@ -101,14 +89,6 @@
   virtual void AddTaskObserver(base::TaskObserver* task_observer) = 0;
   virtual void RemoveTaskObserver(base::TaskObserver* task_observer) = 0;
 
-  // Returns a list of all unique attributions that are marked for event
-  // dispatch. If |include_continuous| is true, include event types from
-  // "continuous" sources (see PendingUserInput::IsContinuousEventTypes).
-  virtual Vector<WebInputEventAttribution> GetPendingUserInputInfo(
-      bool include_continuous) const {
-    return {};
-  }
-
   // Associates |isolate| to the scheduler.
   virtual void SetV8Isolate(v8::Isolate* isolate) = 0;
 
@@ -125,17 +105,6 @@
 
   virtual void InitializeTaskAttributionTracker(
       std::unique_ptr<scheduler::TaskAttributionTracker>) {}
-
- private:
-  // For GetWebMainThreadScheduler().
-  friend class scheduler::WebThreadScheduler;
-
-  // Return a reference to an underlying main thread WebThreadScheduler object.
-  // Can be null if there is no underlying main thread WebThreadScheduler
-  // (e.g. worker threads).
-  virtual scheduler::WebThreadScheduler* GetWebMainThreadScheduler() {
-    return nullptr;
-  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_impl.h
index c4b1e68a..7bcec294 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_impl.h
@@ -54,8 +54,6 @@
                            Thread::IdleTask) override;
   void PostNonNestableIdleTask(const base::Location&,
                                Thread::IdleTask) override;
-  void AddRAILModeObserver(RAILModeObserver*) override {}
-  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override;
   base::TimeTicks MonotonicallyIncreasingVirtualTime() override;
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
index 44b11d83..e1192ddf 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -59,8 +59,6 @@
                            Thread::IdleTask) override;
   void PostNonNestableIdleTask(const base::Location&,
                                Thread::IdleTask) override;
-  void AddRAILModeObserver(RAILModeObserver*) override {}
-  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override;
   base::TimeTicks MonotonicallyIncreasingVirtualTime() override;
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index 42999ac2..0a288cc6 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -19,7 +19,6 @@
 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/detach-locked-slot-children-crash.html [ Crash ]
 # ax_object.cc#2792 -- IsInert():
 crbug.com/1350162 external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html [ Crash ]
-crbug.com/1350162 virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html [ Crash ]
 # Should not receive unincluded child:
 external/wpt/inert/dynamic-inert-on-focused-element.html [ Crash ]
 # Unknown cause:
diff --git a/third_party/blink/web_tests/FlagExpectations/highdpi b/third_party/blink/web_tests/FlagExpectations/highdpi
index 1fd108c..58e57d8 100644
--- a/third_party/blink/web_tests/FlagExpectations/highdpi
+++ b/third_party/blink/web_tests/FlagExpectations/highdpi
@@ -1378,6 +1378,7 @@
 crbug.com/1329180 virtual/document-transition/wpt_internal/document-transition/new-and-old-sizes-match.html [ Failure ]
 crbug.com/1295280 virtual/document-transition/wpt_internal/document-transition/old-content-with-overflow-zoomed.html [ Failure ]
 crbug.com/1295280 virtual/document-transition/wpt_internal/document-transition/new-content-with-overflow-zoomed.html [ Failure ]
+crbug.com/1351422 virtual/document-transition/wpt_internal/document-transition/content-visibility-auto-shared-element.html [ Failure ]
 
 crbug.com/1314903 external/wpt/css/css-sizing/contain-intrinsic-size/animation/contain-intrinsic-size-interpolation.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0c80f0e..7668c943 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1792,7 +1792,7 @@
 crbug.com/753671 [ Mac11-arm64 ] external/wpt/css/css-content/quotes-020.html [ Failure ]
 crbug.com/753671 [ Mac12 ] external/wpt/css/css-content/quotes-020.html [ Failure ]
 crbug.com/753671 [ Mac12-arm64 ] external/wpt/css/css-content/quotes-020.html [ Failure ]
-crbug.com/753671 external/wpt/css/css-content/quotes-030.html [ Failure ]
+crbug.com/753671 [ Win ] external/wpt/css/css-content/quotes-030.html [ Failure ]
 crbug.com/1067277 external/wpt/css/css-content/element-replacement-on-replaced-element.tentative.html [ Failure ]
 
 crbug.com/1335893 external/wpt/css/css-content/content-none-root-columns.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 68f38c5..0ec02df 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -232,7 +232,7 @@
   },
   {
     "prefix": "compute-pressure",
-    "platforms": ["Linux", "Mac", "Win"],
+    "platforms": ["Linux", "Mac"],
     "bases": ["external/wpt/compute-pressure"],
     "args": ["--enable-features=ComputePressure"]
   },
@@ -1038,15 +1038,8 @@
     "prefix": "popup-disabled",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [],
-    "args": ["--stable-release-mode"]
-  },
-  {
-    "prefix": "dialogfocus-old-behavior",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": [
-      "external/wpt/html/semantics/interactive-elements/the-dialog-element"
-    ],
-    "args": ["--disable-features=DialogFocusNewSpecBehavior"]
+    "args": ["--stable-release-mode",
+             "--disable-features=HTMLPopupAttribute"]
   },
   {
     "prefix": "object-param-url",
diff --git a/third_party/blink/web_tests/editing/spelling/checking_at_ambiguous_word_boundary.html b/third_party/blink/web_tests/editing/spelling/checking_at_ambiguous_word_boundary.html
new file mode 100644
index 0000000..55e27bd
--- /dev/null
+++ b/third_party/blink/web_tests/editing/spelling/checking_at_ambiguous_word_boundary.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script src="spellcheck_test.js"></script>
+<script>
+// This test asserts that during active typing, we don't immediately check it
+// if the caret ends at an ambiguous word boundary (i.e., "don'|"). We'll check
+// it after typing more contents or when the page is left idle for a while.
+
+spellcheck_test(
+    "<div contenteditable>|</div>",
+    "InsertText zz'",
+    "<div contenteditable>zz'</div>",
+    {
+      title: '1 Do not immediately check at ambiguous word boundary',
+      callback: sample => spellcheck_test(
+          sample,
+          "InsertText t forget to mark me. ",
+          "<div contenteditable>#zz't# forget to mark me.\u00A0</div>",
+          '1 But check after typing more contents',
+      )
+    }
+);
+
+spellcheck_test(
+    "<div contenteditable>|</div>",
+    "InsertText zz'",
+    "<div contenteditable>zz'</div>",
+    {
+      title: '2 Do not immediately check at ambiguous word boundary',
+      callback: sample => spellcheck_test(
+          sample,
+          () => {},
+          "<div contenteditable>#zz#'</div>",
+          {
+            title: '2 But check after the page is left idle for a while',
+            needsFullCheck: true,
+          }
+      )
+    }
+);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
index 3cf7f3a..58e2b26b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
@@ -192,11 +192,11 @@
   test_condition_known('style(--my-prop: foo - bar ())');
   test_condition_known('style(not ((--foo: calc(10px + 2em)) and ((--foo: url(x)))))');
   test_condition_known('style((--foo: bar) or (--bar: 10px))');
-  test_condition_known('style(--foo: bar !important)');
   test_condition_known('style(--my-prop:)');
   test_condition_known('style(--my-prop: )');
 
   test_condition_unknown('style(--foo: bar;)');
   test_condition_unknown('style(--foo)');
   test_condition_unknown('style(style(--foo: bar))');
+  test_condition_unknown('style(--foo: bar !important)');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html
index aec3f9a3..35110da 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html
@@ -94,3 +94,23 @@
   test_evaluation('outer style(--outer-space-after:true )', true);
   test_evaluation('outer style(--outer-space-after: true )', true);
 </script>
+
+<style>
+  #important {
+    --foo: bar;
+  }
+  @container style(--foo: bar) {
+    #important-child { color: green; }
+  }
+  @container style(--foo: bar !important) {
+    #important-child { color: red; }
+  }
+</style>
+<div id="important">
+  <div id="important-child"></div>
+</div>
+<script>
+  test(() => {
+    assert_equals(getComputedStyle(document.querySelector("#important-child")).color, "rgb(0, 128, 0)");
+  }, "Query custom property with !important declaration");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent-ref.html
new file mode 100644
index 0000000..200edcd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  .container {
+
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    clip-path: circle(15% at 50% 50%);
+  }
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent.html
new file mode 100644
index 0000000..4d4fee1b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-missing-0-percent.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-missing-0-percent-ref.html">
+<style>
+  .container {
+    /*TODO(crbug.com/1349062): Support animation keyframes without 0% or 100%.*/
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    /* Use a long animation that start at 50% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+    clip-path: circle(5% at 50% 50%);
+    animation: clippath 1000000s cubic-bezier(0, 1, 1, 0) -400000s;
+  }
+
+  @keyframes clippath {
+    80% {
+      clip-path: circle(25% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(50% at 50% 50%);
+    }
+  }
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    document.getAnimations()[0].ready.then(() => {
+      takeScreenshot();
+    });
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none-ref.html
new file mode 100644
index 0000000..f47e9f3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<style>
+  .target {
+    background-color: blue;
+    width: 100px;
+    height: 100px;
+  }
+
+  .outofbounds {
+    position: absolute;
+    top: 200px;
+    left: 200px;
+    height: 10px;
+    width: 10px;
+    background-color: blue
+  }
+</style>
+<div class="target">
+  <div class="outofbounds"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none.html
new file mode 100644
index 0000000..73b8a473
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-none.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-none-ref.html">
+<!--
+  This test verifies that
+  1) clip-path: none in an animation does not cause a crash
+  2) that clip-path: none displays correctly for an animation
+-->
+<style>
+  @keyframes clippath {
+    0% {
+      clip-path: inset(25% 25%);
+    }
+
+    100% {
+      clip-path: none;
+    }
+  }
+
+  .target {
+    animation: clippath 100000s infinite -50000s;
+    background-color: blue;
+    width: 100px;
+    height: 100px;
+  }
+
+  /*
+   * Ensure that clip-path: none is truly none, and not a rectangle that
+   * encompasses area of the parent
+   */
+  .outofbounds {
+    position: absolute;
+    top: 200px;
+    left: 200px;
+    height: 10px;
+    width: 10px;
+    background-color: blue
+  }
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="target">
+    <div class="outofbounds"></div>
+  </div>
+  <script>
+    document.getAnimations()[0].ready.then(() => {
+      takeScreenshot();
+    });
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1-ref.html
new file mode 100644
index 0000000..0feacb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  .container {
+
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    clip-path: inset(10px 10px);
+  }
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1.html
new file mode 100644
index 0000000..d6d0485
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length1.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="two-clip-path-animation-diff-length1-ref.html">
+<style>
+  .container {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    animation: clippath2 10s, clippath1 0.001s;
+  }
+
+  @keyframes clippath1 {
+    0% {
+      clip-path: circle(10% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(50% at 50% 50%);
+    }
+  }
+
+  @keyframes clippath2 {
+    0% {
+      clip-path: inset(10px 10px);
+    }
+
+    100% {
+      clip-path: inset(11px 11px);
+    }
+  }
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="../../../../web-animations/testcommon.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    // This test ensures that if we have two different-length animations, when
+    // the one with higher compositing order finishes, the other one would still
+    // run normally.
+    const animations = document.getAnimations();
+    animations[1].finished.then(() => {
+      takeScreenshot();
+    });
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2-ref.html
new file mode 100644
index 0000000..0feacb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  .container {
+
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    clip-path: inset(10px 10px);
+  }
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2.html
new file mode 100644
index 0000000..0d86120
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length2.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="two-clip-path-animation-diff-length2-ref.html">
+<style>
+  .container {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    animation: clippath2 10s, clippath1 2s 1s;
+  }
+
+  @keyframes clippath1 {
+    0% {
+      clip-path: circle(10% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(50% at 50% 50%);
+    }
+  }
+
+  @keyframes clippath2 {
+    0% {
+      clip-path: inset(10px 10px);
+    }
+
+    100% {
+      clip-path: inset(11px 11px);
+    }
+  }
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="../../../../web-animations/testcommon.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    // This test ensures that when there are two animations where one of them has
+    // animation delays, we show the right clip when the delayed animation is not
+    // started yet.
+    const animations = document.getAnimations();
+    Promise.all([animations[0].ready, animations[1].ready]).then(() => {
+      takeScreenshot();
+    });
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3-ref.html
new file mode 100644
index 0000000..853f47f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  .container {
+
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    clip-path: circle(50% at 50% 50%);
+  }
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3.html
new file mode 100644
index 0000000..3c8141a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/two-clip-path-animation-diff-length3.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="two-clip-path-animation-diff-length3-ref.html">
+<style>
+  .container {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    animation: clippath2 10000s, clippath1 2s 0.001s;
+  }
+
+  /* Use un-interpolatable keyframes to force discrete transition */
+  @keyframes clippath1 {
+    0% {
+      clip-path: circle(50% at 50% 50%);
+    }
+
+    100% {
+      clip-path: inset(11px 11px);
+    }
+  }
+
+  @keyframes clippath2 {
+    0% {
+      clip-path: circle(10% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(25% at 50% 50%);
+    }
+  }
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="../../../../web-animations/testcommon.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    // This test ensures that when there are two animations where one of them has
+    // animation delays, we show the right clip when the delayed animation is
+    // started.
+    const animations = document.getAnimations();
+    Promise.all([animations[0].ready, animations[1].ready]).then(() => {
+      waitForAnimationFrames(3).then(() => {
+        takeScreenshot();
+      });
+    });
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/fast/css-generated-content/quotes-xml-lang.xhtml b/third_party/blink/web_tests/fast/css-generated-content/quotes-xml-lang.xhtml
index 9590558c..642f668 100644
--- a/third_party/blink/web_tests/fast/css-generated-content/quotes-xml-lang.xhtml
+++ b/third_party/blink/web_tests/fast/css-generated-content/quotes-xml-lang.xhtml
@@ -6,6 +6,6 @@
             <a href="http://www.w3.org/TR/xhtml1/#C_7">http://www.w3.org/TR/xhtml1/#C_7</a>
         </p>
 
-        <q xml:lang="ru" lang="en"></q>
+        <div xml:lang="ru" lang="en"><q></q></div>
     </body>
 </html>
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt
deleted file mode 100644
index 65f1035..0000000
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt
+++ /dev/null
@@ -1,117 +0,0 @@
-This is a testharness.js-based test.
-Found 113 tests; 112 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS (width)
-PASS (min-width: 0px)
-PASS (max-width: 0px)
-PASS (height)
-PASS (min-height: 0px)
-PASS (max-height: 0px)
-PASS (aspect-ratio)
-PASS (min-aspect-ratio: 1/2)
-PASS (max-aspect-ratio: 1/2)
-PASS (orientation: portrait)
-PASS (inline-size)
-PASS (min-inline-size: 0px)
-PASS (max-inline-size: 0px)
-PASS (block-size)
-PASS (min-block-size: 0px)
-PASS (max-block-size: 0px)
-PASS (width: 100px)
-PASS ((width: 100px))
-PASS (not (width: 100px))
-PASS ((width: 100px) and (height: 100px))
-PASS (((width: 40px) or (width: 50px)) and (height: 100px))
-PASS ((width: 100px) and ((height: 40px) or (height: 50px)))
-PASS (((width: 40px) and (height: 50px)) or (height: 100px))
-PASS ((width: 50px) or ((width: 40px) and (height: 50px)))
-PASS ((width: 100px) and (not (height: 100px)))
-PASS (width < 100px)
-PASS (width <= 100px)
-PASS (width = 100px)
-PASS (width > 100px)
-PASS (width >= 100px)
-PASS (100px < width)
-PASS (100px <= width)
-PASS (100px = width)
-PASS (100px > width)
-PASS (100px >= width)
-PASS (100px < width < 200px)
-PASS (100px < width <= 200px)
-PASS (100px <= width < 200px)
-PASS (100px > width > 200px)
-PASS (100px > width >= 200px)
-PASS (100px >= width > 200px)
-PASS (width: calc(10px))
-PASS (width: calc(10em))
-PASS (width: calc(10px + 10em))
-PASS (width < calc(10px + 10em))
-PASS (width < max(10px, 10em))
-PASS (calc(10px + 10em) < width)
-PASS (calc(10px + 10em) < width < max(30px, 30em))
-PASS (width: 100px) and (height: 100px)
-PASS (width: 100px) or (height: 100px)
-PASS not (width: 100px)
-PASS foo(width)
-PASS size(width)
-PASS (asdf)
-PASS (resolution > 100dpi)
-PASS (resolution: 150dpi)
-PASS (color)
-PASS (min-color: 1)
-PASS (color-index >= 1)
-PASS size(grid)
-PASS (grid)
-PASS (width == 100px)
-PASS (100px == width)
-PASS (100px = width = 200px)
-PASS (100px < width > 200px)
-PASS (100px <= width >= 200px)
-PASS (100px <= width > 200px)
-PASS (100px < width >= 200px)
-PASS (100px : width : 200px)
-PASS screen
-PASS print
-PASS not print
-PASS only print
-PASS screen and (width: 100px)
-PASS screen or (width: 100px)
-PASS not screen and (width: 100px)
-PASS not screen or (width: 100px)
-PASS (width: 100px), (height: 100px)
-PASS foo (width: 100px)
-PASS name not (width <= 500px)
-PASS not (width <= 500px)
-PASS Container name: foo
-PASS Container name:  foo
-PASS Container name:  foo 
-PASS Container name: foo foo
-PASS Container name: 1px
-PASS Container name: 50gil
-PASS Container name: name(foo)
-PASS Container name: type(inline-size)
-PASS Container name: "foo"
-PASS Container name: "inherit"
-PASS Container name: none
-PASS Container name: None
-PASS Container name: normal
-PASS Container name: Normal
-PASS Container name: auto
-PASS Container name: Auto
-PASS Container name: and
-PASS Container name: or
-PASS Container name: not
-PASS Container name: And
-PASS Container name: oR
-PASS Container name: nOt
-PASS style(--my-prop: foo)
-PASS style(--my-prop: foo - bar ())
-PASS style(not ((--foo: calc(10px + 2em)) and ((--foo: url(x)))))
-PASS style((--foo: bar) or (--bar: 10px))
-FAIL style(--foo: bar !important) assert_equals: expected "true" but got ""
-PASS style(--my-prop:)
-PASS style(--my-prop: )
-PASS style(--foo: bar;)
-PASS style(--foo)
-PASS style(style(--foo: bar))
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt
deleted file mode 100644
index 9981d12..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-This is a testharness.js-based test.
-FAIL show: No autofocus, no delegatesFocus, no siblings assert_equals: expected Element node <button tabindex="-1" id="focus-between-tests">Focus betw... but got Element node <div></div>
-FAIL showModal: No autofocus, no delegatesFocus, no siblings assert_equals: expected Element node <body>
-
-<!--
-  We focus this one between each test, to en... but got Element node <div></div>
-PASS show: No autofocus, no delegatesFocus, sibling before
-PASS showModal: No autofocus, no delegatesFocus, sibling before
-FAIL show: No autofocus, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: No autofocus, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-PASS show: No autofocus, yes delegatesFocus, no siblings
-PASS showModal: No autofocus, yes delegatesFocus, no siblings
-PASS show: No autofocus, yes delegatesFocus, sibling before
-PASS showModal: No autofocus, yes delegatesFocus, sibling before
-PASS show: No autofocus, yes delegatesFocus, sibling after
-PASS showModal: No autofocus, yes delegatesFocus, sibling after
-PASS show: Autofocus before, no delegatesFocus
-PASS showModal: Autofocus before, no delegatesFocus
-PASS show: Autofocus before, yes delegatesFocus
-PASS showModal: Autofocus before, yes delegatesFocus
-PASS show: Autofocus after, no delegatesFocus
-PASS showModal: Autofocus after, no delegatesFocus
-PASS show: Autofocus after, yes delegatesFocus
-PASS showModal: Autofocus after, yes delegatesFocus
-PASS show: Autofocus on shadow host, yes delegatesFocus, no siblings
-PASS showModal: Autofocus on shadow host, yes delegatesFocus, no siblings
-FAIL show: Autofocus on shadow host, yes delegatesFocus, sibling before assert_equals: expected Element node <div autofocus=""></div> but got Element node <button tabindex="-1">Focusable</button>
-FAIL showModal: Autofocus on shadow host, yes delegatesFocus, sibling before assert_equals: expected Element node <div autofocus=""></div> but got Element node <button tabindex="-1">Focusable</button>
-PASS show: Autofocus on shadow host, yes delegatesFocus, sibling after
-PASS showModal: Autofocus on shadow host, yes delegatesFocus, sibling after
-FAIL show: Autofocus on shadow host, no delegatesFocus, no siblings assert_equals: expected Element node <button tabindex="-1" id="focus-between-tests">Focus betw... but got Element node <div autofocus=""></div>
-FAIL showModal: Autofocus on shadow host, no delegatesFocus, no siblings assert_equals: expected Element node <body>
-
-<!--
-  We focus this one between each test, to en... but got Element node <div autofocus=""></div>
-PASS show: Autofocus on shadow host, no delegatesFocus, sibling before
-PASS showModal: Autofocus on shadow host, no delegatesFocus, sibling before
-FAIL show: Autofocus on shadow host, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div autofocus=""></div>
-FAIL showModal: Autofocus on shadow host, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div autofocus=""></div>
-PASS show: Autofocus inside shadow tree, yes delegatesFocus, no siblings
-PASS showModal: Autofocus inside shadow tree, yes delegatesFocus, no siblings
-FAIL show: Autofocus inside shadow tree, yes delegatesFocus, sibling before assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: Autofocus inside shadow tree, yes delegatesFocus, sibling before assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL show: Autofocus inside shadow tree, yes delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: Autofocus inside shadow tree, yes delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL show: Autofocus inside shadow tree, no delegatesFocus, no siblings assert_equals: expected Element node <button tabindex="-1" id="focus-between-tests">Focus betw... but got Element node <div></div>
-FAIL showModal: Autofocus inside shadow tree, no delegatesFocus, no siblings assert_equals: expected Element node <body>
-
-<!--
-  We focus this one between each test, to en... but got Element node <div></div>
-FAIL show: Autofocus inside shadow tree, no delegatesFocus, sibling before assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: Autofocus inside shadow tree, no delegatesFocus, sibling before assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL show: Autofocus inside shadow tree, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: Autofocus inside shadow tree, no delegatesFocus, sibling after assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/focus-after-close-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/focus-after-close-expected.txt
deleted file mode 100644
index 77bcc34..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/focus-after-close-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This is a testharness.js-based test.
-FAIL Focus should be moved to the previously focused element (Simple dialog usage) assert_equals: expected Element node <input></input> but got Element node <button id="button1">This is a button1</button>
-FAIL Focus should be moved to the previously focused element (Complex dialog usage) promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'focus')"
-FAIL Focus should be moved to the previously focused element even if it has moved in between show/close assert_equals: Focus should be restored to previously focused input expected Element node <input></input> but got Element node <button id="button1">This is a button1</button>
-FAIL Focus should be moved to the previously focused element even if it has moved to shadow DOM root in between show/close assert_equals: Element is in correct position expected Element node <dialog>
-    <button id="button1">This is a button1</butt... but got null
-FAIL Focus should be moved to the body if the previously focused element is removed assert_equals: expected Element node <body>
-
-<dialog>
-    <button id="button1">This is a butto... but got Element node <button id="button1">This is a button1</button>
-FAIL Focus should be moved to the shadow DOM host if the previouly focused element is a shadow DOM node assert_equals: expected Element node <div></div> but got Element node <button id="button1">This is a button1</button>
-FAIL Focus should not scroll if the previously focused element is outside the viewport assert_equals: expected Element node <button style="top: 610px; position: absolute;"></button> but got Element node <button id="button1">This is a button1</button>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index da6046d..dfb70170 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -325,6 +325,8 @@
 placeItems
 placeSelf
 pointerEvents
+popUpHideDelay
+popUpShowDelay
 position
 prefix
 quotes
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-property-listing-expected.txt
index 62bd2f184..ed7fe4a9 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -287,6 +287,8 @@
     perspective
     perspective-origin
     pointer-events
+    pop-up-hide-delay
+    pop-up-show-delay
     position
     quotes
     r
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index c25b109..b8ae76f8 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -96,6 +96,7 @@
     property contains
     property contentEditable
     property dataset
+    property defaultOpen
     property dir
     property dispatchEvent
     property draggable
@@ -123,6 +124,7 @@
     property hasChildNodes
     property hasPointerCapture
     property hidden
+    property hidePopUp
     property id
     property inert
     property innerHTML
@@ -270,6 +272,10 @@
     property parentElement
     property parentNode
     property part
+    property popUp
+    property popUpHideTarget
+    property popUpShowTarget
+    property popUpToggleTarget
     property prefix
     property prepend
     property previousElementSibling
@@ -305,6 +311,7 @@
     property setHTML
     property setPointerCapture
     property shadowRoot
+    property showPopUp
     property slot
     property spellcheck
     property style
@@ -1278,6 +1285,7 @@
     property computedStyleMap
     property contains
     property dataset
+    property defaultOpen
     property dispatchEvent
     property elementTiming
     property firstChild
@@ -1301,6 +1309,7 @@
     property hasAttributes
     property hasChildNodes
     property hasPointerCapture
+    property hidePopUp
     property id
     property innerHTML
     property insertAdjacentElement
@@ -1438,6 +1447,10 @@
     property parentElement
     property parentNode
     property part
+    property popUp
+    property popUpHideTarget
+    property popUpShowTarget
+    property popUpToggleTarget
     property prefix
     property prepend
     property previousElementSibling
@@ -1473,6 +1486,7 @@
     property setHTML
     property setPointerCapture
     property shadowRoot
+    property showPopUp
     property slot
     property style
     property tabIndex
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
index ec65ec90..42acbce 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -1842,6 +1842,7 @@
     getter clientLeft
     getter clientTop
     getter clientWidth
+    getter defaultOpen
     getter elementTiming
     getter firstElementChild
     getter id
@@ -1860,6 +1861,10 @@
     getter onwebkitfullscreenerror
     getter outerHTML
     getter part
+    getter popUp
+    getter popUpHideTarget
+    getter popUpShowTarget
+    getter popUpToggleTarget
     getter prefix
     getter previousElementSibling
     getter role
@@ -1895,6 +1900,7 @@
     method hasAttributeNS
     method hasAttributes
     method hasPointerCapture
+    method hidePopUp
     method insertAdjacentElement
     method insertAdjacentHTML
     method insertAdjacentText
@@ -1922,6 +1928,7 @@
     method setAttributeNodeNS
     method setHTML
     method setPointerCapture
+    method showPopUp
     method toggleAttribute
     method webkitMatchesSelector
     method webkitRequestFullScreen
@@ -1967,6 +1974,7 @@
     setter ariaValueText
     setter classList
     setter className
+    setter defaultOpen
     setter elementTiming
     setter id
     setter innerHTML
@@ -1980,6 +1988,10 @@
     setter onwebkitfullscreenerror
     setter outerHTML
     setter part
+    setter popUp
+    setter popUpHideTarget
+    setter popUpShowTarget
+    setter popUpToggleTarget
     setter role
     setter scrollLeft
     setter scrollTop
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt
deleted file mode 100644
index bee1f6e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS show: When autofocus is not present, the first focusable shadow-including descendant must be focused
-PASS showModal: When autofocus is not present, the first focusable shadow-including descendant must be focused
-FAIL show: autofocus outside a shadow tree must take precedence over earlier in-shadow-tree focusable elements assert_equals: expected Element node <div></div> but got Element node <button tabindex="-1" autofocus="">Focusable</button>
-FAIL showModal: autofocus outside a shadow tree must take precedence over earlier in-shadow-tree focusable elements assert_equals: expected Element node <div></div> but got Element node <button tabindex="-1" autofocus="">Focusable</button>
-FAIL show: autofocus inside a shadow tree must be ignored: no focusable elements outside the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <button tabindex="-1" autofocus="">Focusable</button>
-FAIL showModal: autofocus inside a shadow tree must be ignored: no focusable elements outside the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <button tabindex="-1" autofocus="">Focusable</button>
-FAIL show: autofocus inside a shadow tree must be ignored: focusable element before the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: autofocus inside a shadow tree must be ignored: focusable element before the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL show: autofocus inside a shadow tree must be ignored: focusable element after the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-FAIL showModal: autofocus inside a shadow tree must be ignored: focusable element after the shadow tree assert_equals: expected Element node <button tabindex="-1" class="focus-me">Focusable</button> but got Element node <div></div>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/dialogfocus-old-behavior/README.md b/third_party/blink/web_tests/virtual/dialogfocus-old-behavior/README.md
deleted file mode 100644
index e578c3a..0000000
--- a/third_party/blink/web_tests/virtual/dialogfocus-old-behavior/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a virtual test suite for the *old* dialog element focusing steps, to
-ensure it respects the feature flag, in case this behavior needs to be disabled
-via Finch.
-
-Flag: --disable-features=DialogFocusNewSpecBehavior
-Bug: crbug.com/298078
diff --git a/third_party/blink/web_tests/virtual/popup-disabled/README.md b/third_party/blink/web_tests/virtual/popup-disabled/README.md
index bd5ab1e..e594e8e 100644
--- a/third_party/blink/web_tests/virtual/popup-disabled/README.md
+++ b/third_party/blink/web_tests/virtual/popup-disabled/README.md
@@ -1,5 +1,5 @@
 # Overview
 
-This suite runs a small subset of tests with `--disable-blink-features=HTMLPopupAttribute`
+This suite runs a small subset of tests with `--disable-features=HTMLPopupAttribute`
 to make sure no Popup API functionality is inadvertently exposed when the feature is
 disabled.
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/aggregatable-debug-report.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/aggregatable-debug-report.sub.https.html
index a2c87e6..60f0a84 100644
--- a/third_party/blink/web_tests/wpt_internal/attribution-reporting/aggregatable-debug-report.sub.https.html
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/aggregatable-debug-report.sub.https.html
@@ -22,7 +22,6 @@
   });
   await registerAttributionSrc(t, {
     trigger: {
-      event_trigger_data: [{trigger_data: '0'}],
       debug_key: trigger_debug_key,
       aggregatable_trigger_data: [
         {
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html
index 822b7015..0c1ddb6 100644
--- a/third_party/blink/web_tests/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html
@@ -15,7 +15,6 @@
   attribution_reporting_promise_test(async t => {
     await registerAttributionSrc(t, {
       source: {
-        source_event_id: '999',
         destination: `https://{{host}}`,
         filter_data: testCase.source_filter_data,
       },
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-position-inline-004.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-position-inline-004.html
new file mode 100644
index 0000000..adf79bf
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-position-inline-004.html
@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#propdef-anchor-name">
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos">
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos">
+<link rel="author" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<style>
+body > div {
+  font-family: Ahem;
+  font-size: 10px;
+  line-height: 1;
+  width: 10em;
+}
+.cb {
+  position: relative;
+}
+.columns {
+  column-count: 3;
+  column-fill: auto;
+  column-gap: 1em;
+  column-width: 10em;
+  orphans: 1;
+  widows: 1;
+  width: 32em;
+  height: 50px;
+  background: yellow;
+}
+.anchor1 {
+  anchor-name: --a1;
+  color: red;
+}
+.target {
+  position: absolute;
+  background: lime;
+  opacity: .2;
+}
+.target1-pos {
+  left: anchor(--a1 left);
+  top: anchor(--a1 top);
+  right: anchor(--a1 right);
+  bottom: anchor(--a1 bottom);
+}
+.target1-size {
+  left: anchor(--a1 left);
+  top: anchor(--a1 top);
+  width: anchor-size(--a1 width);
+  height: anchor-size(--a1 height);
+}
+</style>
+<body onload="checkLayout('.target')">
+  <!-- The inline anchor appear in a single line inline containing block. -->
+  <div class="cb">
+    <div>spacer</div>
+    <div>
+      0
+      <span class="cb">
+        12
+        <span class="anchor1">a1</span>
+        34
+        <span class="target target1-pos"
+              data-offset-x=30 data-offset-y=0
+              data-expected-width=20 data-expected-height=10></span>
+        <span class="target target1-size"
+              data-offset-x=30 data-offset-y=0
+              data-expected-width=20 data-expected-height=10></span>
+      </span>
+      <span class="target target1-pos"
+            data-offset-x=50 data-offset-y=10
+            data-expected-width=20 data-expected-height=10></span>
+      <span class="target target1-size"
+            data-offset-x=50 data-offset-y=10
+            data-expected-width=20 data-expected-height=10></span>
+    </div>
+    <span class="target target1-pos"
+          data-offset-x=50 data-offset-y=10
+          data-expected-width=20 data-expected-height=10></span>
+    <span class="target target1-size"
+          data-offset-x=50 data-offset-y=10
+          data-expected-width=20 data-expected-height=10></span>
+  </div>
+
+  <!-- The inline anchor and inline containing block wrap to two lines. -->
+  <div class="cb">
+    <div>
+      0
+      <span class="cb">
+        12
+        <span class="anchor1">a1 a1 a1</span>
+        345
+        <span class="target target1-pos"
+              data-offset-x=-20 data-offset-y=0
+              data-expected-width=100 data-expected-height=20></span>
+        <span class="target target1-size"
+              data-offset-x=-20 data-offset-y=0
+              data-expected-width=100 data-expected-height=20></span>
+      </span>
+      <span class="target target1-pos"
+            data-offset-x=0 data-offset-y=0
+            data-expected-width=100 data-expected-height=20></span>
+      <span class="target target1-size"
+            data-offset-x=0 data-offset-y=0
+            data-expected-width=100 data-expected-height=20></span>
+    </div>
+    <span class="target target1-pos"
+          data-offset-x=0 data-offset-y=0
+          data-expected-width=100 data-expected-height=20></span>
+    <span class="target target1-size"
+          data-offset-x=0 data-offset-y=0
+          data-expected-width=100 data-expected-height=20></span>
+  </div>
+
+  <!-- The inline anchor and inline containing block have forced line breaks. -->
+  <div class="cb">
+    <div>
+      0
+      <span class="cb">
+        12
+        <span class="anchor1">a1<br>a1</span>
+        345
+        <span class="target target1-pos"
+              data-offset-x=-20 data-offset-y=0
+              data-expected-width=70 data-expected-height=20></span>
+        <span class="target target1-size"
+              data-offset-x=-20 data-offset-y=0
+              data-expected-width=70 data-expected-height=20></span>
+      </span>
+      <span class="target target1-pos"
+            data-offset-x=0 data-offset-y=0
+            data-expected-width=70 data-expected-height=20></span>
+      <span class="target target1-size"
+            data-offset-x=0 data-offset-y=0
+            data-expected-width=70 data-expected-height=20></span>
+    </div>
+    <span class="target target1-pos"
+          data-offset-x=0 data-offset-y=0
+          data-expected-width=70 data-expected-height=20></span>
+    <span class="target target1-size"
+          data-offset-x=0 data-offset-y=0
+          data-expected-width=70 data-expected-height=20></span>
+  </div>
+
+  <!-- The inline anchor and inline containing block wrap to two columns. -->
+  <div class="cb columns">
+    <div class="spacer" style="height: 90px"></div>
+    <div>
+      0
+      <span class="cb">
+        12
+        <span class="anchor1">a1 a1 a1</span>
+        345
+        <span class="target target1-pos"
+              data-offset-x=30 data-offset-y=-40
+              data-expected-width=80 data-expected-height=50></span>
+        <span class="target target1-size"
+              data-offset-x=30 data-offset-y=-40
+              data-expected-width=80 data-expected-height=50></span>
+      </span>
+      <span class="target target1-pos"
+            data-offset-x=160 data-offset-y=0
+            data-expected-width=80 data-expected-height=50></span>
+      <span class="target target1-size"
+            data-offset-x=160 data-offset-y=0
+            data-expected-width=80 data-expected-height=50></span>
+    </div>
+    <span class="target target1-pos"
+          data-offset-x=160 data-offset-y=0
+          data-expected-width=80 data-expected-height=50></span>
+    <span class="target target1-size"
+          data-offset-x=160 data-offset-y=0
+          data-expected-width=80 data-expected-height=50></span>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
index c9e1134..bd3e7278 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
@@ -11,7 +11,6 @@
 }
 .anchor1 {
   anchor-name: --a1;
-  position: absolute;
   width: 5px;
   height: 7px;
   background: orange;
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element-ref.html b/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element-ref.html
new file mode 100644
index 0000000..1c51562
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<title>Shared transitions: offscreen content</title>
+<link rel="help" href="https://github.com/WICG/shared-element-transitions">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<style>
+body { background: pink }
+.flex {
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  align-items: flex-start;
+}
+.box {
+  width: 100px;
+  height: 500px;
+  contain: paint;
+  background: green;
+  border: 1px solid black;
+  box-sizing: border-box;
+}
+</style>
+
+<div class=flex>
+ <div class=box>ancestor c-v</div>
+ <div class=box>self c-v</div>
+ <div class=box>descendant c-v</div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element.html b/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element.html
new file mode 100644
index 0000000..60eb91b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/document-transition/content-visibility-auto-shared-element.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>Shared transitions: offscreen content with content-visibility auto</title>
+<link rel="help" href="https://github.com/WICG/shared-element-transitions">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="match" href="content-visibility-auto-shared-element-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+body {
+  overflow: hidden;
+}
+.flex {
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  align-items: flex-start;
+}
+.box {
+  width: 100px;
+  height: 500px;
+  contain: paint;
+}
+.shared {
+  background: green;
+  border: 1px solid black;
+  box-sizing: border-box;
+}
+.spacer {
+  height: 3000px;
+}
+#hidden {
+  width: 10px;
+  height: 10px;
+  background: red;
+  contain: paint;
+  page-transition-tag: hidden;
+}
+.locked {
+  content-visibility: auto;
+  contain-intrinsic-size: 500px;
+}
+  
+html::page-transition-container(hidden) { animation-duration: 300s; }
+html::page-transition-image-wrapper(hidden) { visibility: hidden; }
+
+html::page-transition-container(*) { animation-duration: 0s; }
+html::page-transition-incoming-image(*) { animation: unset; opacity: 0; }
+html::page-transition-outgoing-image(*) { animation: unset; opacity: 1; }
+html::page-transition-container(root) { display: none; }
+html::page-transition { background: pink }
+
+</style>
+
+<div class=flex>
+ <div id=dst1 class=box></div>
+ <div id=dst2 class=box></div>
+ <div id=dst3 class=box></div>
+</div>
+<div id=hidden></div>
+<div class=spacer></div>
+<div id=content>
+ <div class=locked><div id=src1 class="box shared" style="page-transition-tag: one">ancestor c-v</div></div>
+ <div id=src2 class="box shared locked" style="page-transition-tag: two">self c-v</div>
+ <div id=src3 class="box shared" style="page-transition-tag: three"><div class=locked>descendant c-v</div></div>
+</div>
+
+<script>
+async function runTest() {
+  document.createDocumentTransition().prepare(() => {
+    content.remove();
+    dst1.style = "page-transition-tag: one";
+    dst2.style = "page-transition-tag: two";
+    dst3.style = "page-transition-tag: three";
+    requestAnimationFrame(() => requestAnimationFrame(takeScreenshot));
+  });
+}
+onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
+</script>
diff --git a/third_party/updater/chrome_mac_universal/3pp/fetch.py b/third_party/updater/chrome_mac_universal/3pp/fetch.py
index 4ece784..eb150f7 100755
--- a/third_party/updater/chrome_mac_universal/3pp/fetch.py
+++ b/third_party/updater/chrome_mac_universal/3pp/fetch.py
@@ -11,7 +11,7 @@
 import urllib.request
 
 # TODO(crbug.com/1268555): This is compared lexically. Remove it before M1000.
-MIN_VERSION = '100.0.4876.0'
+MIN_VERSION = '106.0.5243.0'
 
 def fetch():
     """
diff --git a/third_party/updater/chrome_mac_universal/3pp/install.sh b/third_party/updater/chrome_mac_universal/3pp/install.sh
index da865ae..5cb9db1 100755
--- a/third_party/updater/chrome_mac_universal/3pp/install.sh
+++ b/third_party/updater/chrome_mac_universal/3pp/install.sh
@@ -11,4 +11,4 @@
 # The commands below should output the built product to this directory.
 PREFIX="$1"
 
-mv updater/GoogleUpdater_test.app "$PREFIX"
+mv GoogleUpdater_test.app "$PREFIX"
diff --git a/third_party/updater/chrome_mac_universal_prod/3pp/fetch.py b/third_party/updater/chrome_mac_universal_prod/3pp/fetch.py
index d8e8d36..38e48f7 100755
--- a/third_party/updater/chrome_mac_universal_prod/3pp/fetch.py
+++ b/third_party/updater/chrome_mac_universal_prod/3pp/fetch.py
@@ -11,7 +11,7 @@
 import urllib.request
 
 # TODO(crbug.com/1336630): This is compared lexically. Remove it before M1000.
-MIN_VERSION = '105.0.5151.0'
+MIN_VERSION = '106.0.5243.0'
 
 def fetch():
     """
diff --git a/third_party/updater/chrome_win_x86/3pp/fetch.py b/third_party/updater/chrome_win_x86/3pp/fetch.py
index d2d52855..1e1d507 100755
--- a/third_party/updater/chrome_win_x86/3pp/fetch.py
+++ b/third_party/updater/chrome_win_x86/3pp/fetch.py
@@ -11,7 +11,7 @@
 import urllib.request
 
 # TODO(crbug.com/1268555): This is compared lexically. Remove it before M1000.
-MIN_VERSION = '100.0.4876.0'
+MIN_VERSION = '106.0.5243.0'
 
 def fetch():
     """
diff --git a/third_party/updater/chrome_win_x86_64/3pp/fetch.py b/third_party/updater/chrome_win_x86_64/3pp/fetch.py
index a81fc90..0d76f06 100755
--- a/third_party/updater/chrome_win_x86_64/3pp/fetch.py
+++ b/third_party/updater/chrome_win_x86_64/3pp/fetch.py
@@ -11,7 +11,7 @@
 import urllib.request
 
 # TODO(crbug.com/1268555): This is compared lexically. Remove it before M1000.
-MIN_VERSION = '100.0.4876.0'
+MIN_VERSION = '106.0.5243.0'
 
 def fetch():
     """
diff --git a/third_party/updater/chromium_mac_amd64/3pp/install.sh b/third_party/updater/chromium_mac_amd64/3pp/install.sh
index d30d200..3b70975b 100755
--- a/third_party/updater/chromium_mac_amd64/3pp/install.sh
+++ b/third_party/updater/chromium_mac_amd64/3pp/install.sh
@@ -11,4 +11,4 @@
 # The commands below should output the built product to this directory.
 PREFIX="$1"
 
-mv updater/ChromiumUpdater_test.app "$PREFIX"
+mv ChromiumUpdater_test.app "$PREFIX"
diff --git a/third_party/updater/chromium_mac_arm64/3pp/install.sh b/third_party/updater/chromium_mac_arm64/3pp/install.sh
index d30d200..3b70975b 100755
--- a/third_party/updater/chromium_mac_arm64/3pp/install.sh
+++ b/third_party/updater/chromium_mac_arm64/3pp/install.sh
@@ -11,4 +11,4 @@
 # The commands below should output the built product to this directory.
 PREFIX="$1"
 
-mv updater/ChromiumUpdater_test.app "$PREFIX"
+mv ChromiumUpdater_test.app "$PREFIX"
diff --git a/tools/gdb/viewg.gdb b/tools/gdb/viewg.gdb
index c664f607..7328630 100644
--- a/tools/gdb/viewg.gdb
+++ b/tools/gdb/viewg.gdb
@@ -21,13 +21,13 @@
   else
     set pagination off
     set print elements 0
-    set logging off
+    set logging enabled off
     set logging file ~/state.dot
     set logging overwrite on
     set logging redirect on
-    set logging on
+    set logging enabled on
     printf "%s\n", views::PrintViewGraph(this).c_str()
-    set logging off
+    set logging enabled off
     shell dot -Tsvg -o ~/state.svg ~/state.dot
     set pagination on
   end
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 189ca872..a2ee6bf 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -71202,6 +71202,11 @@
   <int value="2" label="NTP Promo link clicked"/>
 </enum>
 
+<enum name="NtpPromoDismissAction">
+  <int value="0" label="NTP Promo dismissed"/>
+  <int value="1" label="NTP Promo restored"/>
+</enum>
+
 <enum name="NtpRequestThrottlerStatus">
   <int value="0" label="Interactive request - quota granted"/>
   <int value="1" label="Background request - quota granted"/>
diff --git a/tools/metrics/histograms/metadata/enterprise/OWNERS b/tools/metrics/histograms/metadata/enterprise/OWNERS
index 8bed734..9dfc941 100644
--- a/tools/metrics/histograms/metadata/enterprise/OWNERS
+++ b/tools/metrics/histograms/metadata/enterprise/OWNERS
@@ -4,4 +4,3 @@
 # Use chromium-metrics-reviews@google.com as a backup.
 poromov@chromium.org
 zmin@chromium.org
-amraboelkher@chromium.org
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index b119524..1e4b4b1 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -883,7 +883,7 @@
 </histogram>
 
 <histogram name="NewTabPage.FeedPositionSegmentationResult"
-    enum="FeedPositionSegmentationResult" expires_after="2022-09-24">
+    enum="FeedPositionSegmentationResult" expires_after="2022-11-15">
   <owner>hanxi@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <owner>spdonghao@chromium.org</owner>
@@ -1502,6 +1502,19 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.Promos.DismissAction" enum="NtpPromoDismissAction"
+    expires_after="2023-08-11">
+  <owner>danpeng@google.com</owner>
+  <owner>pauladedeji@google.com</owner>
+  <owner>chrome-desktop-ntp@google.com</owner>
+  <summary>
+    Logs when action is taken on promo e.g. dismissal or restoration. Only
+    logged on the 1P NTP. Note that even if the user has Google as their default
+    search engine, Incognito and Guest mode NTPs are not considered 1P and don't
+    log this histogram.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.Promos.LinkClicked" units="count"
     expires_after="never">
 <!-- expires-never: part of top-line metric (internal: go/chrome-browser-nsm) -->
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index e644d8c..2b71981c 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -214,6 +214,24 @@
   </token>
 </histogram>
 
+<histogram name="SBClientDownload.DownloadRequestDurationMedium" units="ms"
+    expires_after="2022-12-25">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the total time it takes for the SafeBrowsing download service to
+    check whether the content of a download is malicious or not, including file
+    feature extraction, allowlist checking, and server ping. This histogram only
+    includes checks that sent a ping to the SafeBrowsing server. It does not
+    include requests that were cancelled, but does include requests that
+    received a bad response.
+
+    This histogram is a &quot;medium&quot; version of
+    SBClientDownload.DownloadRequestDuration. It has a maximum of 3 minutes to
+    help investigate long-tail downloads.
+  </summary>
+</histogram>
+
 <histogram name="SBClientDownload.DownloadRequestNetError" enum="NetErrorCodes"
     expires_after="2023-04-24">
   <owner>drubery@chromium.org</owner>
@@ -306,6 +324,27 @@
   </token>
 </histogram>
 
+<histogram name="SBClientDownload.FileFeatureExtractionDuration" units="ms"
+    expires_after="2022-12-15">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records how long it takes to extract features from a downloaded file. This
+    is aggregated over all file types. It is recorded once per download.
+  </summary>
+</histogram>
+
+<histogram name="SBClientDownload.GetTabRedirectsDuration" units="ms"
+    expires_after="2022-12-15">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records how long it takes to get tab redirects when populating a
+    ClientDownloadRequest. This is aggregated over all file types. It is
+    recorded once per download.
+  </summary>
+</histogram>
+
 <histogram name="SBClientDownload.MalwareDeepScanResult.{trigger}"
     enum="SBClientDownloadCheckResult" expires_after="2023-04-28">
   <owner>drubery@chromium.org</owner>
@@ -327,6 +366,18 @@
   </token>
 </histogram>
 
+<histogram name="SBClientDownload.MemoryMapFileDuration" units="ms"
+    expires_after="2022-12-15">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records how long it takes to load the download as a memory-mapped file for
+    the purpose of extracting executable features. This is aggregated over all
+    file types that are checked as executables. It is recorded once per
+    download.
+  </summary>
+</histogram>
+
 <histogram name="SBClientDownload.SafeDownloadOpenedLatency2.{ShowAction}"
     units="ms" expires_after="2023-04-13">
   <owner>xinghuilu@chromium.org</owner>
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
index 72cacd18..11c3b95 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "21.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "32.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "15.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "18.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
index c1b5eed..e8e947f6 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "23.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "27.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "13.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "2.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
index 0a5e5ca..6cd0ce1 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "14.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "19.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "11.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "13.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel4_webview-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel4_webview-perf_timing.json
index faf48c6..28efe7e 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel4_webview-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel4_webview-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "15.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "18.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "10.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "2.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/lacros-eve-perf_timing.json b/tools/perf/core/shard_maps/timing_data/lacros-eve-perf_timing.json
index af7878c..b5c460f 100644
--- a/tools/perf/core/shard_maps/timing_data/lacros-eve-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/lacros-eve-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "63.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "70.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "56.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "58.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json b/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
index c88764e..abf51be 100644
--- a/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "9.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "13.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "7.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "9.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
index 7c8af74..06e26ab 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "12.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "15.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1089,10 +1085,6 @@
     },
     {
         "duration": "10.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
-        "duration": "10.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
     {
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
index e20353d..ef2d3df4 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
@@ -1092,10 +1092,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "17.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "20.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1128,10 +1124,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "13.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "15.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
index 9acc55c..3d913ca 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "8.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "9.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "7.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "8.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json b/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
index 63b0a3f..eb1aa41 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "14.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "17.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "12.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "13.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
index f140cb6..ee1883c9 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
@@ -1052,10 +1052,6 @@
         "name": "blink_perf.paint/complex-content-slow-scroll.html"
     },
     {
-        "duration": "21.0",
-        "name": "blink_perf.paint/complex-iframe-filtered.html"
-    },
-    {
         "duration": "26.0",
         "name": "blink_perf.paint/contain-update-layer-tree.html"
     },
@@ -1088,10 +1084,6 @@
         "name": "blink_perf.paint/modify-selection.html"
     },
     {
-        "duration": "16.0",
-        "name": "blink_perf.paint/move-text-with-mask.html"
-    },
-    {
         "duration": "18.0",
         "name": "blink_perf.paint/paint-offset-changes.html"
     },
diff --git a/tools/traffic_annotation/auditor/chromeos/safe_list.txt b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
index 8646eee..296c69e 100644
--- a/tools/traffic_annotation/auditor/chromeos/safe_list.txt
+++ b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
@@ -18,7 +18,6 @@
 all,chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
 all,chrome/services/sharing/nearby/platform/webrtc.cc
 all,chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc
-all,ash/services/device_sync/cryptauth_device_manager_impl.cc
 all,chrome/browser/nearby_sharing/instantmessaging/receive_messages_express.cc
 all,chrome/browser/nearby_sharing/instantmessaging/send_message_express.cc
 all,chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 12489efb..67fb3fa4 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -386,4 +386,5 @@
  <item id="chrome_commerce_subscriptions_create" added_in_milestone="105" content_hash_code="076a1b98" os_list="windows,android,linux,chromeos" file_path="components/commerce/core/subscriptions/subscriptions_server_proxy.cc" />
  <item id="chrome_commerce_subscriptions_delete" added_in_milestone="105" content_hash_code="05f47d4b" os_list="windows,android,linux,chromeos" file_path="components/commerce/core/subscriptions/subscriptions_server_proxy.cc" />
  <item id="chrome_commerce_subscriptions_get" added_in_milestone="105" content_hash_code="02f49372" os_list="windows,android,linux,chromeos" file_path="components/commerce/core/subscriptions/subscriptions_server_proxy.cc" />
+ <item id="cryptauth_get_my_devices" added_in_milestone="106" type="partial" second_id="oauth2_api_call_flow" content_hash_code="04b33199" os_list="chromeos" file_path="ash/services/device_sync/cryptauth_device_manager_impl.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 8fd13cc..cd7c14c 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -255,6 +255,7 @@
       <annotation id="chrome_commerce_subscriptions_create"/>
       <annotation id="chrome_commerce_subscriptions_delete"/>
       <annotation id="chrome_commerce_subscriptions_get"/>
+      <annotation id="cryptauth_get_my_devices"/>
     </sender>
   </group>
   <group name="Admin Features" hidden="true">
diff --git a/ui/accessibility/extensions/color_contrast_companion/background.js b/ui/accessibility/extensions/color_contrast_companion/background.js
new file mode 100644
index 0000000..68e985c
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/background.js
@@ -0,0 +1,96 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stream = null;
+var ui = null;
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+  console.log('Got message');
+  console.log(request);
+  if (request.close && ui) {
+    chrome.windows.remove(ui.id);
+  }
+});
+
+function capture() {
+  let video = document.createElement('video');
+  video.autoplay = true;
+  document.body.appendChild(video);
+  video.addEventListener('canplay', () => {
+    if (video.videoWidth < 100) {
+      // We probably need the permission again.
+      getStream();
+      return;
+    }
+
+    window.setTimeout(() => {
+      let canvas = document.createElement('canvas');
+      canvas.height = video.videoHeight;
+      canvas.width = video.videoWidth;
+      var context = canvas.getContext('2d');
+      context.drawImage(video, 0, 0, canvas.width, canvas.height);
+      var imageDataUrl = canvas.toDataURL();
+      document.body.removeChild(video);
+
+      // Close the stream so it stops using resources.
+      let tracks = stream.getTracks();
+      tracks.forEach(function(track) {
+        track.stop();
+      });
+      stream = null;
+
+      chrome.windows.create(
+          {
+            'url': chrome.runtime.getURL('ui.html'),
+            'focused': true,
+            'type': 'popup',
+            'state': 'fullscreen'
+          },
+          (win) => {
+            ui = win;
+            var tab = win.tabs[0];
+            window.setTimeout(() => {
+              console.log('Sending message');
+              chrome.tabs.sendMessage(tab.id, {'imageDataUrl': imageDataUrl});
+            }, 250);
+          });
+    }, 250);
+  });
+  video.srcObject = stream;
+}
+
+function getStream() {
+  chrome.desktopCapture.chooseDesktopMedia(['screen'], (streamId) => {
+    let video = document.createElement('video');
+    navigator.mediaDevices
+        .getUserMedia({
+          video: {
+            mandatory: {
+              chromeMediaSource: 'desktop',
+              chromeMediaSourceId: streamId,
+            }
+          }
+        })
+        .then(returnedStream => {
+          stream = returnedStream;
+          capture();
+        });
+  });
+}
+
+chrome.browserAction.onClicked.addListener(() => {
+  if (!stream) {
+    getStream();
+    return;
+  }
+
+  capture();
+});
+
+var alreadyShowedHelp = localStorage.getItem('help');
+if (!alreadyShowedHelp) {
+  localStorage.setItem('help', 'true');
+  chrome.windows.create(
+      {'url': chrome.runtime.getURL('help.html'), 'focused': true});
+}
diff --git a/ui/accessibility/extensions/color_contrast_companion/browser_action.png b/ui/accessibility/extensions/color_contrast_companion/browser_action.png
new file mode 100644
index 0000000..a72899c
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/browser_action.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/common.js b/ui/accessibility/extensions/color_contrast_companion/common.js
new file mode 100644
index 0000000..b4cf068
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/common.js
@@ -0,0 +1,101 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var DEFAULT_SCHEME = 3;
+var MAX_SCHEME = 5;
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+function getEnabled() {
+  var result = localStorage['enabled'];
+  if (result === 'true' || result === 'false') {
+    return (result === 'true');
+  }
+  localStorage['enabled'] = 'true';
+  return true;
+}
+
+function setEnabled(enabled) {
+  localStorage['enabled'] = enabled;
+}
+
+function getKeyAction() {
+  var keyAction = localStorage['keyaction'];
+  if (keyAction == 'global' || keyAction == 'site') {
+    return keyAction;
+  }
+  keyAction = 'global';
+  localStorage['keyaction'] = keyAction;
+  return keyAction;
+}
+
+function setKeyAction(keyAction) {
+  if (keyAction != 'global' && keyAction != 'site') {
+    keyAction = 'global';
+  }
+  localStorage['keyaction'] = keyAction;
+}
+
+function getDefaultScheme() {
+  var scheme = localStorage['scheme'];
+  if (scheme >= 0 && scheme <= MAX_SCHEME) {
+    return scheme;
+  }
+  scheme = DEFAULT_SCHEME;
+  localStorage['scheme'] = scheme;
+  return scheme;
+}
+
+function setDefaultScheme(scheme) {
+  if (!(scheme >= 0 && scheme <= MAX_SCHEME)) {
+    scheme = DEFAULT_SCHEME;
+  }
+  localStorage['scheme'] = scheme;
+}
+
+function getSiteScheme(site) {
+  var scheme = getDefaultScheme();
+  try {
+    var siteSchemes = JSON.parse(localStorage['siteschemes']);
+    scheme = siteSchemes[site];
+    if (!(scheme >= 0 && scheme <= MAX_SCHEME)) {
+      scheme = getDefaultScheme();
+    }
+  } catch (e) {
+    scheme = getDefaultScheme();
+  }
+  return scheme;
+}
+
+function setSiteScheme(site, scheme) {
+  if (!(scheme >= 0 && scheme <= MAX_SCHEME)) {
+    scheme = getDefaultScheme();
+  }
+  var siteSchemes = {};
+  try {
+    siteSchemes = JSON.parse(localStorage['siteschemes']);
+    siteSchemes['www.example.com'] = getDefaultScheme();
+  } catch (e) {
+    siteSchemes = {};
+  }
+  siteSchemes[site] = scheme;
+  localStorage['siteschemes'] = JSON.stringify(siteSchemes);
+}
+
+function resetSiteSchemes() {
+  var siteSchemes = {};
+  localStorage['siteschemes'] = JSON.stringify(siteSchemes);
+}
+
+function siteFromUrl(url) {
+  var a = document.createElement('a');
+  a.href = url;
+  return a.hostname;
+}
+
+function isDisallowedUrl(url) {
+  return url.startsWith('chrome') || url.startsWith('about');
+}
diff --git a/ui/accessibility/extensions/color_contrast_companion/contrast-128.png b/ui/accessibility/extensions/color_contrast_companion/contrast-128.png
new file mode 100644
index 0000000..2814544c
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/contrast-128.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/contrast-16.png b/ui/accessibility/extensions/color_contrast_companion/contrast-16.png
new file mode 100644
index 0000000..9313a62
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/contrast-16.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/contrast-19.png b/ui/accessibility/extensions/color_contrast_companion/contrast-19.png
new file mode 100644
index 0000000..84d1b827
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/contrast-19.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/contrast-38.png b/ui/accessibility/extensions/color_contrast_companion/contrast-38.png
new file mode 100644
index 0000000..998b3f1a
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/contrast-38.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/contrast-48.png b/ui/accessibility/extensions/color_contrast_companion/contrast-48.png
new file mode 100644
index 0000000..0f61571e
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/contrast-48.png
Binary files differ
diff --git a/ui/accessibility/extensions/color_contrast_companion/help.html b/ui/accessibility/extensions/color_contrast_companion/help.html
new file mode 100644
index 0000000..e99eb01
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/help.html
@@ -0,0 +1,151 @@
+<head>
+  <title>
+    Color Contrast Companion
+  </title>
+  <style>
+    .logo {
+      float: left;
+    }
+    h1 {
+      padding-top: 24px;
+    }
+    body, div, p {
+      font-family: system-ui, sans-serif;
+      font-size: 14pt;
+      line-height: 1.5;
+    }
+    p {
+      margin: 24px 0;
+    }
+    body {
+      background-color: #eee;
+      padding: 0;
+      margin: 0;
+    }
+    #main {
+      background-color: #fff;
+      width: 780px;
+      margin: 0 auto;
+      border-left: 1px solid #444;
+      border-right: 1px solid #444;
+      padding: 24px;
+      min-height: 500px;
+    }
+    .top {
+      width: 100%;
+      float: left;
+    }
+    .note {
+      padding: 0 16px;
+      border: 1px solid #88e;
+    }
+  </style>
+</head>
+<body>
+  <div id="main">
+    <div class="top">
+      <div class="logo">
+        <img src="contrast-128.png" alt="Color Contrast Companion logo">
+      </div>
+      <h1>Color Contrast Companion</h2>
+      <p>
+        <i>Quickly compute the color contrast of pixels anywhere on your screen.</i>
+      </p>
+    </div>
+
+    <p>
+      This is a tool to compute the contrast between a foreground and
+      background color, to help test whether an application is
+      providing enough contrast so that text can be read by people
+      with moderately low vision.
+    </p>
+
+    <p>
+      <a href="https://www.w3.org/TR/WCAG21/#contrast-minimum">WCAG</a>
+      recommends these contrast ratios:
+      <ul>
+        <li> AA: 4.5:1 for all text, 3:1 for 18 pts and larger.
+        <li> AAA: 7:1 for all text, 4.5:1 for 18 pts and larger.
+        <li> Focus indicators: 3:1 if 3px thick, 4.5:1 if less.
+          <a href="https://www.w3.org/WAI/GL/low-vision-a11y-tf/wiki/Contrast_(Minimum)#Focus_Indicators">(Source)</a>
+      </ul>
+    </p>
+    <p>
+      For more information, see the links at the bottom of this page.
+    </p>
+
+    <h2>How to use Color Contrast Companion</h2>
+
+    <div class="note">
+      <p>
+        <b>Note</b>:
+        If you're a web developer or if you're testing a web app, you already
+        have great color contrast tools built directly into Chrome's
+        Developer Tools! For more information, see
+        <a href="https://developers.google.com/web/updates/2018/01/devtools#contrast">
+          Contrast ratio in the Color Picker</a>.
+      </p>
+      <p>
+        However, there are cases where you might want to check the contrast of
+        something on your screen that you can't inspect in Chrome's Developer Tools -
+        such as Chrome's UI, text inside an image, or another app outside of Chrome.
+        For those cases, this extension can help!
+      </p>
+    </div>
+
+    <p>
+      <b>Step 1</b>:
+      Click on the Color Contrast Companion icon on the right side of the
+      Chrome toolbar. If you have a lot of extensions installed, the icon
+      might be inside the Chrome menu (the three dots).
+    </p>
+
+    <p>
+      <img src="browser_action.png" alt="Image of icon in Chrome toolbar">
+    </p>
+
+    <p>
+      <b>Step 2</b>:
+      A <b>Share Your Screen</b> dialog pops up asking for your permission to
+      share your entire screen with Color Contrast Companion.
+      This is necessary for Color Contrast Companion to take a screenshot of
+      your entire desktop. This image is never saved or sent to any server, it's
+      only used to let you pick colors. Click the Share button.
+    </p>
+
+    <p>
+      <b>Step 3</b>:
+      A window opens up showing a screenshot of your computer screen, magnified.
+      Scroll to the portion of the screen containing the pixels you're interested
+      in. Click once to pick the foreground color, click again to pick the
+      background color. Keep clicking as many times as necessary if you're not
+      happy with the colors you picked the first time.
+    </p>
+
+    <p>
+      <b>Step 4</b>:
+      The contrast ratio is shown at the top of the page. Copy the text from the
+      text box and paste it directly into a bug report if necessary, then click
+      the Close button.
+    </p>
+
+    <h2>Further reading</h2>
+
+    <p>
+      <ul>
+        <li>
+          <a href="https://www.w3.org/TR/WCAG21/#contrast-minimum">
+            Web Contents Accessibility Guidelines (WCAG) 2.1 spec
+          </a>
+        <li>
+
+          <a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html">
+            Understanding Success Criterion 1.4.3: Contrast (Minimum)
+          </a>
+        </li>
+      </ul>
+    </p>
+
+  </div>
+
+</body>
diff --git a/ui/accessibility/extensions/color_contrast_companion/highcontrast.js b/ui/accessibility/extensions/color_contrast_companion/highcontrast.js
new file mode 100644
index 0000000..83e31cf
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/highcontrast.js
@@ -0,0 +1,177 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var mode;
+var enabled = false;
+var scheme = '';
+var timeoutId = null;
+
+var filterMap = {
+  '0': 'url("#hc_extension_off")',
+  '1': 'url("#hc_extension_highcontrast")',
+  '2': 'url("#hc_extension_grayscale")',
+  '3': 'url("#hc_extension_invert")',
+  '4': 'url("#hc_extension_invert_grayscale")',
+  '5': 'url("#hc_extension_yellow_on_black")'
+};
+
+var svgContent =
+    '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"><defs><filter x="0" y="0" width="99999" height="99999" id="hc_extension_off"><feComponentTransfer><feFuncR type="table" tableValues="0 1"/><feFuncG type="table" tableValues="0 1"/><feFuncB type="table" tableValues="0 1"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_highcontrast"><feComponentTransfer><feFuncR type="gamma" exponent="3.0"/><feFuncG type="gamma" exponent="3.0"/><feFuncB type="gamma" exponent="3.0"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_highcontrast_back"><feComponentTransfer><feFuncR type="gamma" exponent="0.33"/><feFuncG type="gamma" exponent="0.33"/><feFuncB type="gamma" exponent="0.33"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_grayscale"><feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/><feComponentTransfer><feFuncR type="gamma" exponent="3"/><feFuncG type="gamma" exponent="3"/><feFuncB type="gamma" exponent="3"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_grayscale_back"><feComponentTransfer><feFuncR type="gamma" exponent="0.33"/><feFuncG type="gamma" exponent="0.33"/><feFuncB type="gamma" exponent="0.33"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_invert"><feComponentTransfer><feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_invert_back"><feComponentTransfer><feFuncR type="table" tableValues="1 0"/><feFuncG type="table" tableValues="1 0"/><feFuncB type="table" tableValues="1 0"/></feComponentTransfer><feComponentTransfer><feFuncR type="gamma" exponent="1.7"/><feFuncG type="gamma" exponent="1.7"/><feFuncB type="gamma" exponent="1.7"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_invert_grayscale"><feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/><feComponentTransfer><feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/></feComponentTransfer></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_yellow_on_black"><feComponentTransfer><feFuncR type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncG type="gamma" amplitude="-1" exponent="3" offset="1"/><feFuncB type="gamma" amplitude="-1" exponent="3" offset="1"/></feComponentTransfer><feColorMatrix type="matrix" values="0.3 0.5 0.2 0 0 0.3 0.5 0.2 0 0 0 0 0 0 0 0 0 0 1 0"/></filter><filter x="0" y="0" width="99999" height="99999" id="hc_extension_yellow_on_black_back"><feComponentTransfer><feFuncR type="table" tableValues="1 0"/><feFuncG type="table" tableValues="1 0"/><feFuncB type="table" tableValues="1 0"/></feComponentTransfer><feComponentTransfer><feFuncR type="gamma" exponent="0.33"/><feFuncG type="gamma" exponent="0.33"/><feFuncB type="gamma" exponent="0.33"/></feComponentTransfer></filter></defs></svg>';
+
+var cssTemplate =
+    'html[hc="a0"] {   -webkit-filter: url("#hc_extension_off"); } html[hcx="0"] img[src*="jpg"], html[hcx="0"] img[src*="jpeg"], html[hcx="0"] svg image, html[hcx="0"] img.rg_i, html[hcx="0"] embed, html[hcx="0"] object, html[hcx="0"] video {   -webkit-filter: url("#hc_extension_off"); }  html[hc="a1"] {   -webkit-filter: url("#hc_extension_highcontrast"); } html[hcx="1"] img[src*="jpg"], html[hcx="1"] img[src*="jpeg"], html[hcx="1"] img.rg_i, html[hcx="1"] svg image, html[hcx="1"] embed, html[hcx="1"] object, html[hcx="1"] video {   -webkit-filter: url("#hc_extension_highcontrast_back"); }  html[hc="a2"] {   -webkit-filter: url("#hc_extension_grayscale"); } html[hcx="2"] img[src*="jpg"], html[hcx="2"] img[src*="jpeg"], html[hcx="2"] img.rg_i, html[hcx="2"] svg image, html[hcx="2"] embed, html[hcx="2"] object, html[hcx="2"] video {   -webkit-filter: url("#hc_extension_grayscale_back"); }  html[hc="a3"] {   -webkit-filter: url("#hc_extension_invert"); } html[hcx="3"] img[src*="jpg"], html[hcx="3"] img[src*="jpeg"], html[hcx="3"] img.rg_i, html[hcx="3"] svg image, html[hcx="3"] embed, html[hcx="3"] object, html[hcx="3"] video {   -webkit-filter: url("#hc_extension_invert_back"); }  html[hc="a4"] {   -webkit-filter: url("#hc_extension_invert_grayscale"); } html[hcx="4"] img[src*="jpg"], html[hcx="4"] img[src*="jpeg"], html[hcx="4"] img.rg_i, html[hcx="4"] svg image, html[hcx="4"] embed, html[hcx="4"] object, html[hcx="4"] video {   -webkit-filter: url("#hc_extension_invert_back"); }  html[hc="a5"] {   -webkit-filter: url("#hc_extension_yellow_on_black"); } html[hcx="5"] img[src*="jpg"], html[hcx="5"] img[src*="jpeg"], html[hcx="5"] img.rg_i, html[hcx="5"] svg image, html[hcx="5"] embed, html[hcx="5"] object, html[hcx="5"] video {   -webkit-filter: url("#hc_extension_yellow_on_black_back"); }';
+
+/**
+ * Add the elements to the pgae that make high-contrast adjustments possible.
+ */
+function addOrUpdateExtraElements() {
+  if (!enabled)
+    return;
+
+  // We used to include the CSS, but that doesn't work when the document
+  // uses the <base> element to set a relative url. So instead we
+  // add a <style> element directly to the document with the right
+  // urls hard-coded into it.
+  var style = document.getElementById('hc_style');
+  if (!style) {
+    var baseUrl = window.location.href.replace(window.location.hash, '');
+    var css = cssTemplate.replace(/#/g, baseUrl + '#');
+    style = document.createElement('style');
+    style.id = 'hc_style';
+    style.setAttribute('type', 'text/css');
+    style.innerHTML = css;
+    document.head.appendChild(style);
+  }
+
+  // Starting in Chrome 45 we can't apply a filter to the html element,
+  // so instead we create an element with low z-index that copies the
+  // body's background.
+  var bg = document.getElementById('hc_extension_bkgnd');
+  if (!bg) {
+    bg = document.createElement('div');
+    bg.id = 'hc_extension_bkgnd';
+    bg.style.position = 'fixed';
+    bg.style.left = '0px';
+    bg.style.top = '0px';
+    bg.style.right = '0px';
+    bg.style.bottom = '0px';
+    bg.style.zIndex = -1999999999;
+    document.body.appendChild(bg);
+  }
+  bg.style.display = 'block';
+  bg.style.background = window.getComputedStyle(document.body).background;
+
+  // As a special case, replace a zero-alpha background with white,
+  // otherwise we can't invert it.
+  var c = bg.style.backgroundColor;
+  c = c.replace(/\s\s*/g, '');
+  if (m = /^rgba\(([\d]+),([\d]+),([\d]+),([\d]+|[\d]*.[\d]+)\)/.exec(c)) {
+    if (m[4] == '0') {
+      bg.style.backgroundColor = '#fff';
+    }
+  }
+
+  // Add a hidden element with the SVG filters.
+  var wrap = document.getElementById('hc_extension_svg_filters');
+  if (wrap)
+    return;
+
+  wrap = document.createElement('span');
+  wrap.id = 'hc_extension_svg_filters';
+  wrap.setAttribute('hidden', '');
+  wrap.innerHTML = svgContent;
+  document.body.appendChild(wrap);
+}
+
+/**
+ * This is called on load and every time the mode might have changed
+ * (i.e. enabling/disabling, or changing the type of contrast adjustment
+ * for this page).
+ */
+function update() {
+  var html = document.documentElement;
+  if (enabled) {
+    if (!document.body) {
+      window.setTimeout(update, 100);
+      return;
+    }
+    addOrUpdateExtraElements();
+    if (html.getAttribute('hc') != mode + scheme)
+      html.setAttribute('hc', mode + scheme);
+    if (html.getAttribute('hcx') != scheme)
+      html.setAttribute('hcx', scheme);
+
+    if (window == window.top) {
+      window.scrollBy(0, 1);
+      window.scrollBy(0, -1);
+    }
+  } else {
+    html.setAttribute('hc', mode + '0');
+    html.setAttribute('hcx', '0');
+    window.setTimeout(function() {
+      html.removeAttribute('hc');
+      html.removeAttribute('hcx');
+      var bg = document.getElementById('hc_extension_bkgnd');
+      if (bg)
+        bg.style.display = 'none';
+    }, 0);
+  }
+}
+
+/**
+ * Called when we get a message from the background page.
+ */
+function onExtensionMessage(request) {
+  if (enabled != request.enabled || scheme != request.scheme) {
+    enabled = request.enabled;
+    scheme = request.scheme;
+    update();
+  }
+}
+
+/**
+ * KeyDown event handler
+ */
+function onKeyDown(evt) {
+  if (evt.keyCode == 122 /* F11 */ && evt.shiftKey) {
+    chrome.extension.sendRequest({'toggle_global': true});
+    evt.stopPropagation();
+    evt.preventDefault();
+    return false;
+  }
+  if (evt.keyCode == 123 /* F12 */ && evt.shiftKey) {
+    chrome.extension.sendRequest({'toggle_site': true});
+    evt.stopPropagation();
+    evt.preventDefault();
+    return false;
+  }
+  return true;
+}
+
+function init() {
+  if (window == window.top) {
+    mode = 'a';
+  } else {
+    mode = 'b';
+  }
+  chrome.extension.onRequest.addListener(onExtensionMessage);
+  chrome.extension.sendRequest({'init': true}, onExtensionMessage);
+  document.addEventListener('keydown', onKeyDown, false);
+
+  // Update again after a few seconds and again after load so that
+  // the background isn't wrong for long.
+  window.setTimeout(addOrUpdateExtraElements, 2000);
+  window.addEventListener('load', function() {
+    addOrUpdateExtraElements();
+
+    // Also update when the document body attributes change.
+    var config = {attributes: true, childList: false, characterData: false};
+    var observer = new MutationObserver(function(mutations) {
+      addOrUpdateExtraElements();
+    });
+    observer.observe(document.body, config);
+  });
+}
+
+init();
diff --git a/ui/accessibility/extensions/color_contrast_companion/manifest.json b/ui/accessibility/extensions/color_contrast_companion/manifest.json
new file mode 100644
index 0000000..2f06555
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/manifest.json
@@ -0,0 +1,22 @@
+{
+  "background": {
+    "scripts": [ "background.js" ]
+  },
+  "browser_action": {
+    "default_icon": {
+      "19": "contrast-19.png",
+      "38": "contrast-38.png"
+    },
+    "default_title": "Color Contrast Companion"
+  },
+  "description": "Quickly compute the color contrast of pixels anywhere on your screen.",
+  "icons": {
+    "128": "contrast-128.png",
+    "16": "contrast-16.png",
+    "48": "contrast-48.png"
+  },
+  "manifest_version": 2,
+  "name": "Color Contrast Companion",
+  "permissions": [ "desktopCapture", "tabs" ],
+  "version": "0.0.5"
+}
diff --git a/ui/accessibility/extensions/color_contrast_companion/ui.html b/ui/accessibility/extensions/color_contrast_companion/ui.html
new file mode 100644
index 0000000..fafc518
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/ui.html
@@ -0,0 +1,130 @@
+<head>
+  <style>
+    #top_panel {
+        position: fixed;
+        top: 0px;
+        left: 0px;
+        right: 0px;
+        height: 96px;
+        display: flex;
+    }
+    #img_panel {
+        cursor: crosshair;
+        position: fixed;
+        bottom: 0px;
+        left: 0px;
+        right: 0px;
+        top: 96px;
+        overflow: scroll;
+        padding: 4px;
+        border: 4px solid #ff9900;
+    }
+    .color {
+        margin: 8px;
+        width: 128px;
+        height: 48px;
+        border: 1px solid #000;
+    }
+    .rgb {
+    }
+    .ratio {
+        width: 88px;
+        height: 48px;
+        border: 1px solid #000;
+        text-align: center;
+        line-height: 48px;
+        font-size: 18px;
+        margin: 8px;
+    }
+    #details {
+        width: 148px;
+        height: 72px;
+        margin: 8px;
+    }
+    .current {
+        outline: 3px solid #f00;
+    }
+    .group {
+    }
+    .gtop {
+    }
+    .caption {
+        margin-left: 8px;
+    }
+    button {
+        width: 90px;
+        height: 32px;
+        margin: 8px 8px 0 8px;
+        color: #000;
+        font-weight: bold;
+        background-color: #fc9;
+    }
+    button:disabled {
+        color: #999;
+    }
+    canvas {
+      image-rendering: pixelated;
+      transform-origin: top left;
+    }
+    #highlight_container {
+      position: relative;
+      width: 0;
+      height: 0;
+      overflow: visible;
+    }
+    #highlight {
+      position: absolute;
+      outline: 2px solid #ff9900;
+      outline-offset: 2px;
+      z-index: 2;
+      pointer-events: none;
+    }
+  </style>
+</head>
+<body>
+  <div id="top_panel">
+    <div style="width:96px">
+      <img src="contrast-128.png" width=96 height=96 >
+    </div>
+    <div class="group">
+      <div class="gtop">
+        <div class="color" id="hover"></div>
+      </div>
+      <div class="caption">Hover: <span class="rgb" id="hoverrgb"></span></div>
+    </div>
+    <div class="group">
+      <div class="gtop">
+        <div class="color" id="fg"></div>
+      </div>
+      <div class="caption">Foreground: <span class="rgb" id="fgrgb"><span></div>
+    </div>
+    <div class="group">
+      <div class="gtop">
+        <div class="color" id="bg"></div>
+      </div>
+      <div class="caption">Background: <span class="rgb" id="bgrgb"></span></div>
+    </div>
+    <div>
+      <div class="ratio" id="ratio"></div>
+      <div class="caption">Contrast Ratio</div>
+    </div>
+    <div>
+      <textarea id="details"></textarea>
+    </div>
+    <div style="width: 104px">
+      <button id="zoomin">Zoom In (+)</button>
+      <button id="zoomout">Zoom Out (-)</button>
+    </div>
+    <div style="width: 104px">
+      <button id="close">Close</button>
+      <button id="help">Help</button>
+    </div>
+  </div>
+  <div id="img_panel">
+    <div id="highlight_container">
+      <div id="highlight"></div>
+    </div>
+    <canvas></canvas>
+  </div>
+  <script src="ui.js"></script>
+</body>
diff --git a/ui/accessibility/extensions/color_contrast_companion/ui.js b/ui/accessibility/extensions/color_contrast_companion/ui.js
new file mode 100644
index 0000000..4fc2ff3
--- /dev/null
+++ b/ui/accessibility/extensions/color_contrast_companion/ui.js
@@ -0,0 +1,290 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var maxScale = 32;
+
+var scale = 4;
+var fgtoggle = true;
+var fgcolor = null;
+var bgcolor = null;
+var srcImage = null;
+var imageData = null;
+var prevScale = 1;
+
+let close = document.getElementById('close');
+close.addEventListener('click', () => {
+  console.log('Sending message');
+  chrome.runtime.sendMessage({'close': true});
+});
+let help = document.getElementById('help');
+help.addEventListener('click', () => {
+  window.open(chrome.runtime.getURL('help.html'), '_blank');
+});
+
+let canvas = document.querySelector('canvas');
+
+function repaint() {
+  console.log('Repaint ' + scale);
+  let width = srcImage.naturalWidth;
+  let height = srcImage.naturalHeight;
+  let context = canvas.getContext('2d');
+  context.imageSmoothingEnabled = false;
+  canvas.style.transform = 'scale(' + scale + ')';
+
+  canvas.width = 1;
+  canvas.height = 1;
+  canvas.offsetLeft;
+  canvas.width = width;
+  canvas.height = height;
+  canvas.offsetLeft;
+
+  context.drawImage(srcImage, 0, 0, width, height);
+  imageData = context.getImageData(0, 0, width, height).data;
+}
+
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+  if (request.imageDataUrl) {
+    var img = document.createElement('img');
+    img.addEventListener('load', () => {
+      srcImage = img;
+      repaint();
+    });
+    img.src = request.imageDataUrl;
+  }
+});
+
+let hover = document.getElementById('hover');
+let hoverrgb = document.getElementById('hoverrgb');
+let fg = document.getElementById('fg');
+let fgrgb = document.getElementById('fgrgb');
+let bg = document.getElementById('bg');
+let bgrgb = document.getElementById('bgrgb');
+let scrollpanel = document.getElementById('img_panel');
+let ratio = document.getElementById('ratio');
+let details = document.getElementById('details');
+let zoomin = document.getElementById('zoomin');
+let zoomout = document.getElementById('zoomout');
+let highlight = document.getElementById('highlight');
+
+fg.classList.add('current');
+bg.classList.remove('current');
+
+function rgbToHex(color) {
+  var r = color[0];
+  var g = color[1];
+  var b = color[2];
+  return '#' + ((r << 16) | (g << 8) | b).toString(16);
+}
+
+function getRelativeLuminance(color) {
+  var rSRGB = color[0] / 255;
+  var gSRGB = color[1] / 255;
+  var bSRGB = color[2] / 255;
+  var r =
+      rSRGB <= .03928 ? rSRGB / 12.92 : Math.pow((rSRGB + .055) / 1.055, 2.4);
+  var g =
+      gSRGB <= .03928 ? gSRGB / 12.92 : Math.pow((gSRGB + .055) / 1.055, 2.4);
+  var b =
+      bSRGB <= .03928 ? bSRGB / 12.92 : Math.pow((bSRGB + .055) / 1.055, 2.4);
+  return .2126 * r + .7152 * g + .0722 * b;
+};
+
+function getContrast(color1, color2) {
+  var c1lum = getRelativeLuminance(color1);
+  var c2lum = getRelativeLuminance(color2);
+  return (Math.max(c1lum, c2lum) + .05) / (Math.min(c1lum, c2lum) + .05);
+}
+
+let context = canvas.getContext('2d');
+let bounds = canvas.getBoundingClientRect();
+
+function getXY(evt) {
+  var x = evt.clientX + scrollpanel.scrollLeft - bounds.left;
+  var y = evt.clientY + scrollpanel.scrollTop - bounds.top;
+  return [Math.floor(x / scale), Math.floor(y / scale)];
+}
+
+function getColor(x, y) {
+  try {
+    var pixelIndex = y * srcImage.naturalWidth + x;
+    return imageData.slice(4 * pixelIndex, 4 * (pixelIndex + 1));
+  } catch (e) {
+    return [0, 0, 0, 0];
+  }
+}
+
+function brightness(color) {
+  return (color[0] + color[1] + color[2]) / 3;
+}
+
+function localMax(x, y) {
+  // This needs to be optional. Doesn't always do what we want.
+  return [x, y];
+
+  if (x < 0 || x >= srcImage.naturalWidth || y < 0 ||
+      y + j >= srcImage.naturalHeight) {
+    return [x, y];
+  }
+
+  var ctr = getColor(x, y);
+  var max = brightness(ctr);
+
+  var max;
+  var amax;
+  for (var i = -2; i <= 2; i++) {
+    for (var j = -2; j <= 2; j++) {
+      if (x + i < 0 || x + i >= srcImage.naturalWidth)
+        continue;
+      if (y + j < 0 || y + j >= srcImage.naturalHeight)
+        continue;
+      var c = getColor(x + i, y + j);
+      var cbright = brightness(c);
+      if (max > 128 && cbright > max) {
+        max = cbright;
+        amax = [x + i, y + j];
+      } else if (max < 128 && cbright < max) {
+        max = cbright;
+        amax = [x + i, y + j];
+      }
+    }
+  }
+
+  if (amax)
+    return amax;
+  else
+    return [x, y];
+}
+
+canvas.addEventListener('mousemove', (evt) => {
+  var x1, y1, x, y;
+  [x1, y1] = getXY(evt);
+  [x, y] = localMax(x1, y1);
+  var color = getColor(x, y);
+  var hex = rgbToHex(color);
+  hover.style.backgroundColor = hex;
+  hoverrgb.innerText = hex;
+
+  if (scale >= 8) {
+    highlight.style.display = 'block';
+    highlight.style.left = (scale * x) + 'px';
+    highlight.style.top = (scale * y) + 'px';
+    highlight.style.width = scale + 'px';
+    highlight.style.height = scale + 'px';
+    highlight.style.top = (scale * y) + 'px';
+  } else {
+    highlight.style.display = 'none';
+  }
+});
+
+canvas.addEventListener('mouseenter', (evt) => {
+  highlight.style.display = 'block';
+});
+
+canvas.addEventListener('mouseleave', (evt) => {
+  highlight.style.display = 'none';
+});
+
+canvas.addEventListener('click', (evt) => {
+  var x1, y1, x, y;
+  [x1, y1] = getXY(evt);
+  [x, y] = localMax(x1, y1);
+  var color = getColor(x, y);
+  var hex = rgbToHex(color);
+  if (fgtoggle) {
+    fg.style.backgroundColor = hex;
+    fgrgb.innerText = hex;
+    fgcolor = color;
+    bg.classList.add('current');
+    fg.classList.remove('current');
+  } else {
+    bg.style.backgroundColor = hex;
+    bgrgb.innerText = hex;
+    bgcolor = color;
+    fg.classList.add('current');
+    bg.classList.remove('current');
+  }
+  fgtoggle = !fgtoggle;
+  if (fgcolor && bgcolor) {
+    var contrast = getContrast(fgcolor, bgcolor);
+    ratio.innerText = contrast.toFixed(2);
+    details.innerHTML = 'Foreground: ' + fgrgb.innerText + '\n' +
+        'Background: ' + bgrgb.innerText + '\n' +
+        'Ratio: ' + ratio.innerText;
+    details.select();
+  }
+});
+
+function updateZoom() {
+  highlight.style.display = 'none';
+  var prevScrollLeft = scrollpanel.scrollLeft;
+  var prevScrollTop = scrollpanel.scrollTop;
+  var panelBounds = scrollpanel.getBoundingClientRect();
+
+  localStorage.setItem('scale', scale);
+  zoomout.disabled = (scale == 1);
+  zoomin.disabled = (scale >= maxScale);
+  if (srcImage)
+    repaint();
+
+  console.log('prev: ' + prevScrollLeft + ', ' + prevScrollTop);
+  console.log('factor: ' + (scale / prevScale));
+  var newLeft = prevScrollLeft * (scale / prevScale);
+  var newTop = prevScrollTop * (scale / prevScale);
+  console.log('newLeft: ' + newLeft);
+  console.log('newTop: ' + newTop);
+  if (scale > prevScale) {
+    newLeft += panelBounds.width / 2;
+    newTop += panelBounds.height / 2;
+    console.log('c newLeft: ' + newLeft);
+    console.log('c newTop: ' + newTop);
+  } else if (scale < prevScale) {
+    newLeft -= panelBounds.width / 4;
+    newTop -= panelBounds.height / 4;
+    console.log('c newLeft: ' + newLeft);
+    console.log('c newTop: ' + newTop);
+  }
+  scrollpanel.scrollLeft = newLeft;
+  scrollpanel.scrollTop = newTop;
+  prevScale = scale;
+}
+
+var scalevalue = localStorage.getItem('scale');
+scale = parseInt(scalevalue, 10);
+if (!scale || scale < 1 || scale > maxScale)
+  scale = 4;
+prevScale = scale;
+
+updateZoom();
+
+function onZoomIn() {
+  if (scale < maxScale)
+    scale *= 2;
+  updateZoom();
+}
+
+function onZoomOut() {
+  if (scale > 1)
+    scale /= 2;
+  updateZoom();
+}
+
+zoomin.addEventListener('click', () => {
+  onZoomIn();
+});
+
+zoomout.addEventListener('click', () => {
+  onZoomOut();
+});
+
+document.addEventListener('keydown', function(e) {
+  if (e.key == '+' || e.key == '=') {
+    onZoomIn();
+  }
+  if (e.key == '-') {
+    onZoomOut();
+  }
+
+  console.log(e.key);
+});
diff --git a/ui/accessibility/platform/inspect/ax_inspect_scenario.h b/ui/accessibility/platform/inspect/ax_inspect_scenario.h
index 3f870ae48..5be2b665 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_scenario.h
+++ b/ui/accessibility/platform/inspect/ax_inspect_scenario.h
@@ -10,6 +10,7 @@
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
 
 namespace base {
 class FilePath;
@@ -17,8 +18,6 @@
 
 namespace ui {
 
-struct AXPropertyFilter;
-struct AXNodeFilter;
 class AXScriptInstruction;
 
 // Describes the test execution flow, which is parsed from a sequence
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc
index 4a840e9..1cba159 100644
--- a/ui/base/clipboard/clipboard.cc
+++ b/ui/base/clipboard/clipboard.cc
@@ -197,9 +197,8 @@
       ExtractCustomPlatformNames(buffer, data_dst);
   for (const auto& items : custom_format_names)
     format_names.push_back(base::ASCIIToUTF16(items.first));
-  for (const auto& item : GetStandardFormats(buffer, data_dst)) {
+  for (const auto& item : GetStandardFormats(buffer, data_dst))
     format_names.push_back(item);
-  }
   return format_names;
 }
 
diff --git a/ui/base/clipboard/clipboard_data.cc b/ui/base/clipboard/clipboard_data.cc
index 066df20e..68ff253 100644
--- a/ui/base/clipboard/clipboard_data.cc
+++ b/ui/base/clipboard/clipboard_data.cc
@@ -30,7 +30,7 @@
   return data;
 }
 
-ClipboardData::ClipboardData() : web_smart_paste_(false), format_(0) {}
+ClipboardData::ClipboardData() = default;
 
 ClipboardData::ClipboardData(const ClipboardData& other) {
   format_ = other.format_;
diff --git a/ui/base/clipboard/clipboard_data.h b/ui/base/clipboard/clipboard_data.h
index 30ba0c75..85c2af9 100644
--- a/ui/base/clipboard/clipboard_data.h
+++ b/ui/base/clipboard/clipboard_data.h
@@ -198,7 +198,7 @@
   std::string custom_data_data_;
 
   // WebKit smart paste data.
-  bool web_smart_paste_;
+  bool web_smart_paste_ = false;
 
   // Svg data.
   std::string svg_data_;
@@ -206,7 +206,7 @@
   // text/uri-list filenames data.
   std::vector<ui::FileInfo> filenames_;
 
-  int format_;
+  int format_ = 0;
 
   // The source of the data.
   std::unique_ptr<DataTransferEndpoint> src_;
diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h
index c4790931..96f5bf8b 100644
--- a/ui/base/clipboard/scoped_clipboard_writer.h
+++ b/ui/base/clipboard/scoped_clipboard_writer.h
@@ -47,7 +47,7 @@
   // Converts |text| to UTF-8 and adds it to the clipboard.
   void WriteText(const std::u16string& text);
 
-  // Adds HTML to the clipboard.  The url parameter is optional, but especially
+  // Adds HTML to the clipboard. The url parameter is optional, but especially
   // useful if the HTML fragment contains relative links.
   void WriteHTML(const std::u16string& markup, const std::string& source_url);
 
diff --git a/ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h b/ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h
index df34a841..9f54a83 100644
--- a/ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h
+++ b/ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/component_export.h"
+#include "ui/base/ime/ash/component_extension_ime_manager.h"
 #include "ui/base/ime/ash/component_extension_ime_manager_delegate.h"
 
 namespace ash {
diff --git a/ui/ozone/public/platform_screen.h b/ui/ozone/public/platform_screen.h
index 20ec099..d8b85c5 100644
--- a/ui/ozone/public/platform_screen.h
+++ b/ui/ozone/public/platform_screen.h
@@ -51,7 +51,7 @@
 
   virtual ~PlatformScreen();
 
-  // Provide a |display:;Display| for each physical display available to Chrome.
+  // Provide a |display::Display| for each physical display available to Chrome.
   virtual const std::vector<display::Display>& GetAllDisplays() const = 0;
 
   // Returns the |Display| whose origin (top left corner) is 0,0 in the
diff --git a/ui/views/view.cc b/ui/views/view.cc
index b625d43..80d3b7b 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -607,9 +607,6 @@
 
     // Notify all other subscriptions of the change.
     OnPropertyChanged(&visible_, kPropertyEffectsPaint);
-
-    if (was_visible)
-      UpdateTooltip();
   }
 
   if (parent_) {