diff --git a/DEPS b/DEPS
index de66601a..7d5ac6e 100644
--- a/DEPS
+++ b/DEPS
@@ -304,7 +304,7 @@
   # 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': '7635aa1d11c238dbb641242c2667ce64d1bf0d60',
+  'skia_revision': 'cda8bf7920926fb6ea030e919dfd3f1af20b4148',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -331,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:12.20230410.0.1',
+  'fuchsia_version': 'version:12.20230410.2.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -355,7 +355,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6',
+  'freetype_revision': '1a4c18f7cb70a2ae4fa209bb75a6c6c5b6ace0f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -790,7 +790,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'c8d2390b002d8df1df2511b69a5af0e40caa1138',
+    'd996d2ddeac36d91a9966cae7b9784e2db74cd27',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -889,7 +889,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'cEqLZFRh9Z9H_JScNlrMykH0L0LHtW7NiTBYnOcN3osC',
+          'version': 'LVwfiKHngM0Fs6-dE2Nv5TyhC_0tZqnolPHHgoTJ2zIC',
         },
       ],
       'dep_type': 'cipd',
@@ -900,7 +900,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'En_kT6CUgnTK89Y_P1H3yYGKp44arsOPLAi5NcKePM0C',
+          'version': 'B4Qm0Hx3ySFQJcqEqfP_7vNZy7oPHnxYEroMpwWFNTAC',
         },
       ],
       'dep_type': 'cipd',
@@ -911,7 +911,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'Qy9v9yvY-QE5GNO1wuElUjgIbi6xz4sDqu1uALUsAJQC',
+          'version': 'aOdqg50SXdXFE-MaRhaTyTuyBsiwADHhLTCLilmSaj4C',
         },
       ],
       'dep_type': 'cipd',
@@ -1187,7 +1187,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f2b209128b13c03b87dc057f6654afbc8deaa079',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '97f213a7084d48eb65e06964bee9b83c1994e440',
       'condition': 'checkout_chromeos',
   },
 
@@ -1219,7 +1219,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '019e73a13bf58542f2572daba6fbc5e389c40607',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd3cbd40cf98db81c6846a66b161e6588c9484888',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1691,7 +1691,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e8d3baca2f854d7f4a61d76b9d22f3aeaaf98444',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '047662b410c343a7519f7dd21929e88f70202fab',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1966,7 +1966,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@37960e72284610b638e5ce9250453c6e6dbbb14c',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@bed82b68462aa06edc8f410ff62ad644ddc1a048',
     'condition': 'checkout_src_internal',
   },
 
@@ -1996,7 +1996,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'yROnMzQO_9iFEnueXW5nZxNicmKV96qicI90T5aPyWwC',
+        'version': 'TmgZnR3psuJHeawU00CGq8zQPXs1c4fWSJwYQq9MyygC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2007,7 +2007,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'hgXyqYLQ0lHmnBinjgPVhNkRKniEiOuvVmuX9NgBHI0C',
+        'version': 'Z9WSBkcFY0RAWBvwsn08_gPfgFbvbA8Yj1O9Z9fW-MwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/accelerators/accelerator_alias_converter_unittest.cc b/ash/accelerators/accelerator_alias_converter_unittest.cc
index de015cd5..48261d5 100644
--- a/ash/accelerators/accelerator_alias_converter_unittest.cc
+++ b/ash/accelerators/accelerator_alias_converter_unittest.cc
@@ -355,8 +355,7 @@
                           ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN}}},
     }));
 
-// TODO(crbug.com/1430899): Re-enable this test.
-TEST_P(TopRowAliasTest, DISABLED_CheckTopRowAlias) {
+TEST_P(TopRowAliasTest, CheckTopRowAlias) {
   // Add fake keyboards based on layout type.
   fake_keyboard_manager_->RemoveAllDevices();
   for (int i = 0; const std::string& layout : keyboard_layout_types_) {
diff --git a/ash/accessibility/ui/accessibility_focus_ring_controller_unittest.cc b/ash/accessibility/ui/accessibility_focus_ring_controller_unittest.cc
index ba659fc1..7ea7fe8 100644
--- a/ash/accessibility/ui/accessibility_focus_ring_controller_unittest.cc
+++ b/ash/accessibility/ui/accessibility_focus_ring_controller_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
diff --git a/ash/accessibility/ui/accessibility_focus_ring_group_unittest.cc b/ash/accessibility/ui/accessibility_focus_ring_group_unittest.cc
index 049675a..8d97b78 100644
--- a/ash/accessibility/ui/accessibility_focus_ring_group_unittest.cc
+++ b/ash/accessibility/ui/accessibility_focus_ring_group_unittest.cc
@@ -93,7 +93,7 @@
 }
 
 TEST_F(AccessibilityFocusRingGroupTest, RectsToRingsSimpleBoundsCheck) {
-  // Easy sanity check. Given a single rectangle, make sure we get back
+  // Easy confidence check. Given a single rectangle, make sure we get back
   // a focus ring with the same bounds.
   std::vector<gfx::Rect> rects;
   rects.push_back(gfx::Rect(20, 30, 70, 150));
diff --git a/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc b/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
index aae432a5..b30bb59 100644
--- a/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
+++ b/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/functional/bind.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/aura/window.h"
-#include "ui/base/ime/dummy_text_input_client.h"
+#include "ui/base/ime/fake_text_input_client.h"
 #include "ui/compositor/compositor_switches.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/base_event_utils.h"
@@ -33,9 +33,9 @@
 
 namespace {
 
-class MockTextInputClient : public ui::DummyTextInputClient {
+class MockTextInputClient : public ui::FakeTextInputClient {
  public:
-  MockTextInputClient() : ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {}
+  MockTextInputClient() : ui::FakeTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {}
 
   MockTextInputClient(const MockTextInputClient&) = delete;
   MockTextInputClient& operator=(const MockTextInputClient&) = delete;
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index 7c47460..1f5e7646 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -58,6 +58,19 @@
   return suffix;
 }
 
+void UseFixedPlaceholderTextAndHideCursor(
+    raw_ptr<SearchBoxView> search_box_view) {
+  ASSERT_TRUE(search_box_view);
+
+  // Use a fixed placeholder text instead of the one picked randomly to
+  // avoid the test flakiness.
+  search_box_view->UseFixedPlaceholderTextForTest();
+
+  // Hide the search box cursor to avoid the flakiness due to the blinking.
+  views::TextfieldTestApi(search_box_view->search_box())
+      .SetCursorLayerOpacity(0.f);
+}
+
 }  // namespace
 
 class AppListViewPixelRTLTest
@@ -75,18 +88,6 @@
   void ShowAppList() {
     AppListTestHelper* test_helper = GetAppListTestHelper();
     test_helper->ShowAppList();
-
-    // 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.
-  void HideCursor() {
-    views::TextfieldTestApi(
-        GetAppListTestHelper()->GetBubbleSearchBoxView()->search_box())
-        .SetCursorLayerOpacity(0.f);
   }
 
   void SetUpAnswerCardResult(SearchModel::SearchResults* results,
@@ -158,7 +159,7 @@
   // OnSearchResultContainerResultsChanged will schedule show animations().
   base::RunLoop().RunUntilIdle();
 
-  HideCursor();
+  UseFixedPlaceholderTextAndHideCursor(test_helper->GetSearchBoxView());
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "bubble_launcher_answer_card_search_results",
       /*revision_number=*/0, GetAppListTestHelper()->GetBubbleView(),
@@ -180,7 +181,7 @@
   // OnSearchResultContainerResultsChanged will schedule show animations().
   base::RunLoop().RunUntilIdle();
 
-  HideCursor();
+  UseFixedPlaceholderTextAndHideCursor(test_helper->GetSearchBoxView());
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "bubble_launcher_url_search_results",
       /*revision_number=*/0, GetAppListTestHelper()->GetBubbleView(),
@@ -193,7 +194,8 @@
       /*num_apps=*/2, AppListTestHelper::IconColorType::kAlternativeColor,
       /*set_name=*/true);
   ShowAppList();
-  HideCursor();
+  UseFixedPlaceholderTextAndHideCursor(
+      GetAppListTestHelper()->GetSearchBoxView());
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "bubble_launcher_basics",
       /*revision_number=*/0, GetAppListTestHelper()->GetBubbleView(),
@@ -206,7 +208,8 @@
       /*num_apps=*/22, AppListTestHelper::IconColorType::kAlternativeColor,
       /*set_name=*/true);
   ShowAppList();
-  HideCursor();
+  UseFixedPlaceholderTextAndHideCursor(
+      GetAppListTestHelper()->GetSearchBoxView());
   views::ScrollView* scroll_view =
       GetAppListTestHelper()->GetBubbleAppsPage()->scroll_view();
 
@@ -271,8 +274,9 @@
   // Wait re-layout for adding IPH view.
   base::RunLoop().RunUntilIdle();
 
+  UseFixedPlaceholderTextAndHideCursor(search_box_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
-      "launcher_search_iph", /*revision_number=*/0, search_box_view));
+      "launcher_search_iph", /*revision_number=*/1, search_box_view));
 }
 
 class AppListViewTabletPixelTest
diff --git a/ash/app_list/views/launcher_search_iph_view.cc b/ash/app_list/views/launcher_search_iph_view.cc
index e393f455..1b2f4576 100644
--- a/ash/app_list/views/launcher_search_iph_view.cc
+++ b/ash/app_list/views/launcher_search_iph_view.cc
@@ -25,6 +25,7 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
@@ -39,7 +40,6 @@
 
 constexpr char16_t kTitleTextPlaceholder[] = u"Title text";
 constexpr char16_t kDescriptionTextPlaceholder[] = u"Description text";
-constexpr char16_t kSeparator[] = u" ";
 constexpr char16_t kLinkTextPlaceholder[] = u"Link text";
 
 constexpr char16_t kChipOneQueryPlaceholder[] = u"Weather";
@@ -111,29 +111,33 @@
   title_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_TO_HEAD);
   title_label->SetEnabledColorId(kColorAshTextColorPrimary);
 
-  std::u16string text(kDescriptionTextPlaceholder);
-  text.append(kSeparator);
-  text_range_ = gfx::Range(0, text.size());
+  // Add a description text and a link into another container to have a
+  // different between-child-spacing.
+  raw_ptr<views::BoxLayoutView> description_container =
+      text_container->AddChildView(std::make_unique<views::BoxLayoutView>());
+  description_container->SetOrientation(
+      views::BoxLayout::Orientation::kVertical);
+  description_container->SetCrossAxisAlignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
 
-  std::u16string link(kLinkTextPlaceholder);
-  link_range_ = gfx::Range(text.size(), text.size() + link.size());
+  raw_ptr<views::Label> description_label = description_container->AddChildView(
+      std::make_unique<views::Label>(kDescriptionTextPlaceholder));
+  description_label->SetEnabledColorId(kColorAshTextColorPrimary);
 
-  description_label_ =
-      text_container->AddChildView(std::make_unique<views::StyledLabel>());
-  description_label_->SetID(kDescriptionLabel);
-  description_label_->SetDefaultTextStyle(
-      views::style::TextStyle::STYLE_PRIMARY);
-  description_label_->SetHorizontalAlignment(
-      gfx::HorizontalAlignment::ALIGN_LEFT);
-  description_label_->SetText(text + link);
+  link_label_ = description_container->AddChildView(
+      std::make_unique<views::Link>(kLinkTextPlaceholder));
+  link_label_->SetID(ViewId::kDescriptionLinkLabel);
+  link_label_->SetCallback(base::BindRepeating(
+      &LauncherSearchIphView::OnLinkClicked, weak_ptr_factory_.GetWeakPtr()));
 
   raw_ptr<const TypographyProvider> typography_provider =
       TypographyProvider::Get();
   DCHECK(typography_provider) << "TypographyProvider must not be null";
   if (typography_provider) {
     typography_provider->StyleLabel(TypographyToken::kCrosTitle1, *title_label);
-    description_label_->SetLineHeight(
-        typography_provider->ResolveLineHeight(TypographyToken::kCrosBody2));
+    typography_provider->StyleLabel(TypographyToken::kCrosBody2,
+                                    *description_label);
+    typography_provider->StyleLabel(TypographyToken::kCrosBody2, *link_label_);
   }
 
   raw_ptr<views::BoxLayoutView> actions_container =
@@ -186,49 +190,13 @@
 void LauncherSearchIphView::OnThemeChanged() {
   views::View::OnThemeChanged();
 
-  description_label_->ClearStyleRanges();
-
-  // Apply no style if ranges are not initialized for a fail-safe behavior.
-  DCHECK(!text_range_.is_empty());
-  DCHECK(!link_range_.is_empty());
-  if (text_range_.is_empty() || link_range_.is_empty()) {
-    return;
-  }
-
-  views::StyledLabel::RangeStyleInfo text_range_style_info;
-  text_range_style_info.text_style = views::style::TextStyle::STYLE_PRIMARY;
-
-  views::StyledLabel::RangeStyleInfo link_range_style_info;
-  link_range_style_info.text_style = views::style::TextStyle::STYLE_LINK;
-  link_range_style_info.callback = base::BindRepeating(
-      &LauncherSearchIphView::OnLinkClicked, weak_ptr_factory_.GetWeakPtr());
-  raw_ptr<ui::ColorProvider> color_provider =
-      description_label_->GetColorProvider();
+  raw_ptr<ui::ColorProvider> color_provider = link_label_->GetColorProvider();
   DCHECK(color_provider) << "ColorProvider must not be null";
   if (color_provider) {
-    // `TextStyle::STYLE_LINK` has a different color from
-    // `ui::ColorIds::kColorLabelForeground`.
-    link_range_style_info.override_color =
-        description_label_->GetColorProvider()->GetColor(
-            ui::ColorIds::kColorLabelForeground);
+    // TODO(b/277380563): `views::Link::SetEnabledColorId` does not work.
+    link_label_->SetEnabledColor(
+        color_provider->GetColor(ui::ColorIds::kColorLabelForeground));
   }
-
-  raw_ptr<const TypographyProvider> typography_provider =
-      TypographyProvider::Get();
-  DCHECK(typography_provider) << "TypographyProvider must not be null";
-  if (typography_provider) {
-    // `TextStyle::STYLE_PRIMARY` is different from
-    // `TypographyToken::kCrosBody2`.
-    text_range_style_info.custom_font =
-        typography_provider->ResolveTypographyToken(
-            TypographyToken::kCrosBody2);
-
-    // TODO(b/272370530): We cannot set `custom_font` for the link range.
-    // Consult with UX about this limitation.
-  }
-
-  description_label_->AddStyleRange(text_range_, text_range_style_info);
-  description_label_->AddStyleRange(link_range_, link_range_style_info);
 }
 
 void LauncherSearchIphView::RunLauncherSearchQuery(
diff --git a/ash/app_list/views/launcher_search_iph_view.h b/ash/app_list/views/launcher_search_iph_view.h
index ab7c466..af0c00a 100644
--- a/ash/app_list/views/launcher_search_iph_view.h
+++ b/ash/app_list/views/launcher_search_iph_view.h
@@ -10,6 +10,7 @@
 
 #include "ash/public/cpp/app_list/app_list_client.h"
 #include "ui/events/event.h"
+#include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/view.h"
 
@@ -38,7 +39,7 @@
 
   enum ViewId {
     kSelf = 1,
-    kDescriptionLabel,
+    kDescriptionLinkLabel,
     kAssistant,
     // Do not put a new id after `kChipStart`. Numbers after `kChipStart`
     // will be used for chips.
@@ -62,11 +63,7 @@
 
   std::unique_ptr<ScopedIphSession> scoped_iph_session_;
   raw_ptr<Delegate> delegate_;
-  raw_ptr<views::StyledLabel> description_label_;
-
-  // Ranges for text part and link part in the text of `description_label_`.
-  gfx::Range text_range_;
-  gfx::Range link_range_;
+  raw_ptr<views::Link> link_label_;
 
   base::WeakPtrFactory<LauncherSearchIphView> weak_ptr_factory_{this};
 };
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index f21230c..e7f56ac 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2581,6 +2581,9 @@
       <message name="IDS_ASH_HOTSPOT_NOTIFICATION_TURN_ON_BUTTON" desc="The label used as the button to turn on hotspot.">
         Turn on hotspot
       </message>
+      <message name="IDS_ASH_HOTSPOT_NOTIFICATION_WIFI_TURN_ON_BUTTON" desc="The label for button to turn WiFi on in the hotspot notification.">
+        Turn on WiFi instead
+      </message>
       <message name="IDS_ASH_HOTSPOT_WIFI_TURNED_OFF_MESSAGE" desc="Message displayed in the system notification shown when WiFi is turned off upon enabling hotspot.">
         We've turned off the WiFi to start using Hotspot through Mobile data. This may incur data costs.
       </message>
@@ -4017,7 +4020,7 @@
         <ph name="MODIFIER_ONE">$1<ex>shift</ex></ph><ph name="MODIFIER_TWO">$2<ex>alt</ex></ph><ph name="KEY_ONE">$3<ex>i</ex></ph> then <ph name="KEY_TWO">$4<ex>tab</ex></ph> or <ph name="KEY_THREE">$5<ex>right</ex></ph>
       </message>
       <message name="IDS_AMBIENT_ACCELERATOR_HIGHTLIGHT_PREVIOUS_ITEM_ON_SHELF" translateable="false" desc="Action for accelerator action - Highlight the previous item on your shelf.">
-        <ph name="MODIFIER_ONE">$1<ex>shift</ex></ph><ph name="MODIFIER_TWO">$2<ex>alt</ex></ph><ph name="KEY_ONE">$3<ex>i</ex></ph> then <ph name="KEY_TWO">$4<ex>tab</ex></ph><ph name="MODIFIER_THREE">$5<ex>shift</ex></ph> or <ph name="KEY_THREE">$6<ex>left</ex></ph>
+        <ph name="MODIFIER_ONE">$1<ex>shift</ex></ph><ph name="MODIFIER_TWO">$2<ex>alt</ex></ph><ph name="KEY_ONE">$3<ex>i</ex></ph> then <ph name="MODIFIER_THREE">$5<ex>shift</ex></ph><ph name="KEY_TWO">$4<ex>tab</ex></ph> or <ph name="KEY_THREE">$6<ex>left</ex></ph>
       </message>
       <message name="IDS_AMBIENT_ACCELERATOR_OPEN_HIGHLIGHTED_ITEM_ON_SHELF" translateable="false" desc="Action for accelerator action - Open the highlighted item on your shelf.">
         <ph name="MODIFIER_ONE">$1<ex>shift</ex></ph><ph name="MODIFIER_TWO">$2<ex>alt</ex></ph><ph name="KEY_ONE">$3<ex>i</ex></ph> then <ph name="KEY_TWO">$4<ex>space</ex></ph> or <ph name="KEY_THREE">$5<ex>enter</ex></ph>
diff --git a/ash/ash_strings_grd/IDS_ASH_HOTSPOT_NOTIFICATION_WIFI_TURN_ON_BUTTON.png.sha1 b/ash/ash_strings_grd/IDS_ASH_HOTSPOT_NOTIFICATION_WIFI_TURN_ON_BUTTON.png.sha1
new file mode 100644
index 0000000..a124e07
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_HOTSPOT_NOTIFICATION_WIFI_TURN_ON_BUTTON.png.sha1
@@ -0,0 +1 @@
+c702fbf5ed2c91d143dc713285e3c1440a8b9eda
\ No newline at end of file
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
index 7bf6eac..2f54285 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
@@ -187,30 +187,30 @@
   ASSERT_EQ(initial_close_button_bounds.x(),
             initial_size_button_bounds.right());
 
-  // Maximize/restore button is hidden in tablet mode and the other buttons
+  // Size and minimize buttons are hidden in tablet mode and the other buttons
   // should shift accordingly.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-  container.UpdateCaptionButtonState(false /*=animate*/);
+  container.UpdateCaptionButtonState(/*animate=*/false);
   test.EndAnimations();
   // Parent needs to layout in response to size change.
   views::test::RunScheduledLayout(&container);
 
-  EXPECT_TRUE(test.minimize_button()->GetVisible());
+  EXPECT_TRUE(extra_button->GetVisible());
+  EXPECT_FALSE(test.minimize_button()->GetVisible());
   EXPECT_FALSE(test.size_button()->GetVisible());
   EXPECT_TRUE(test.close_button()->GetVisible());
   gfx::Rect extra_button_bounds = extra_button->bounds();
-  gfx::Rect minimize_button_bounds = test.minimize_button()->bounds();
   gfx::Rect close_button_bounds = test.close_button()->bounds();
-  EXPECT_EQ(minimize_button_bounds.x(), extra_button_bounds.right());
-  EXPECT_EQ(close_button_bounds.x(), minimize_button_bounds.right());
+  EXPECT_EQ(close_button_bounds.x(), extra_button_bounds.right());
   EXPECT_EQ(initial_close_button_bounds.size(), close_button_bounds.size());
-  EXPECT_EQ(
-      initial_container_bounds.width() - initial_size_button_bounds.width(),
-      container.GetPreferredSize().width());
+  EXPECT_EQ(initial_container_bounds.width() -
+                initial_size_button_bounds.width() -
+                initial_minimize_button_bounds.width(),
+            container.GetPreferredSize().width());
 
   // Button positions should be the same when leaving tablet mode.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
-  container.UpdateCaptionButtonState(false /*=animate*/);
+  container.UpdateCaptionButtonState(/*animate=*/false);
   // Calling code needs to layout in response to size change.
   views::test::RunScheduledLayout(&container);
   test.EndAnimations();
diff --git a/ash/frame/caption_buttons/frame_size_button_unittest.cc b/ash/frame/caption_buttons/frame_size_button_unittest.cc
index fb5d4c2..c04dc3d 100644
--- a/ash/frame/caption_buttons/frame_size_button_unittest.cc
+++ b/ash/frame/caption_buttons/frame_size_button_unittest.cc
@@ -944,7 +944,7 @@
 // Tests that moving the mouse outside the menu will close the menu, if opened
 // via hovering on the frame size button.
 TEST_F(MultitaskMenuTest, MoveMouseOutsideMenu) {
-  chromeos::MultitaskMenuView::SetSkipMouseOutDelayFoTesting(true);
+  chromeos::MultitaskMenuView::SetSkipMouseOutDelayForTesting(true);
 
   // Simulate opening the menu by moving the mouse to the frame size button and
   // opening the menu.
diff --git a/ash/glanceables/tasks/glanceables_tasks_client.h b/ash/glanceables/tasks/glanceables_tasks_client.h
index 38f2a9e0..50a2df2 100644
--- a/ash/glanceables/tasks/glanceables_tasks_client.h
+++ b/ash/glanceables/tasks/glanceables_tasks_client.h
@@ -34,6 +34,12 @@
   virtual void GetTasks(const std::string& task_list_id,
                         GetTasksCallback callback) = 0;
 
+  // Marks the specified task in the specified task list as completed. Only root
+  // tasks can be marked as completed (all subtasks will be marked as completed
+  // automatically by the API).
+  virtual void MarkAsCompleted(const std::string& task_list_id,
+                               const std::string& task_id) = 0;
+
  protected:
   virtual ~GlanceablesTasksClient() = default;
 };
diff --git a/ash/public/cpp/input_device_settings_controller.h b/ash/public/cpp/input_device_settings_controller.h
index c5fe4c2..cb6d897 100644
--- a/ash/public/cpp/input_device_settings_controller.h
+++ b/ash/public/cpp/input_device_settings_controller.h
@@ -39,6 +39,8 @@
     virtual void OnMouseConnected(const mojom::Mouse& mouse) {}
     virtual void OnMouseDisconnected(const mojom::Mouse& mouse) {}
     virtual void OnMouseSettingsUpdated(const mojom::Mouse& mouse) {}
+    virtual void OnMousePoliciesUpdated(
+        const mojom::MousePolicies& keyboard_policies) {}
 
     virtual void OnPointingStickConnected(
         const mojom::PointingStick& pointing_stick) {}
@@ -76,6 +78,9 @@
   // Returns the current set of enterprise policies which control keyboard
   // settings.
   virtual const mojom::KeyboardPolicies& GetKeyboardPolicies() = 0;
+  // Returns the current set of enterprise policies which control mouse
+  // settings.
+  virtual const mojom::MousePolicies& GetMousePolicies() = 0;
 
   // Configure the settings for keyboard of `id` with the provided
   // `settings`.
diff --git a/ash/public/cpp/test/mock_input_device_settings_controller.h b/ash/public/cpp/test/mock_input_device_settings_controller.h
index 65ee117..a8b7d63 100644
--- a/ash/public/cpp/test/mock_input_device_settings_controller.h
+++ b/ash/public/cpp/test/mock_input_device_settings_controller.h
@@ -56,6 +56,7 @@
               GetKeyboardPolicies,
               (),
               (override));
+  MOCK_METHOD(const mojom::MousePolicies&, GetMousePolicies, (), (override));
   MOCK_METHOD(void,
               SetKeyboardSettings,
               (DeviceId id, mojom::KeyboardSettingsPtr settings),
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.cc b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
index 467413b7..8123b4f1 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
@@ -273,8 +273,8 @@
     DispatchTouchpadSettingsChanged(id);
   }
   for (const auto& [id, mouse] : mice_) {
-    mouse_pref_handler_->InitializeMouseSettings(active_pref_service_,
-                                                 mouse.get());
+    mouse_pref_handler_->InitializeMouseSettings(
+        active_pref_service_, policy_handler_->mouse_policies(), mouse.get());
     if (active_pref_service_) {
       metrics_manager_->RecordMouseInitialMetrics(*mouse);
     }
@@ -341,7 +341,15 @@
 }
 
 void InputDeviceSettingsControllerImpl::OnMousePoliciesChanged() {
-  // TODO(dpad): Reinitialize mouse settings and inform observers.
+  for (const auto& [id, mouse] : mice_) {
+    mouse_pref_handler_->InitializeMouseSettings(
+        active_pref_service_, policy_handler_->mouse_policies(), mouse.get());
+    DispatchMouseSettingsChanged(id);
+  }
+
+  for (auto& observer : observers_) {
+    observer.OnMousePoliciesUpdated(policy_handler_->mouse_policies());
+  }
 }
 
 const mojom::KeyboardPolicies&
@@ -350,6 +358,12 @@
   return policy_handler_->keyboard_policies();
 }
 
+const mojom::MousePolicies&
+InputDeviceSettingsControllerImpl::GetMousePolicies() {
+  CHECK(policy_handler_);
+  return policy_handler_->mouse_policies();
+}
+
 const mojom::KeyboardSettings*
 InputDeviceSettingsControllerImpl::GetKeyboardSettings(DeviceId id) {
   auto iter = keyboards_.find(id);
@@ -516,7 +530,8 @@
 
   auto& found_mouse = *found_mouse_iter->second;
   found_mouse.settings = settings.Clone();
-  mouse_pref_handler_->UpdateMouseSettings(active_pref_service_, found_mouse);
+  mouse_pref_handler_->UpdateMouseSettings(
+      active_pref_service_, policy_handler_->mouse_policies(), found_mouse);
   DispatchMouseSettingsChanged(id);
   // Check the list of mice to see if any have the same |device_key|.
   // If so, their settings need to also be updated.
@@ -722,8 +737,9 @@
     std::vector<DeviceId> mouse_ids_to_remove) {
   for (const auto& mouse : mice_to_add) {
     auto mojom_mouse = BuildMojomMouse(mouse);
-    mouse_pref_handler_->InitializeMouseSettings(active_pref_service_,
-                                                 mojom_mouse.get());
+    mouse_pref_handler_->InitializeMouseSettings(
+        active_pref_service_, policy_handler_->mouse_policies(),
+        mojom_mouse.get());
     if (active_pref_service_) {
       metrics_manager_->RecordMouseInitialMetrics(*mojom_mouse);
     }
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.h b/ash/system/input_device_settings/input_device_settings_controller_impl.h
index 622d53c..0af8bc7 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.h
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.h
@@ -61,6 +61,7 @@
   const mojom::PointingStickSettings* GetPointingStickSettings(
       DeviceId id) override;
   const mojom::KeyboardPolicies& GetKeyboardPolicies() override;
+  const mojom::MousePolicies& GetMousePolicies() override;
   void SetKeyboardSettings(DeviceId id,
                            mojom::KeyboardSettingsPtr settings) override;
   void SetTouchpadSettings(DeviceId id,
diff --git a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler.h b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler.h
index 28199dc..32722e19 100644
--- a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler.h
+++ b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler.h
@@ -20,12 +20,15 @@
   // Initializes device settings in prefs and update the `settings` member of
   // the `mojom::Mouse` object.
   // If `pref_service` is null, sets the `settings` member to default settings.
-  virtual void InitializeMouseSettings(PrefService* pref_service,
-                                       mojom::Mouse* mouse) = 0;
+  virtual void InitializeMouseSettings(
+      PrefService* pref_service,
+      const mojom::MousePolicies& mouse_policies,
+      mojom::Mouse* mouse) = 0;
 
   // Updates device settings stored in prefs to match the values in
   // `mouse.settings`.
   virtual void UpdateMouseSettings(PrefService* pref_service,
+                                   const mojom::MousePolicies& mouse_policies,
                                    const mojom::Mouse& mouse) = 0;
 };
 
diff --git a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.cc b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.cc
index 9afb8dd..2a9f12dc 100644
--- a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.cc
+++ b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.cc
@@ -6,6 +6,7 @@
 
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/mojom/input_device_settings.mojom-forward.h"
+#include "ash/public/mojom/input_device_settings.mojom-shared.h"
 #include "ash/public/mojom/input_device_settings.mojom.h"
 #include "ash/shell.h"
 #include "ash/system/input_device_settings/input_device_settings_defaults.h"
@@ -30,9 +31,20 @@
   bool scroll_sensitivity = false;
 };
 
-mojom::MouseSettingsPtr GetDefaultMouseSettings() {
+bool GetDefaultSwapRightValue(const mojom::MousePolicies& mouse_policies) {
+  if (mouse_policies.swap_right_policy &&
+      mouse_policies.swap_right_policy->policy_status ==
+          mojom::PolicyStatus::kRecommended) {
+    return mouse_policies.swap_right_policy->value;
+  }
+
+  return kDefaultSwapRight;
+}
+
+mojom::MouseSettingsPtr GetDefaultMouseSettings(
+    const mojom::MousePolicies& mouse_policies) {
   mojom::MouseSettingsPtr settings = mojom::MouseSettings::New();
-  settings->swap_right = kDefaultSwapRight;
+  settings->swap_right = GetDefaultSwapRightValue(mouse_policies);
   settings->sensitivity = kDefaultSensitivity;
   settings->reverse_scrolling = kDefaultReverseScrolling;
   settings->acceleration_enabled = kDefaultAccelerationEnabled;
@@ -45,6 +57,7 @@
 // to be used as settings for new mouses.
 mojom::MouseSettingsPtr GetMouseSettingsFromPrefs(
     PrefService* prefs,
+    const mojom::MousePolicies& mouse_policies,
     ForceMouseSettingPersistence& force_persistence) {
   mojom::MouseSettingsPtr settings = mojom::MouseSettings::New();
 
@@ -52,7 +65,7 @@
       prefs->GetUserPrefValue(prefs::kPrimaryMouseButtonRight);
   settings->swap_right = swap_right_preference
                              ? swap_right_preference->GetBool()
-                             : kDefaultSwapRight;
+                             : GetDefaultSwapRightValue(mouse_policies);
   force_persistence.swap_right = swap_right_preference != nullptr;
 
   const auto* sensitivity_preference =
@@ -98,11 +111,13 @@
 }
 
 mojom::MouseSettingsPtr RetrieveMouseSettings(
+    const mojom::MousePolicies& mouse_policies,
     const mojom::Mouse& mouse,
     const base::Value::Dict& settings_dict) {
   mojom::MouseSettingsPtr settings = mojom::MouseSettings::New();
-  settings->swap_right = settings_dict.FindBool(prefs::kMouseSettingSwapRight)
-                             .value_or(kDefaultSwapRight);
+  settings->swap_right =
+      settings_dict.FindBool(prefs::kMouseSettingSwapRight)
+          .value_or(GetDefaultSwapRightValue(mouse_policies));
   settings->sensitivity = settings_dict.FindInt(prefs::kMouseSettingSensitivity)
                               .value_or(kDefaultSensitivity);
   settings->reverse_scrolling =
@@ -122,6 +137,7 @@
 
 void UpdateMouseSettingsImpl(
     PrefService* pref_service,
+    const mojom::MousePolicies& mouse_policies,
     const mojom::Mouse& mouse,
     const ForceMouseSettingPersistence& force_persistence) {
   DCHECK(mouse.settings);
@@ -134,9 +150,10 @@
   // Populate `settings_dict` with all settings in `settings`.
   base::Value::Dict settings_dict;
 
-  if (ShouldPersistSetting(prefs::kMouseSettingSwapRight, settings.swap_right,
-                           kDefaultSwapRight, force_persistence.swap_right,
-                           existing_settings_dict)) {
+  if (ShouldPersistSetting(
+          mouse_policies.swap_right_policy, prefs::kMouseSettingSwapRight,
+          settings.swap_right, GetDefaultSwapRightValue(mouse_policies),
+          force_persistence.swap_right, existing_settings_dict)) {
     settings_dict.Set(prefs::kMouseSettingSwapRight, settings.swap_right);
   }
 
@@ -197,10 +214,12 @@
 MousePrefHandlerImpl::MousePrefHandlerImpl() = default;
 MousePrefHandlerImpl::~MousePrefHandlerImpl() = default;
 
-void MousePrefHandlerImpl::InitializeMouseSettings(PrefService* pref_service,
-                                                   mojom::Mouse* mouse) {
+void MousePrefHandlerImpl::InitializeMouseSettings(
+    PrefService* pref_service,
+    const mojom::MousePolicies& mouse_policies,
+    mojom::Mouse* mouse) {
   if (!pref_service) {
-    mouse->settings = GetDefaultMouseSettings();
+    mouse->settings = GetDefaultMouseSettings(mouse_policies);
     return;
   }
 
@@ -210,23 +229,34 @@
   ForceMouseSettingPersistence force_persistence;
 
   if (settings_dict) {
-    mouse->settings = RetrieveMouseSettings(*mouse, *settings_dict);
+    mouse->settings =
+        RetrieveMouseSettings(mouse_policies, *mouse, *settings_dict);
   } else if (Shell::Get()->input_device_tracker()->WasDevicePreviouslyConnected(
                  InputDeviceTracker::InputDeviceCategory::kMouse,
                  mouse->device_key)) {
-    mouse->settings =
-        GetMouseSettingsFromPrefs(pref_service, force_persistence);
+    mouse->settings = GetMouseSettingsFromPrefs(pref_service, mouse_policies,
+                                                force_persistence);
   } else {
-    mouse->settings = GetDefaultMouseSettings();
+    mouse->settings = GetDefaultMouseSettings(mouse_policies);
   }
   DCHECK(mouse->settings);
 
-  UpdateMouseSettingsImpl(pref_service, *mouse, force_persistence);
+  UpdateMouseSettingsImpl(pref_service, mouse_policies, *mouse,
+                          force_persistence);
+
+  if (mouse_policies.swap_right_policy &&
+      mouse_policies.swap_right_policy->policy_status ==
+          mojom::PolicyStatus::kManaged) {
+    mouse->settings->swap_right = mouse_policies.swap_right_policy->value;
+  }
 }
 
-void MousePrefHandlerImpl::UpdateMouseSettings(PrefService* pref_service,
-                                               const mojom::Mouse& mouse) {
-  UpdateMouseSettingsImpl(pref_service, mouse, /*force_persistence=*/{});
+void MousePrefHandlerImpl::UpdateMouseSettings(
+    PrefService* pref_service,
+    const mojom::MousePolicies& mouse_policies,
+    const mojom::Mouse& mouse) {
+  UpdateMouseSettingsImpl(pref_service, mouse_policies, mouse,
+                          /*force_persistence=*/{});
 }
 
 }  // namespace ash
diff --git a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h
index bc6dca5..db0ad60 100644
--- a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h
+++ b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h
@@ -22,8 +22,10 @@
 
   // MousePrefHandler:
   void InitializeMouseSettings(PrefService* pref_service,
+                               const mojom::MousePolicies& mouse_policies,
                                mojom::Mouse* mouse) override;
   void UpdateMouseSettings(PrefService* pref_service,
+                           const mojom::MousePolicies& mouse_policies,
                            const mojom::Mouse& mouse) override;
 };
 
diff --git a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_unittest.cc b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_unittest.cc
index 30dff821..43f3c03 100644
--- a/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_unittest.cc
+++ b/ash/system/input_device_settings/pref_handlers/mouse_pref_handler_unittest.cc
@@ -174,7 +174,8 @@
     mouse->settings = settings.Clone();
     mouse->device_key = device_key;
 
-    pref_handler_->UpdateMouseSettings(pref_service_.get(), *mouse);
+    pref_handler_->UpdateMouseSettings(pref_service_.get(),
+                                       /*mouse_policies=*/{}, *mouse);
   }
 
   mojom::MouseSettingsPtr CallInitializeMouseSettings(
@@ -182,7 +183,8 @@
     mojom::MousePtr mouse = mojom::Mouse::New();
     mouse->device_key = device_key;
 
-    pref_handler_->InitializeMouseSettings(pref_service_.get(), mouse.get());
+    pref_handler_->InitializeMouseSettings(pref_service_.get(),
+                                           /*mouse_policies=*/{}, mouse.get());
     return std::move(mouse->settings);
   }
 
@@ -305,7 +307,8 @@
 TEST_F(MousePrefHandlerTest, DefaultSettingsWhenPrefServiceNull) {
   mojom::Mouse mouse;
   mouse.device_key = kMouseKey1;
-  pref_handler_->InitializeMouseSettings(nullptr, &mouse);
+  pref_handler_->InitializeMouseSettings(nullptr, /*mouse_policies=*/{},
+                                         &mouse);
   EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);
 }
 
diff --git a/ash/system/network/hotspot_notifier.cc b/ash/system/network/hotspot_notifier.cc
index f41cf55..ddaf36a3 100644
--- a/ash/system/network/hotspot_notifier.cc
+++ b/ash/system/network/hotspot_notifier.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/network/hotspot_notifier.h"
 #include "ash/public/cpp/hotspot_config_service.h"
+#include "ash/public/cpp/network_config_service.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -35,18 +36,32 @@
       remote_cros_hotspot_config_.BindNewPipeAndPassReceiver());
   remote_cros_hotspot_config_->ObserveEnabledStateChanges(
       hotspot_enabled_state_observer_receiver_.BindNewPipeAndPassRemote());
+
+  GetNetworkConfigService(
+      remote_cros_network_config_.BindNewPipeAndPassReceiver());
+  remote_cros_network_config_->AddObserver(
+      cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
 }
 
 HotspotNotifier::~HotspotNotifier() = default;
 
 void HotspotNotifier::OnHotspotTurnedOn(bool wifi_turned_off) {
   if (wifi_turned_off) {
+    scoped_refptr<message_center::NotificationDelegate> delegate =
+        base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+            base::BindRepeating(&HotspotNotifier::EnableWiFiHandler,
+                                weak_ptr_factory_.GetWeakPtr(),
+                                kWiFiTurnedOffNotificationId));
+
+    std::vector<message_center::ButtonInfo> notification_actions;
     std::unique_ptr<message_center::Notification> notification =
         CreateNotification(IDS_ASH_HOTSPOT_ON_TITLE,
                            IDS_ASH_HOTSPOT_WIFI_TURNED_OFF_MESSAGE,
-                           kWiFiTurnedOffNotificationId,
-                           /*delegate=*/nullptr);
-
+                           kWiFiTurnedOffNotificationId, delegate);
+    notification_actions.push_back(
+        message_center::ButtonInfo(l10n_util::GetStringUTF16(
+            IDS_ASH_HOTSPOT_NOTIFICATION_WIFI_TURN_ON_BUTTON)));
+    notification->set_buttons(notification_actions);
     message_center::MessageCenter* message_center =
         message_center::MessageCenter::Get();
     message_center->RemoveNotification(kWiFiTurnedOffNotificationId,
@@ -139,6 +154,40 @@
   }
 }
 
+void HotspotNotifier::EnableWiFiHandler(const char* notification_id,
+                                        absl::optional<int> button_index) {
+  if (!button_index) {
+    return;
+  }
+
+  if (button_index.value() == 0) {
+    remote_cros_network_config_->SetNetworkTypeEnabledState(
+        chromeos::network_config::mojom::NetworkType::kWiFi, /*enabled=*/true,
+        base::DoNothing());
+  }
+}
+
+void HotspotNotifier::OnDeviceStateListChanged() {
+  remote_cros_network_config_->GetDeviceStateList(base::BindOnce(
+      &HotspotNotifier::OnGetDeviceStateList, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void HotspotNotifier::OnGetDeviceStateList(
+    std::vector<chromeos::network_config::mojom::DeviceStatePropertiesPtr>
+        devices) {
+  for (auto& device : devices) {
+    if (device->type == chromeos::network_config::mojom::NetworkType::kWiFi &&
+        device->device_state ==
+            chromeos::network_config::mojom::DeviceStateType::kEnabled) {
+      message_center::MessageCenter* message_center =
+          message_center::MessageCenter::Get();
+      message_center->RemoveNotification(kWiFiTurnedOffNotificationId,
+                                         /*by_user=*/false);
+      return;
+    }
+  }
+}
+
 std::unique_ptr<message_center::Notification>
 HotspotNotifier::CreateNotification(
     const int title_id,
diff --git a/ash/system/network/hotspot_notifier.h b/ash/system/network/hotspot_notifier.h
index 6455ac8..b594142 100644
--- a/ash/system/network/hotspot_notifier.h
+++ b/ash/system/network/hotspot_notifier.h
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "ui/message_center/message_center.h"
@@ -24,7 +25,8 @@
 //  -  4. In activity
 //  - Hotspot is turned on and has 'n' active connections
 class ASH_EXPORT HotspotNotifier
-    : public hotspot_config::mojom::HotspotEnabledStateObserver {
+    : public hotspot_config::mojom::HotspotEnabledStateObserver,
+      public chromeos::network_config::CrosNetworkConfigObserver {
  public:
   HotspotNotifier();
   HotspotNotifier(const HotspotNotifier&) = delete;
@@ -45,6 +47,13 @@
   void OnHotspotTurnedOff(
       hotspot_config::mojom::DisableReason disable_reason) override;
 
+  // CrosNetworkConfigObserver:
+  void OnDeviceStateListChanged() override;
+
+  void OnGetDeviceStateList(
+      std::vector<chromeos::network_config::mojom::DeviceStatePropertiesPtr>
+          devices);
+
   std::unique_ptr<message_center::Notification> CreateNotification(
       const int title_id,
       const int message_id,
@@ -53,9 +62,15 @@
 
   void EnableHotspotHandler(const char* notification_id,
                             absl::optional<int> index);
+  void EnableWiFiHandler(const char* notification_id,
+                         absl::optional<int> index);
 
   mojo::Remote<hotspot_config::mojom::CrosHotspotConfig>
       remote_cros_hotspot_config_;
+  mojo::Remote<chromeos::network_config::mojom::CrosNetworkConfig>
+      remote_cros_network_config_;
+  mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver>
+      cros_network_config_observer_receiver_{this};
   mojo::Receiver<hotspot_config::mojom::HotspotEnabledStateObserver>
       hotspot_enabled_state_observer_receiver_{this};
 
diff --git a/ash/system/network/hotspot_notifier_unittest.cc b/ash/system/network/hotspot_notifier_unittest.cc
index 61dc525e..835823e 100644
--- a/ash/system/network/hotspot_notifier_unittest.cc
+++ b/ash/system/network/hotspot_notifier_unittest.cc
@@ -11,8 +11,12 @@
 #include "chromeos/ash/components/network/hotspot_state_handler.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
+#include "chromeos/ash/components/network/network_state_test_helper.h"
+#include "chromeos/ash/services/hotspot_config/cros_hotspot_config.h"
 #include "chromeos/ash/services/hotspot_config/public/cpp/cros_hotspot_config_test_helper.h"
+#include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
 #include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 
 namespace ash {
 
@@ -116,6 +120,8 @@
       cros_network_config_test_helper_;
   std::unique_ptr<hotspot_config::CrosHotspotConfigTestHelper>
       cros_hotspot_config_test_helper_;
+  std::unique_ptr<HotspotNotifier> hotspot_notifier_;
+  mojo::Remote<hotspot_config::mojom::CrosHotspotConfig> cros_hotspot_config_;
 };
 
 TEST_F(HotspotNotifierTest, WiFiTurnedOff) {
@@ -129,6 +135,13 @@
   EnableHotspot();
   EXPECT_TRUE(message_center::MessageCenter::Get()->FindVisibleNotificationById(
       HotspotNotifier::kWiFiTurnedOffNotificationId));
+
+  message_center::MessageCenter::Get()->ClickOnNotificationButton(
+      HotspotNotifier::kWiFiTurnedOffNotificationId, 0);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(
+      message_center::MessageCenter::Get()->FindVisibleNotificationById(
+          HotspotNotifier::kWiFiTurnedOffNotificationId));
 }
 
 TEST_F(HotspotNotifierTest, AdminRestricted) {
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 7795ec0..20db300 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -587,9 +587,7 @@
                                expected_test_data, mojo_observer.config());
 }
 
-// TODO(crbug.com/1430899): Re-enable this test.
-TEST_F(AcceleratorConfigurationProviderTest,
-       DISABLED_TopRowKeyAcceleratorRemapped) {
+TEST_F(AcceleratorConfigurationProviderTest, TopRowKeyAcceleratorRemapped) {
   // Add a fake layout2 keyboard.
   ui::InputDevice fake_keyboard(
       /*id=*/1, /*type=*/ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH,
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.cc
index f2a657c..f691f52b 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.cc
@@ -199,8 +199,8 @@
                {TextAcceleratorPart(ui::EF_SHIFT_DOWN),
                 TextAcceleratorPart(ui::EF_ALT_DOWN),
                 TextAcceleratorPart(ui::KeyboardCode::VKEY_I),
-                TextAcceleratorPart(ui::KeyboardCode::VKEY_TAB),
                 TextAcceleratorPart(ui::EF_SHIFT_DOWN),
+                TextAcceleratorPart(ui::KeyboardCode::VKEY_TAB),
                 TextAcceleratorPart(ui::KeyboardCode::VKEY_LEFT)})},
           {NonConfigurableActions::kAmbientOpenHighlightedItemOnShelf,
            NonConfigurableAcceleratorDetails(
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
index d5292ae..69690a02 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.html
@@ -3,7 +3,7 @@
     align-items: center;
     display: grid;
     grid-auto-rows: minmax(52px, auto);
-    grid-template-columns: 286px 1fr;
+    grid-template-columns: minmax(min-content, 286px) 50%;
   }
 
   accelerator-view,
diff --git a/ash/webui/shortcut_customization_ui/resources/js/input_key.html b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
index 3bdec9f..6e1689d 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/input_key.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
@@ -71,6 +71,9 @@
     background-color: var(--cros-bg-color);
   }
 
+  :host([highlighted][key-state='modifier-selected']) #key-icon {
+    --iron-icon-fill-color: var(--cros-button-label-color-primary);
+  }
 
   #key-icon {
     --iron-icon-height: 16px;
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html b/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
index aa65809..bff49735 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcut_customization_app.html
@@ -31,7 +31,6 @@
   }
 
   #navigationPanel::part(navigation-item) {
-    color: var(--cros-text-color-primary);
     padding-inline-start: 28px;
   }
 
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcut_utils.ts b/ash/webui/shortcut_customization_ui/resources/js/shortcut_utils.ts
index efca5082..d824dc58 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcut_utils.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcut_utils.ts
@@ -152,7 +152,7 @@
  * Sort the modifiers in the order of ctrl, alt, shift, meta.
  */
 export const getSortedModifiers = (modifierStrings: string[]): string[] => {
-  const sortOrder = ['ctrl', 'alt', 'shift', 'meta'];
+  const sortOrder = ['meta', 'ctrl', 'alt', 'shift'];
   if (modifierStrings.length <= 1) {
     return modifierStrings;
   }
diff --git a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
index 436a3b0b..95327776 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
@@ -19,6 +19,7 @@
     align-items: center;
     display: flex;
     flex-wrap: wrap;
+    row-gap: 8px;
   }
 
   :host([narrow]) .parts-container {
diff --git a/ash/wm/float/float_controller.cc b/ash/wm/float/float_controller.cc
index 0f0b36f..e2461d9 100644
--- a/ash/wm/float/float_controller.cc
+++ b/ash/wm/float/float_controller.cc
@@ -116,6 +116,40 @@
   floated_window->Show();
 }
 
+FloatController::MagnetismCorner GetMagnetismCornerForBounds(
+    const gfx::Rect& bounds_in_screen) {
+  const gfx::Point display_bounds_center =
+      display::Screen::GetScreen()
+          ->GetDisplayMatching(bounds_in_screen)
+          .bounds()
+          .CenterPoint();
+  const int display_bounds_center_x = display_bounds_center.x();
+  const int display_bounds_center_y = display_bounds_center.y();
+
+  // Check which corner to magnetize to based on which quadrant of the display
+  // the centerpoint of the window was on touch released. Not that the
+  // centerpoint may be offscreen.
+  const gfx::Point center_point = bounds_in_screen.CenterPoint();
+  const int center_point_x = center_point.x();
+  const int center_point_y = center_point.y();
+  FloatController::MagnetismCorner magnetism_corner;
+  if (center_point_x < display_bounds_center_x &&
+      center_point_y < display_bounds_center_y) {
+    magnetism_corner = FloatController::MagnetismCorner::kTopLeft;
+  } else if (center_point_x >= display_bounds_center_x &&
+             center_point_y < display_bounds_center_y) {
+    magnetism_corner = FloatController::MagnetismCorner::kTopRight;
+  } else if (center_point_x < display_bounds_center_x &&
+             center_point_y >= display_bounds_center_y) {
+    magnetism_corner = FloatController::MagnetismCorner::kBottomLeft;
+  } else {
+    CHECK_GE(center_point_x, display_bounds_center_x);
+    CHECK_GE(center_point_y, display_bounds_center_y);
+    magnetism_corner = FloatController::MagnetismCorner::kBottomRight;
+  }
+  return magnetism_corner;
+}
+
 class FloatLayoutManager : public WmDefaultLayoutManager {
  public:
   FloatLayoutManager() = default;
@@ -481,41 +515,8 @@
 void FloatController::OnDragCompletedForTablet(aura::Window* floated_window) {
   auto* floated_window_info = MaybeGetFloatedWindowInfo(floated_window);
   DCHECK(floated_window_info);
-
-  // Use the display bounds since the user may drag on to the shelf or spoken
-  // feedback bar.
-  const gfx::Point display_bounds_center =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(floated_window->GetRootWindow())
-          .bounds()
-          .CenterPoint();
-  const int display_bounds_center_x = display_bounds_center.x();
-  const int display_bounds_center_y = display_bounds_center.y();
-
-  // Check which corner to magnetize to based on which quadrant of the display
-  // the centerpoint of the window was on touch released. Not that the
-  // centerpoint may be offscreen.
-  const gfx::Point float_window_center =
-      floated_window->GetBoundsInScreen().CenterPoint();
-  const int float_window_center_x = float_window_center.x();
-  const int float_window_center_y = float_window_center.y();
-  MagnetismCorner magnetism_corner;
-  if (float_window_center_x < display_bounds_center_x &&
-      float_window_center_y < display_bounds_center_y) {
-    magnetism_corner = MagnetismCorner::kTopLeft;
-  } else if (float_window_center_x >= display_bounds_center_x &&
-             float_window_center_y < display_bounds_center_y) {
-    magnetism_corner = MagnetismCorner::kTopRight;
-  } else if (float_window_center_x < display_bounds_center_x &&
-             float_window_center_y >= display_bounds_center_y) {
-    magnetism_corner = MagnetismCorner::kBottomLeft;
-  } else {
-    DCHECK_GE(float_window_center_x, display_bounds_center_x);
-    DCHECK_GE(float_window_center_y, display_bounds_center_y);
-    magnetism_corner = MagnetismCorner::kBottomRight;
-  }
-
-  floated_window_info->set_magnetism_corner(magnetism_corner);
+  floated_window_info->set_magnetism_corner(
+      GetMagnetismCornerForBounds(floated_window->GetBoundsInScreen()));
   UpdateWindowBoundsForTablet(floated_window,
                               WindowState::BoundsChangeAnimationType::kAnimate);
 }
@@ -657,17 +658,16 @@
   // it. Note that the bounds update has to happen after tablet mode has started
   // as opposed to while it is still starting, since some windows change their
   // minimum size, which tablet float bounds depend on.
-  std::vector<aura::Window*> windows_need_reset;
   for (auto& [window, info] : floated_window_info_map_) {
     if (chromeos::wm::CanFloatWindow(window)) {
+      info->set_magnetism_corner(
+          GetMagnetismCornerForBounds(window->GetBoundsInScreen()));
       UpdateWindowBoundsForTablet(
           window, WindowState::BoundsChangeAnimationType::kCrossFade);
     } else {
-      windows_need_reset.push_back(window);
+      ResetFloatedWindow(window);
     }
   }
-  for (auto* window : windows_need_reset)
-    ResetFloatedWindow(window);
 }
 
 void FloatController::OnTabletModeEnding() {
@@ -785,8 +785,8 @@
     return;
   }
 
-  // Update magnetism so that the float window is roughly in the same location
-  // as it was when it was snapped.
+  // Update magnetism so that the float window is roughly in the same
+  // location as it was when it was snapped.
   const bool left_or_top =
       old_state_type == chromeos::WindowStateType::kPrimarySnapped;
   const bool landscape = IsCurrentScreenOrientationLandscape();
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index c9cf47c..d133c0c 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -999,10 +999,8 @@
   // Drags `window` so that it magnetizes to `corner`.
   void MagnetizeWindow(aura::Window* window,
                        FloatController::MagnetismCorner corner) {
-    // Drag to a point outside of `kScreenEdgeInsetForSnap` from the edge of the
-    // screen to avoid snapping.
-    gfx::Rect area = WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
-    area.Inset(kScreenEdgeInsetForSnap + 5);
+    const gfx::Rect area =
+        WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
 
     gfx::Point end;
     switch (corner) {
@@ -1133,6 +1131,17 @@
   EXPECT_TRUE(WindowState::Get(window2.get())->IsFloated());
 }
 
+TEST_F(TabletWindowFloatTest, ClamshellToTabletMagnetism) {
+  auto window = CreateFloatedWindow();
+  window->SetBounds(gfx::Rect(300, 300));
+
+  // Verify that on entering tablet mode, since our window's origin was 0,0, the
+  // window is magnetized to the top left.
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  EXPECT_TRUE(WindowState::Get(window.get())->IsFloated());
+  CheckMagnetized(window.get(), FloatController::MagnetismCorner::kTopLeft);
+}
+
 // Tests that the expected windows are animating duration a tablet <-> clamshell
 // transition.
 TEST_F(TabletWindowFloatTest, TabletClamshellTransitionAnimation) {
@@ -1450,41 +1459,6 @@
   CheckMagnetized(window.get(), FloatController::MagnetismCorner::kBottomLeft);
 }
 
-// Tests that if a floating window is dragged to the edges, it will snap.
-TEST_F(TabletWindowFloatTest, DraggingSnapping) {
-  // Use a set display size so we can drag to specific spots.
-  UpdateDisplay("1600x1000");
-
-  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-
-  std::unique_ptr<aura::Window> window = CreateFloatedWindow();
-
-  auto* split_view_controller =
-      SplitViewController::Get(Shell::GetPrimaryRootWindow());
-  ASSERT_FALSE(split_view_controller->primary_window());
-  ASSERT_FALSE(split_view_controller->secondary_window());
-
-  // Move finger towards the right edge. Test that on release, it snaps right.
-  // Don't scroll too fast or we will tuck the window.
-  chromeos::HeaderView* header_view = GetHeaderView(window.get());
-  auto* event_generator = GetEventGenerator();
-  event_generator->GestureScrollSequence(
-      header_view->GetBoundsInScreen().CenterPoint(), gfx::Point(1580, 500),
-      base::Milliseconds(500), /*steps=*/50);
-  EXPECT_EQ(split_view_controller->secondary_window(), window.get());
-  ASSERT_TRUE(WindowState::Get(window.get())->IsSnapped());
-
-  // Float the window so we can drag it again.
-  PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
-
-  // Move finger towards the left edge. Test that on release, it snaps left.
-  event_generator->GestureScrollSequence(
-      header_view->GetBoundsInScreen().CenterPoint(), gfx::Point(20, 500),
-      base::Milliseconds(500), /*steps=*/50);
-  EXPECT_EQ(split_view_controller->primary_window(), window.get());
-}
-
 TEST_F(TabletWindowFloatTest, UntuckWindowOnExitTabletMode) {
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   // The window is magnetized to the bottom right by default.
@@ -1978,6 +1952,8 @@
 
   // Counter should not increment as nudge was not shown.
   EXPECT_EQ(3, nudge_counter.nudge_count());
+
+  TabletModeTuckEducation::SetOverrideClockForTesting(nullptr);
 }
 
 using TabletWindowFloatSplitviewTest = TabletWindowFloatTest;
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.cc b/ash/wm/float/tablet_mode_float_window_resizer.cc
index f794921..bf426679 100644
--- a/ash/wm/float/tablet_mode_float_window_resizer.cc
+++ b/ash/wm/float/tablet_mode_float_window_resizer.cc
@@ -7,29 +7,17 @@
 #include "ash/shell.h"
 #include "ash/wm/drag_details.h"
 #include "ash/wm/float/float_controller.h"
-#include "ash/wm/splitview/split_view_drag_indicators.h"
-#include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/window_state.h"
 #include "chromeos/ui/wm/features.h"
 #include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
-#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
 
 namespace {
 
-// TODO(crbug.com/1351562): The following constants are the same the drag window
-// from shelf feature. They need to be changed for this feature, or moved to a
-// shared location.
-
-// If the window drag starts within `kDistanceFromEdge` from screen edge, it
-// will get snapped if the drag ends in the snap region, no matter how far the
-// window has been dragged.
-constexpr int kDistanceFromEdge = 8;
-
 // The minimum distance that will be considered as a drag event.
-constexpr float kMinimumDragDistance = 5.f;
+const float kMinimumDragDistance = 5.f;
 
 // Minimum fling velocity required to tuck the window.
 const int kFlingToTuckVelocityThresholdSquared = 800 * 800;
@@ -39,23 +27,12 @@
 TabletModeFloatWindowResizer::TabletModeFloatWindowResizer(
     WindowState* window_state)
     : WindowResizer(window_state),
-      split_view_drag_indicators_(std::make_unique<SplitViewDragIndicators>(
-          window_state->window()->GetRootWindow())),
       last_location_in_parent_(details().initial_location_in_parent) {
-  DCHECK(chromeos::wm::features::IsWindowLayoutMenuEnabled());
-  // TODO(sophiewen): Remove this once the untuck window widget is implemented.
-  Shell::Get()->float_controller()->MaybeUntuckFloatedWindowForTablet(
-      GetTarget());
-  split_view_drag_indicators_->SetDraggedWindow(GetTarget());
+  CHECK(chromeos::wm::features::IsWindowLayoutMenuEnabled());
   window_state->OnDragStarted(HTCAPTION);
 }
 
 TabletModeFloatWindowResizer::~TabletModeFloatWindowResizer() {
-  // `SplitViewDragIndicators` has a default delayed animation. Setting the
-  // state to no drag instantly hides the indicators so we don't see this
-  // delayed hide.
-  split_view_drag_indicators_->SetWindowDraggingState(
-      SplitViewDragIndicators::WindowDraggingState::kNoDrag);
   window_state_->DeleteDragDetails();
 }
 
@@ -67,33 +44,15 @@
   gfx::Rect bounds = CalculateBoundsForDrag(location_in_parent);
   if (bounds != window->bounds())
     SetBoundsDuringResize(bounds);
-
-  // Update `snap_position_` and the snap drag indicators.
-  gfx::PointF location_in_screen = location_in_parent;
-  gfx::PointF initial_location_in_screen = details().initial_location_in_parent;
-  wm::ConvertPointToScreen(window->parent(), &location_in_screen);
-  wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen);
-
-  snap_position_ = GetSnapPosition(
-      window->GetRootWindow(), window, gfx::ToRoundedPoint(location_in_screen),
-      gfx::ToRoundedPoint(initial_location_in_screen),
-      /*snap_distance_from_edge=*/kDistanceFromEdge,
-      /*minimum_drag_distance=*/kMinDragDistance,
-      /*horizontal_edge_inset=*/kScreenEdgeInsetForSnap,
-      /*vertical_edge_inset=*/kScreenEdgeInsetForSnap);
-  split_view_drag_indicators_->SetWindowDraggingState(
-      SplitViewDragIndicators::ComputeWindowDraggingState(
-          /*is_dragging=*/true,
-          SplitViewDragIndicators::WindowDraggingState::kFromFloat,
-          snap_position_));
 }
 
 void TabletModeFloatWindowResizer::CompleteDrag() {
   // We can reach this state if the user hits a state changing accelerator
   // mid-drag.
   aura::Window* float_window = GetTarget();
-  if (!WindowState::Get(float_window)->IsFloated())
+  if (!WindowState::Get(float_window)->IsFloated()) {
     return;
+  }
 
   // Revert the drag if the window hasn't moved enough. This will prevent
   // accidental magnetisms.
@@ -104,19 +63,6 @@
     return;
   }
 
-  if (snap_position_ != SplitViewController::SnapPosition::kNone) {
-    // Let `SplitViewController` handle windows that should be snapped.
-    auto* split_view_controller =
-        SplitViewController::Get(Shell::GetPrimaryRootWindow());
-    DCHECK(split_view_controller->CanSnapWindow(float_window));
-    gfx::PointF location_in_screen = last_location_in_parent_;
-    wm::ConvertPointToScreen(float_window->parent(), &location_in_screen);
-    split_view_controller->OnWindowDragEnded(
-        float_window, snap_position_, gfx::ToRoundedPoint(location_in_screen));
-    window_state_->OnCompleteDrag(last_location_in_parent_);
-    return;
-  }
-
   // `FloatController` will magnetize windows to one of the corners if it
   // remains in float state and not tucked.
   Shell::Get()->float_controller()->OnDragCompletedForTablet(float_window);
@@ -129,7 +75,8 @@
 }
 
 void TabletModeFloatWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {
-  DCHECK(window_state_->IsFloated());
+  CHECK(window_state_->IsFloated());
+
   const ui::GestureEventDetails& details = event->details();
   float velocity_x = 0.f, velocity_y = 0.f;
   if (event->type() == ui::ET_SCROLL_FLING_START) {
@@ -144,7 +91,7 @@
       return;
     }
   } else {
-    DCHECK_EQ(ui::ET_GESTURE_SWIPE, event->type());
+    CHECK_EQ(ui::ET_GESTURE_SWIPE, event->type());
 
     // Use any negative value if `swipe_left()` or `swipe_up()`, otherwise use
     // any positive value.
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.h b/ash/wm/float/tablet_mode_float_window_resizer.h
index e85099a0..7fc9b10 100644
--- a/ash/wm/float/tablet_mode_float_window_resizer.h
+++ b/ash/wm/float/tablet_mode_float_window_resizer.h
@@ -10,23 +10,7 @@
 
 namespace ash {
 
-class SplitViewDragIndicators;
-class WindowState;
-
-// TODO(crbug.com/1351562): The following constants are the same the drag window
-// from shelf feature. They need to be changed for this feature, or moved to a
-// shared location.
-
-// A window has to be dragged toward the direction of the edge of the screen for
-// a minimum of `kMinDragDistance` to a point within `kScreenEdgeInsetForSnap`
-// of the edge of the screen, or dragged inside `kDistanceEdge` from edge to be
-// snapped.
-constexpr int kMinDragDistance = 96;
-constexpr int kScreenEdgeInsetForSnap = 48;
-
 // WindowResizer implementation for floated windows in tablet mode.
-// TODO(crbug.com/1338715): This resizer adds the most basic dragging. It needs
-// to stick to edges and magnetize to corners on release.
 class TabletModeFloatWindowResizer : public WindowResizer {
  public:
   explicit TabletModeFloatWindowResizer(WindowState* window_state);
@@ -42,17 +26,8 @@
   void FlingOrSwipe(ui::GestureEvent* event) override;
 
  private:
-  // Responsible for showing an indication of whether the dragged window will be
-  // snapped on drag complete.
-  std::unique_ptr<SplitViewDragIndicators> split_view_drag_indicators_;
-
   // The location in parent passed to `Drag()`.
   gfx::PointF last_location_in_parent_;
-
-  // The snap position computed in `Drag()`. It is then cached for use in
-  // `CompleteDrag()`.
-  SplitViewController::SnapPosition snap_position_ =
-      SplitViewController::SnapPosition::kNone;
 };
 
 }  // namespace ash
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 4f198c2..4b6c9698 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230410.1.1
+12.20230410.2.1
diff --git a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
index e1af1ad..48692ba 100644
--- a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
@@ -57,9 +57,8 @@
 import org.chromium.ui.resources.ResourceManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
@@ -253,7 +252,7 @@
         LayoutTab sourceLayoutTab = createLayoutTab(
                 mTabModelSelector.getCurrentTabId(), mTabModelSelector.isIncognitoSelected());
         sourceLayoutTab.setDecorationAlpha(0);
-
+        updateCacheVisibleIds(Collections.singletonList(mTabModelSelector.getCurrentTabId()));
         mLayoutTabs = new LayoutTab[] {sourceLayoutTab};
 
         boolean quick;
@@ -375,7 +374,9 @@
         sourceLayoutTab.setDecorationAlpha(0);
 
         List<LayoutTab> layoutTabs = new ArrayList<>();
+        List<Integer> tabIds = new ArrayList<>();
         layoutTabs.add(sourceLayoutTab);
+        tabIds.add(sourceLayoutTab.getId());
 
         if (sourceTabId != mTabModelSelector.getCurrentTabId()) {
             // Keep the original tab in mLayoutTabs to unblock thumbnail taking at the end of
@@ -385,10 +386,10 @@
             originalTab.setScale(0);
             originalTab.setDecorationAlpha(0);
             layoutTabs.add(originalTab);
+            tabIds.add(originalTab.getId());
         }
         mLayoutTabs = layoutTabs.toArray(new LayoutTab[0]);
-
-        updateCacheVisibleIds(new LinkedList<>(Arrays.asList(sourceTabId)));
+        updateCacheVisibleIds(tabIds);
 
         mIsAnimatingHide = true;
         if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
index d0c8e90..85e3412 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
@@ -53,9 +53,8 @@
 import org.chromium.ui.resources.ResourceManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
@@ -222,7 +221,7 @@
             LayoutTab sourceLayoutTab = createLayoutTab(
                     mTabModelSelector.getCurrentTabId(), mTabModelSelector.isIncognitoSelected());
             sourceLayoutTab.setDecorationAlpha(0);
-
+            updateCacheVisibleIds(Collections.singletonList(mTabModelSelector.getCurrentTabId()));
             mLayoutTabs = new LayoutTab[] {sourceLayoutTab};
 
             boolean quick = mGridTabListDelegate.prepareTabSwitcherView();
@@ -300,7 +299,9 @@
             sourceLayoutTab.setDecorationAlpha(0);
 
             List<LayoutTab> layoutTabs = new ArrayList<>();
+            List<Integer> tabIds = new ArrayList<>();
             layoutTabs.add(sourceLayoutTab);
+            tabIds.add(sourceLayoutTab.getId());
 
             if (sourceTabId != mTabModelSelector.getCurrentTabId()) {
                 // Keep the original tab in mLayoutTabs to unblock thumbnail taking at the end of
@@ -310,10 +311,10 @@
                 originalTab.setScale(0);
                 originalTab.setDecorationAlpha(0);
                 layoutTabs.add(originalTab);
+                tabIds.add(originalTab.getId());
             }
             mLayoutTabs = layoutTabs.toArray(new LayoutTab[0]);
-
-            updateCacheVisibleIds(new LinkedList<>(Arrays.asList(sourceTabId)));
+            updateCacheVisibleIds(tabIds);
 
             mIsAnimatingHide = true;
             if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())) {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index 0aad038..8e0aafd 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -824,8 +824,8 @@
         mSectionHeaderModel.set(SectionHeaderListProperties.IS_LOGO_KEY,
                 !isGoogleSearchEngine && isSignedIn && suggestionsVisible);
         ViewVisibility indicatorState;
-        if (!isSignedIn || !suggestionsVisible) {
-            // Gone when not signed in or feed off to align text to far left.
+        if (!isTabMode) {
+            // Gone when the following/for you tab switcher header is not shown
             indicatorState = ViewVisibility.GONE;
         } else if (!isGoogleSearchEngine) {
             // Visible when Google is not the search engine (show logo).
diff --git a/chrome/android/java/res/layout/custom_tabs_toolbar.xml b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
index 48396bf..3c73d38 100644
--- a/chrome/android/java/res/layout/custom_tabs_toolbar.xml
+++ b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
@@ -72,17 +72,17 @@
                 android:layout_gravity="bottom"
                 style="@style/TextAppearance.TextSmall.Primary" />
         </view>
+        <ViewStub
+            android:id="@+id/maximize_button_stub"
+            android:inflatedId="@+id/custom_tabs_sidepanel_maximize"
+            android:layout_width="@dimen/location_bar_action_icon_width"
+            android:layout_height="match_parent"
+            android:layout="@layout/custom_tabs_sidepanel_maximize"
+            android:layout_gravity="center_vertical|end"
+            android:gravity="center_vertical"
+            android:visibility="gone"
+            app:tint="@color/default_icon_color_tint_list" />
     </FrameLayout>
-    <ViewStub
-        android:id="@+id/maximize_button_stub"
-        android:inflatedId="@+id/custom_tabs_sidepanel_maximize"
-        android:layout_width="@dimen/location_bar_icon_width"
-        android:layout_height="wrap_content"
-        android:layout="@layout/custom_tabs_sidepanel_maximize"
-        android:layout_gravity="center_vertical|end"
-        android:gravity="center_vertical"
-        android:visibility="gone"
-        app:tint="@color/default_icon_color_tint_list" />
     <LinearLayout
         android:id="@+id/action_buttons"
         android:layout_width="wrap_content"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WebContentsFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/WebContentsFactory.java
index 2fc2771d..4476d80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WebContentsFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WebContentsFactory.java
@@ -34,16 +34,10 @@
      * A factory method to build a {@link WebContents} object.
      * @param profile         The profile with which the {@link WebContents} should be built.
      * @param initiallyHidden Whether or not the {@link WebContents} should be initially hidden.
+     * @param initializeRenderer Whether or not the {@link WebContents} should initialize renderer.
      * @return                A newly created {@link WebContents} object.
      */
-    // TODO(https://crbug.com/1099138): Remove static for unit-testability.
-    public static WebContents createWebContents(Profile profile, boolean initiallyHidden) {
-        return WebContentsFactoryJni.get().createWebContents(
-                profile, initiallyHidden, false, new WebContentsCreationException());
-    }
-
-    // TODO(https://crbug.com/1033955): Remove after check discard error is fixed.
-    private static WebContents createWebContents(
+    public static WebContents createWebContents(
             Profile profile, boolean initiallyHidden, boolean initializeRenderer) {
         return WebContentsFactoryJni.get().createWebContents(
                 profile, initiallyHidden, initializeRenderer, new WebContentsCreationException());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
index c7182f0b..5b417a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
@@ -145,7 +145,7 @@
 
     // This implements the CreatorWebContents interface.
     public WebContents createWebContents() {
-        return WebContentsFactory.createWebContents(mProfile, true);
+        return WebContentsFactory.createWebContents(mProfile, true, false);
     }
 
     // This implements the CreatorOpenTab interface.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/video_tutorials/VideoPlayerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/video_tutorials/VideoPlayerActivity.java
index 2b8b7fb..5151e9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/video_tutorials/VideoPlayerActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/video_tutorials/VideoPlayerActivity.java
@@ -68,7 +68,7 @@
 
     private Pair<WebContents, ContentView> createWebContents() {
         Profile profile = Profile.getLastUsedRegularProfile();
-        WebContents webContents = WebContentsFactory.createWebContents(profile, false);
+        WebContents webContents = WebContentsFactory.createWebContents(profile, false, false);
         ContentView contentView =
                 ContentView.createContentView(this, null /* eventOffsetHandler */, webContents);
         webContents.initialize(VersionConstants.PRODUCT_VERSION,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index 86f923e2..2410ed6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -349,7 +349,7 @@
 
         Profile profile = IncognitoUtils.getProfileFromWindowAndroid(mWindowAndroid, mIsIncognito);
         // Creates an initially hidden WebContents which gets shown when the panel is opened.
-        mWebContents = WebContentsFactory.createWebContents(profile, true);
+        mWebContents = WebContentsFactory.createWebContents(profile, true, false);
 
         ContentView cv = ContentView.createContentView(
                 mActivity, null /* eventOffsetHandler */, mWebContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
index 906d9f72..492d785 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
@@ -89,7 +89,7 @@
 
     private void createWebContents() {
         final Profile profile = Profile.getLastUsedRegularProfile();
-        mWebContents = WebContentsFactory.createWebContents(profile, false);
+        mWebContents = WebContentsFactory.createWebContents(profile, false, false);
         mWebContentView = ContentView.createContentView(mContext, null, mWebContents);
         final ViewAndroidDelegate delegate =
                 ViewAndroidDelegate.createBasicDelegate(mWebContentView);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index a353de0..c205d61d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -230,7 +230,7 @@
         assert mWebContents == null;
 
         // Creates an initially hidden WebContents which gets shown when the panel is opened.
-        mWebContents = WebContentsFactory.createWebContents(profile, true);
+        mWebContents = WebContentsFactory.createWebContents(profile, true, false);
 
         mContentView = ContentView.createContentView(
                 mContext, null /* eventOffsetHandler */, mWebContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 1f06039c..9fbed2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -227,8 +227,6 @@
 
         // Add the view at the beginning of the child list.
         mCustomActionButtons.addView(button, 0);
-
-        updateMaximizeButtonPosition();
     }
 
     @Override
@@ -237,7 +235,6 @@
                 mCustomActionButtons.getChildCount() - 1 - index);
         assert button != null;
         updateCustomActionButtonVisuals(button, drawable, description);
-        updateMaximizeButtonPosition();
     }
 
     /**
@@ -274,18 +271,41 @@
             boolean maximizedOnInit, MaximizeButtonCallback callback) {
         if (!ChromeFeatureList.sCctResizableSideSheet.isEnabled()) return;
         var maximizeButton = (ImageButton) findViewById(R.id.custom_tabs_sidepanel_maximize);
-        boolean buttonExists = maximizeButton != null;
-        if (buttonExists) {
-            maximizeButton.setVisibility(View.VISIBLE);
-        } else {
+        if (maximizeButton == null) {
             ViewStub maximizeButtonStub = findViewById(R.id.maximize_button_stub);
             maximizeButtonStub.inflate();
             maximizeButton = (ImageButton) findViewById(R.id.custom_tabs_sidepanel_maximize);
         }
+        // The visibility will get updated after the location bar completes its layout.
+        maximizeButton.setVisibility(View.GONE);
         setMaximizeButtonDrawable(maximizedOnInit);
         maximizeButton.setOnClickListener((v) -> setMaximizeButtonDrawable(callback.onClick()));
     }
 
+    private void setMaximizeButtonVisibility() {
+        var maximizeButton = (ImageButton) findViewById(R.id.custom_tabs_sidepanel_maximize);
+        if (maximizeButton == null) return;
+
+        // Find the title/url width threshold that turns the maximize button visible.
+        int containerWidthPx = mLocationBar.mTitleUrlContainer.getWidth();
+        int maximizeButtonWidthPx =
+                getResources().getDimensionPixelSize(R.dimen.location_bar_action_icon_width);
+        int titleUrlPaddingEndPx =
+                getResources().getDimensionPixelSize(R.dimen.toolbar_edge_padding);
+        if (containerWidthPx < maximizeButtonWidthPx * 2 - titleUrlPaddingEndPx) {
+            // We expect to see at least as much URL text as the width of the maximize button.
+            // Hide the button if we can't.
+            maximizeButton.setVisibility(View.GONE);
+        } else {
+            // Take some space from the title/url for maximization button.
+            var lpTitle = (ViewGroup.MarginLayoutParams) mLocationBar.mTitleBar.getLayoutParams();
+            var lpUrl = (ViewGroup.MarginLayoutParams) mLocationBar.mUrlBar.getLayoutParams();
+            lpTitle.rightMargin = maximizeButtonWidthPx;
+            lpUrl.rightMargin = maximizeButtonWidthPx;
+            maximizeButton.setVisibility(View.VISIBLE);
+        }
+    }
+
     private void setMaximizeButtonDrawable(boolean maximized) {
         @DrawableRes
         int drawableId = maximized ? R.drawable.ic_fullscreen_exit : R.drawable.ic_fullscreen_enter;
@@ -293,9 +313,7 @@
                                      : R.string.custom_tab_side_sheet_maximize;
         var maximizeButton = (ImageButton) findViewById(R.id.custom_tabs_sidepanel_maximize);
         var d = UiUtils.getTintedDrawable(getContext(), drawableId, mTint);
-        updateCustomActionButtonVisuals(maximizeButton, d, null);
-        maximizeButton.setImageDrawable(d);
-        maximizeButton.setContentDescription(getResources().getString(buttonDescId));
+        updateCustomActionButtonVisuals(maximizeButton, d, getResources().getString(buttonDescId));
     }
 
     /**
@@ -308,23 +326,6 @@
         maximizeButton.setVisibility(View.GONE);
     }
 
-    private void updateMaximizeButtonPosition() {
-        ImageButton maximizeButton =
-                (ImageButton) findViewById(R.id.custom_tabs_sidepanel_maximize);
-        if (maximizeButton != null) {
-            FrameLayout.LayoutParams lp =
-                    (FrameLayout.LayoutParams) maximizeButton.getLayoutParams();
-            View buttonAtEnd =
-                    mCloseButtonPosition == CLOSE_BUTTON_POSITION_END ? mCloseButton : mMenuButton;
-            int margin = buttonAtEnd.getVisibility() == View.GONE
-                    ? 0
-                    : getResources().getDimensionPixelSize(R.dimen.toolbar_button_width);
-            if (mCustomActionButtons != null) margin += mCustomActionButtons.getWidth();
-            lp.setMarginEnd(margin);
-            maximizeButton.setLayoutParams(lp);
-        }
-    }
-
     private void updateCustomActionButtonVisuals(
             ImageButton button, Drawable drawable, String description) {
         Resources resources = getResources();
@@ -660,8 +661,8 @@
         maybeSwapCloseAndMenuButtons();
         updateToolbarLayoutMargin();
         maybeAdjustButtonSpacingForCloseButtonPosition();
+        setMaximizeButtonVisibility();
 
-        updateMaximizeButtonPosition();
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -717,7 +718,6 @@
                 (ViewGroup.MarginLayoutParams) mCustomActionButtons.getLayoutParams();
         p.setMarginEnd(0);
         mCustomActionButtons.setLayoutParams(p);
-        updateMaximizeButtonPosition();
     }
 
     @Override
@@ -1340,5 +1340,10 @@
         void setAnimDelegateForTesting(CustomTabToolbarAnimationDelegate animDelegate) {
             mAnimDelegate = animDelegate;
         }
+
+        @VisibleForTesting
+        void setTitleUrlContainerForTesting(View titleUrlContainer) {
+            mTitleUrlContainer = titleUrlContainer;
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
index 7b539437..b6855dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
@@ -78,7 +78,7 @@
         if (activity == null) return null;
         Profile profile = IncognitoUtils.getProfileFromWindowAndroid(windowAndroid, isIncognito);
         mPaymentHandlerWebContents =
-                WebContentsFactory.createWebContents(profile, /*initiallyHidden=*/false);
+                WebContentsFactory.createWebContents(profile, /*initiallyHidden=*/false, false);
         PaymentHandlerNavigationThrottle.markPaymentHandlerWebContents(mPaymentHandlerWebContents);
         ContentView webContentView = ContentView.createContentView(
                 activity, null /* eventOffsetHandler */, mPaymentHandlerWebContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index c4ce3d9..a3a4124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -331,7 +331,7 @@
             }
         };
 
-        WebContents webContents = WebContentsFactory.createWebContents(profile, false);
+        WebContents webContents = WebContentsFactory.createWebContents(profile, false, false);
         mTab = new TabBuilder()
                        .setWindow(getWindowAndroid())
                        .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 6163007..1db9f456 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -565,7 +565,7 @@
             if (webContents == null) {
                 Profile profile =
                         IncognitoUtils.getProfileFromWindowAndroid(mWindowAndroid, isIncognito());
-                webContents = WebContentsFactory.createWebContents(profile, isHidden());
+                webContents = WebContentsFactory.createWebContents(profile, isHidden(), false);
             }
             initWebContents(webContents);
             loadUrl(mPendingLoadParams);
@@ -921,7 +921,8 @@
                 if (webContents == null) {
                     Profile profile = IncognitoUtils.getProfileFromWindowAndroid(
                             mWindowAndroid, isIncognito());
-                    webContents = WebContentsFactory.createWebContents(profile, initiallyHidden);
+                    webContents =
+                            WebContentsFactory.createWebContents(profile, initiallyHidden, false);
                 }
             }
 
@@ -1553,7 +1554,7 @@
                 // an error page instead of a blank page in that case (and the last loaded URL).
                 Profile profile =
                         IncognitoUtils.getProfileFromWindowAndroid(mWindowAndroid, isIncognito());
-                webContents = WebContentsFactory.createWebContents(profile, isHidden());
+                webContents = WebContentsFactory.createWebContents(profile, isHidden(), false);
                 for (TabObserver observer : mObservers) observer.onRestoreFailed(this);
                 restored = false;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 707470d..4c4b20d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -739,10 +739,12 @@
                 assert tab == mLocationBarModel.getTab();
                 mLocationBarModel.notifySecurityStateChanged();
                 mLocationBarModel.notifyUrlChanged();
+                onBackPressStateChanged();
             }
 
             @Override
             public void onTitleUpdated(Tab tab) {
+                onBackPressStateChanged();
                 mLocationBarModel.notifyTitleChanged();
             }
 
@@ -751,6 +753,7 @@
                 // Update the SSL security state as a result of this notification as it will
                 // sometimes be the only update we receive.
                 updateTabLoadingState(true);
+                onBackPressStateChanged();
 
                 // A URL update is a decent enough indicator that the toolbar widget is in
                 // a stable state to capture its bitmap for use in fullscreen.
@@ -788,12 +791,14 @@
                 maybeShowCursorInLocationBar();
                 // Paint preview status might have been changed. Update the omnibox chip.
                 mLocationBarModel.notifySecurityStateChanged();
+                onBackPressStateChanged();
             }
 
             @Override
             public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
                 if (!didStartLoad) return;
                 mLocationBarModel.notifyUrlChanged();
+                onBackPressStateChanged();
                 mLocationBarModel.notifySecurityStateChanged();
             }
 
@@ -2169,6 +2174,7 @@
                     tab != null && tab.isDestroyed());
             assert false : msg;
         }
+        onBackPressStateChanged();
         return ret ? BackPressResult.SUCCESS : BackPressResult.FAILURE;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index 5508005..0b6f86c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -135,7 +135,7 @@
                     activity.getTabModelSelector(), () -> activity.getLastUserInteractionTime());
             setSelectionController(new MockCSSelectionController(activity, this));
             WebContents webContents = WebContentsFactory.createWebContents(
-                    Profile.getLastUsedRegularProfile(), false);
+                    Profile.getLastUsedRegularProfile(), false, false);
             ContentView cv = ContentView.createContentView(
                     activity, null /* eventOffsetHandler */, webContents);
             webContents.initialize(null, ViewAndroidDelegate.createBasicDelegate(cv), null,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
index 7e9a0c0..102973a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,6 +1,5 @@
 gangwu@chromium.org
 
 # Secondary
-bttk@chromium.org
 donnd@chromium.org
 twellington@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
index 4ec16f1..ed57d29c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
@@ -296,7 +296,7 @@
                 () -> sActivityTestRule.getInfoBarContainer().addAnimationListener(removeListener));
         PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
             WebContents newContents = WebContentsFactory.createWebContents(
-                    Profile.getLastUsedRegularProfile(), false);
+                    Profile.getLastUsedRegularProfile(), false, false);
             TabTestUtils.swapWebContents(
                     sActivityTestRule.getActivity().getActivityTab(), newContents, false, false);
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
index a59ac2c..36382780 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@@ -85,7 +85,7 @@
         // Now create and destroy a different WebContents.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             WebContents webContents = WebContentsFactory.createWebContents(
-                    Profile.getLastUsedRegularProfile(), false);
+                    Profile.getLastUsedRegularProfile(), false, false);
             ChromeActivity activity = mActivityTestRule.getActivity();
 
             ContentView cv = ContentView.createContentView(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS
index eb14167..2f9b7c5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,7 +1,6 @@
 gangwu@chromium.org
 
 # Secondary
-bttk@chromium.org
 donnd@chromium.org
 twellington@chromium.org
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
index c8e3f5bf..4acd2335 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
-import static androidx.browser.customtabs.CustomTabsIntent.CLOSE_BUTTON_POSITION_END;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -28,7 +26,9 @@
 import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
 import android.widget.ImageButton;
 import android.widget.TextView;
 
@@ -379,18 +379,29 @@
         mToolbar.initSideSheetMaximizeButton(/*maximizedOnInit=*/false, () -> true);
         var maximizeButton =
                 (ImageButton) mToolbar.findViewById(R.id.custom_tabs_sidepanel_maximize);
+        assertEquals("Maximize button should be invisible upon start", View.GONE,
+                maximizeButton.getVisibility());
+
+        mToolbar.onFinishInflate();
+        View titleUrlContainer = Mockito.mock(View.class);
+        mLocationBar.setTitleUrlContainerForTesting(titleUrlContainer);
+        int maximizeButtonWidth = mActivity.getResources().getDimensionPixelSize(
+                R.dimen.location_bar_action_icon_width);
+        int titleUrlPaddingEnd =
+                mActivity.getResources().getDimensionPixelSize(R.dimen.toolbar_edge_padding);
+        int threshold = maximizeButtonWidth * 2 - titleUrlPaddingEnd;
+
+        when(titleUrlContainer.getWidth()).thenReturn(threshold + 10);
+        when(titleUrlContainer.getLayoutParams())
+                .thenReturn(new FrameLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        mToolbar.onMeasure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         assertEquals(
                 "Maximize button should be visible", View.VISIBLE, maximizeButton.getVisibility());
 
-        // Check margin from the right end.
-        var lp = (FrameLayout.LayoutParams) maximizeButton.getLayoutParams();
-        assertEquals(0, lp.rightMargin);
-        mToolbar.setCloseButtonPosition(CLOSE_BUTTON_POSITION_END);
-        mToolbar.onMenuButtonDisabled();
-        int closeButtonWidth =
-                mToolbar.getResources().getDimensionPixelSize(R.dimen.toolbar_button_width);
-        assertEquals("Maximize button should be next to close button.", closeButtonWidth,
-                lp.rightMargin);
+        when(titleUrlContainer.getWidth()).thenReturn(threshold - 10);
+        mToolbar.onMeasure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        assertEquals("Maximize button should be hidden", View.GONE, maximizeButton.getVisibility());
 
         mToolbar.removeSideSheetMaximizeButton();
         assertEquals("Maximize button should be hidden", View.GONE, maximizeButton.getVisibility());
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 904c4add..fa9fd16 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1208,6 +1208,15 @@
   <message name="IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_COLUMNS" desc="In the ChromeVox settings subpage, the label for a numberic input field where the user can choose the number of cells in each line of a grid (virtual braille display) that allows the user to control a simulation of a refreshable braille display (a physical hardware device) in the panel at the top of the screen.">
     Cells in each line:
   </message>
+  <message name="IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_LABEL" desc="A label for the dropdown that lets the user select the display style of the virtual display - interleaving braille and regular text (one on top of the other), or side by side where regular text is on the left and braille is on the right">
+    Display style
+  </message>
+  <message name="IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_INTERLEAVE" desc="A dropdown item that lets the user enable the display style of the virtual display that interleaves braille and regular text, one on top of the other.">
+    Interleave
+  </message>
+  <message name="IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_SIDE_BY_SIDE" desc="A dropdown item that lets the user enable the display style of the virtual display that is side by side, where regular text is on the left and braille is on the right.">
+    Side-by-side
+  </message>
   <message name="IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL" desc="Label for checkbox which enables the fullscreen magnifier">
     Full-screen magnifier
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_INTERLEAVE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_INTERLEAVE.png.sha1
new file mode 100644
index 0000000..3a1793a5
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_INTERLEAVE.png.sha1
@@ -0,0 +1 @@
+8a8292153b4cce6997ad0e89ce3cf1d5e008b3ed
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_LABEL.png.sha1
new file mode 100644
index 0000000..3a1793a5
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_LABEL.png.sha1
@@ -0,0 +1 @@
+8a8292153b4cce6997ad0e89ce3cf1d5e008b3ed
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_SIDE_BY_SIDE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_SIDE_BY_SIDE.png.sha1
new file mode 100644
index 0000000..3a1793a5
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_SIDE_BY_SIDE.png.sha1
@@ -0,0 +1 @@
+8a8292153b4cce6997ad0e89ce3cf1d5e008b3ed
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7efc0db0..36f9b6b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8059,6 +8059,13 @@
                                     kChromeRefresh2023Variations,
                                     "ChromeRefresh2023")},
 
+#if BUILDFLAG(IS_MAC)
+    {"cr2023-mac-font-smoothing",
+     flag_descriptions::kCr2023MacFontSmoothingName,
+     flag_descriptions::kCr2023MacFontSmoothingDescription, kOsMac,
+     FEATURE_VALUE_TYPE(features::kCr2023MacFontSmoothing)},
+#endif
+
     {"enable-first-party-sets", flag_descriptions::kEnableFirstPartySetsName,
      flag_descriptions::kEnableFirstPartySetsDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kFirstPartySets)},
diff --git a/chrome/browser/android/compositor/layer/content_layer.cc b/chrome/browser/android/compositor/layer/content_layer.cc
index 57edf426..992b17d 100644
--- a/chrome/browser/android/compositor/layer/content_layer.cc
+++ b/chrome/browser/android/compositor/layer/content_layer.cc
@@ -24,10 +24,11 @@
 static void SetOpacityOnLeaf(scoped_refptr<cc::slim::Layer> layer,
                              float alpha) {
   const auto& children = layer->children();
-  if (children.size() > 0) {
+  if (!children.empty()) {
     layer->SetOpacity(1.0f);
-    for (uint i = 0; i < children.size(); ++i)
-      SetOpacityOnLeaf(children[i], alpha);
+    for (const auto& child : children) {
+      SetOpacityOnLeaf(child, alpha);
+    }
   } else {
     layer->SetOpacity(alpha);
   }
@@ -35,24 +36,29 @@
 
 static cc::slim::Layer* GetDrawsContentLeaf(
     scoped_refptr<cc::slim::Layer> layer) {
-  if (!layer.get())
+  if (!layer.get()) {
     return nullptr;
+  }
 
   // If the subtree is hidden, then any layers in this tree will not be drawn.
-  if (layer->hide_layer_and_subtree())
+  if (layer->hide_layer_and_subtree()) {
     return nullptr;
+  }
 
-  if (layer->opacity() == 0.0f)
+  if (layer->opacity() == 0.0f) {
     return nullptr;
+  }
 
-  if (layer->draws_content())
+  if (layer->draws_content()) {
     return layer.get();
+  }
 
   const auto& children = layer->children();
-  for (unsigned i = 0; i < children.size(); i++) {
-    cc::slim::Layer* leaf = GetDrawsContentLeaf(children[i]);
-    if (leaf)
+  for (const auto& child : children) {
+    cc::slim::Layer* leaf = GetDrawsContentLeaf(child);
+    if (leaf) {
       return leaf;
+    }
   }
   return nullptr;
 }
@@ -67,16 +73,18 @@
                                  const gfx::Rect& clip) {
   scoped_refptr<cc::slim::Layer> live_layer =
       tab_content_manager_->GetLiveLayer(id);
-  if (live_layer)
+  if (live_layer) {
     live_layer->SetHideLayerAndSubtree(!can_use_live_layer);
+  }
   bool live_layer_draws = GetDrawsContentLeaf(live_layer);
 
   float content_opacity =
       should_override_content_alpha ? content_alpha_override : 1.0f;
   float static_opacity =
       should_override_content_alpha ? content_alpha_override : 1.0f;
-  if (live_layer_draws)
+  if (live_layer_draws) {
     static_opacity = static_to_view_blend;
+  }
 
   layer_->RemoveAllChildren();
 
@@ -89,14 +97,15 @@
   }
 
   if (static_opacity > 0) {
-    scoped_refptr<ThumbnailLayer> static_layer =
+    ThumbnailLayer* static_layer =
         tab_content_manager_->GetOrCreateStaticLayer(id, !live_layer_draws);
-    if (static_layer.get()) {
+    if (static_layer) {
       static_layer->layer()->SetIsDrawable(true);
-      if (should_clip)
+      if (should_clip) {
         static_layer->Clip(clip);
-      else
+      } else {
         static_layer->ClearClip();
+      }
       SetOpacityOnLeaf(static_layer->layer(), static_opacity);
 
       std::vector<cc::slim::Filter> filters;
@@ -115,13 +124,14 @@
   scoped_refptr<cc::slim::Layer> live_layer =
       tab_content_manager_->GetLiveLayer(id);
   cc::slim::Layer* leaf_that_draws = GetDrawsContentLeaf(live_layer);
-  if (leaf_that_draws)
+  if (leaf_that_draws) {
     size.SetToMax(leaf_that_draws->bounds());
+  }
 
-  scoped_refptr<ThumbnailLayer> static_layer =
-      tab_content_manager_->GetStaticLayer(id);
-  if (static_layer.get() && GetDrawsContentLeaf(static_layer->layer()))
+  ThumbnailLayer* static_layer = tab_content_manager_->GetStaticLayer(id);
+  if (static_layer && GetDrawsContentLeaf(static_layer->layer())) {
     size.SetToMax(static_layer->layer()->bounds());
+  }
 
   return size;
 }
@@ -134,7 +144,6 @@
     : layer_(cc::slim::Layer::Create()),
       tab_content_manager_(tab_content_manager) {}
 
-ContentLayer::~ContentLayer() {
-}
+ContentLayer::~ContentLayer() = default;
 
 }  //  namespace android
diff --git a/chrome/browser/android/compositor/scene_layer/static_tab_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/static_tab_scene_layer.cc
index 66ab55f..72a847c 100644
--- a/chrome/browser/android/compositor/scene_layer/static_tab_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/static_tab_scene_layer.cc
@@ -28,8 +28,7 @@
       background_color_(SK_ColorWHITE),
       brightness_(1.f) {}
 
-StaticTabSceneLayer::~StaticTabSceneLayer() {
-}
+StaticTabSceneLayer::~StaticTabSceneLayer() = default;
 
 bool StaticTabSceneLayer::ShouldShowBackground() {
   scoped_refptr<cc::slim::Layer> root = layer_->RootLayer();
@@ -40,17 +39,16 @@
   return background_color_;
 }
 
-void StaticTabSceneLayer::UpdateTabLayer(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& jobj,
-    jint id,
-    jboolean can_use_live_layer,
-    jint default_background_color,
-    jfloat x,
-    jfloat y,
-    jfloat static_to_view_blend,
-    jfloat saturation,
-    jfloat brightness) {
+void StaticTabSceneLayer::UpdateTabLayer(JNIEnv* env,
+                                         const JavaParamRef<jobject>& jobj,
+                                         jint id,
+                                         jboolean can_use_live_layer,
+                                         jint default_background_color,
+                                         jfloat x,
+                                         jfloat y,
+                                         jfloat static_to_view_blend,
+                                         jfloat saturation,
+                                         jfloat brightness) {
   DCHECK(tab_content_manager_)
       << "TabContentManager must be set before updating the layer";
 
@@ -66,10 +64,10 @@
   bool should_override_content_alpha = last_set_tab_id_ != id;
   last_set_tab_id_ = id;
 
-  content_layer_->SetProperties(
-      id, can_use_live_layer, static_to_view_blend,
-      should_override_content_alpha, content_alpha_override, saturation,
-      false, gfx::Rect());
+  content_layer_->SetProperties(id, can_use_live_layer, static_to_view_blend,
+                                should_override_content_alpha,
+                                content_alpha_override, saturation, false,
+                                gfx::Rect());
 
   content_layer_->layer()->SetPosition(gfx::PointF(x, y));
   content_layer_->layer()->SetIsDrawable(true);
diff --git a/chrome/browser/android/compositor/tab_content_manager.cc b/chrome/browser/android/compositor/tab_content_manager.cc
index f7a60be..b0c8aea2d 100644
--- a/chrome/browser/android/compositor/tab_content_manager.cc
+++ b/chrome/browser/android/compositor/tab_content_manager.cc
@@ -13,8 +13,10 @@
 
 #include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/containers/contains.h"
 #include "base/cxx17_backports.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -170,18 +172,24 @@
   return live_layer_list_[tab_id];
 }
 
-scoped_refptr<ThumbnailLayer> TabContentManager::GetStaticLayer(int tab_id) {
-  return static_layer_cache_[tab_id];
+ThumbnailLayer* TabContentManager::GetStaticLayer(int tab_id) {
+  if (tab_id == -1) {
+    return nullptr;
+  }
+  scoped_refptr<ThumbnailLayer> static_layer = static_layer_cache_[tab_id];
+  if (base::FeatureList::IsEnabled(thumbnail::kThumbnailCacheRefactor)) {
+    DCHECK(static_layer) << "Static layer should be created with "
+                            "UpdateVisibleIds before being requested";
+  }
+  return static_layer.get();
 }
 
-// TODO(crbug.com/1402843): ThumbnailCache::PruneCache() shouldn't cause issues
-// with `static_layer_cache_` as Thumbnails referenced by this cache may already
-// expire even without eager pruning. Investigate whether entries in
-// `static_layer_cache_` can and should be removed when their thumbnail is
-// dropped from the ThumbnailCache.
-scoped_refptr<ThumbnailLayer> TabContentManager::GetOrCreateStaticLayer(
+ThumbnailLayer* TabContentManager::GetOrCreateStaticLayer(
     int tab_id,
     bool force_disk_read) {
+  if (base::FeatureList::IsEnabled(thumbnail::kThumbnailCacheRefactor)) {
+    return GetStaticLayer(tab_id);
+  }
   thumbnail::Thumbnail* thumbnail =
       thumbnail_cache_->Get(tab_id, force_disk_read, true);
   scoped_refptr<ThumbnailLayer> static_layer = static_layer_cache_[tab_id];
@@ -200,7 +208,7 @@
   }
 
   static_layer->SetThumbnail(thumbnail);
-  return static_layer;
+  return static_layer.get();
 }
 
 void TabContentManager::AttachTab(JNIEnv* env,
@@ -329,15 +337,30 @@
     JNIEnv* env,
     const JavaParamRef<jintArray>& priority,
     jint primary_tab_id) {
-  std::list<int> priority_ids;
-  jsize length = env->GetArrayLength(priority);
-  jint* ints = env->GetIntArrayElements(priority, nullptr);
-  for (jsize i = 0; i < length; ++i) {
-    priority_ids.push_back(static_cast<int>(ints[i]));
-  }
-
-  env->ReleaseIntArrayElements(priority, ints, JNI_ABORT);
+  std::vector<int> priority_ids;
+  base::android::JavaIntArrayToIntVector(env, priority, &priority_ids);
   thumbnail_cache_->UpdateVisibleIds(priority_ids, primary_tab_id);
+  if (!base::FeatureList::IsEnabled(thumbnail::kThumbnailCacheRefactor)) {
+    return;
+  }
+  std::erase_if(static_layer_cache_, [priority_ids](const auto& pair) {
+    bool not_priority = !base::Contains(priority_ids, pair.first);
+    if (not_priority) {
+      pair.second->layer()->RemoveFromParent();
+    }
+    return not_priority;
+  });
+  for (int tab_id : priority_ids) {
+    auto it = static_layer_cache_.find(tab_id);
+    if (it == static_layer_cache_.end()) {
+      it = static_layer_cache_.insert({tab_id, ThumbnailLayer::Create()}).first;
+    }
+    thumbnail::Thumbnail* thumbnail =
+        thumbnail_cache_->Get(tab_id, false, false);
+    if (thumbnail) {
+      it->second->SetThumbnail(thumbnail);
+    }
+  }
 }
 
 void TabContentManager::NativeRemoveTabThumbnail(int tab_id) {
@@ -370,6 +393,18 @@
   thumbnail_cache_->OnUIResourcesWereEvicted();
 }
 
+void TabContentManager::OnThumbnailAddedToCache(int tab_id) {
+  if (!base::FeatureList::IsEnabled(thumbnail::kThumbnailCacheRefactor)) {
+    return;
+  }
+  auto it = static_layer_cache_.find(tab_id);
+  if (it != static_layer_cache_.end()) {
+    thumbnail::Thumbnail* thumbnail =
+        thumbnail_cache_->Get(tab_id, false, false);
+    it->second->SetThumbnail(thumbnail);
+  }
+}
+
 void TabContentManager::OnFinishedThumbnailRead(int tab_id) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_TabContentManager_notifyListenersOfThumbnailChange(
diff --git a/chrome/browser/android/compositor/tab_content_manager.h b/chrome/browser/android/compositor/tab_content_manager.h
index ecd4ae6..0c7a8c5 100644
--- a/chrome/browser/android/compositor/tab_content_manager.h
+++ b/chrome/browser/android/compositor/tab_content_manager.h
@@ -60,11 +60,17 @@
   // Get the live layer from the cache.
   scoped_refptr<cc::slim::Layer> GetLiveLayer(int tab_id);
 
-  scoped_refptr<ThumbnailLayer> GetStaticLayer(int tab_id);
+  // Returns the static ThumbnailLayer for a `tab_id`. Note that the lifecycle
+  // of the thumbnail is managed by the ThumbnailCache and not the
+  // ThumbnailLayer. When displaying a layer it is important that
+  // UpdateVisibleIds is called with all the Tab IDs that are required for
+  // before calling GetStaticLayer. ThumbnailLayer's should not be retained as
+  // their lifecycle is managed by this class.
+  ThumbnailLayer* GetStaticLayer(int tab_id);
 
+  // Deprecated: This will be replace by just GetStaticLayer soon.
   // Get the static thumbnail from the cache, or the NTP.
-  scoped_refptr<ThumbnailLayer> GetOrCreateStaticLayer(int tab_id,
-                                                       bool force_disk_read);
+  ThumbnailLayer* GetOrCreateStaticLayer(int tab_id, bool force_disk_read);
   // JNI methods.
 
   // Should be called when a tab gets a new live layer that should be served
@@ -108,6 +114,7 @@
   jint GetPendingReadbacksForTesting(JNIEnv* env);
 
   // ThumbnailCacheObserver implementation;
+  void OnThumbnailAddedToCache(thumbnail::TabId tab_id) override;
   void OnFinishedThumbnailRead(thumbnail::TabId tab_id) override;
 
  private:
diff --git a/chrome/browser/android/compositor/tab_content_manager_unittest.cc b/chrome/browser/android/compositor/tab_content_manager_unittest.cc
new file mode 100644
index 0000000..3e1d039
--- /dev/null
+++ b/chrome/browser/android/compositor/tab_content_manager_unittest.cc
@@ -0,0 +1,144 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/compositor/tab_content_manager.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/memory/weak_ptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "cc/resources/ui_resource_client.h"
+#include "chrome/browser/thumbnail/cc/features.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/android/ui_android_export.h"
+
+namespace android {
+namespace {
+
+#if DCHECK_IS_ON()
+#define EXPECT_DCHECK(statement, regex) \
+  EXPECT_DEATH_IF_SUPPORTED(statement, regex)
+#else
+#define EXPECT_DCHECK(statement, regex) \
+  { statement; }
+#endif
+
+constexpr int kDefaultCacheSize = 3;
+constexpr int kApproximationCacheSize = 0;
+constexpr int kCompressionQueueMaxSize = 2;
+constexpr int kWriteQueueMaxSize = 2;
+constexpr bool kUseApproximationThumbnail = false;
+constexpr bool kSaveJpegThumbnails = true;
+constexpr double kJpegAspectRatio = 0.85;
+
+class MockUIResourceProvider : public ui::UIResourceProvider {
+ public:
+  MOCK_METHOD(cc::UIResourceId,
+              CreateUIResource,
+              (cc::UIResourceClient*),
+              (override));
+  MOCK_METHOD(void, DeleteUIResource, (cc::UIResourceId), (override));
+  MOCK_METHOD(bool, SupportsETC1NonPowerOfTwo, (), (const, override));
+
+  base::WeakPtr<UIResourceProvider> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtrFactory<MockUIResourceProvider> weak_factory_{this};
+};
+
+}  // namespace
+
+class TabContentManagerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    tab_content_manager_ = std::make_unique<TabContentManager>(
+        env, nullptr, kDefaultCacheSize, kApproximationCacheSize,
+        kCompressionQueueMaxSize, kWriteQueueMaxSize,
+        kUseApproximationThumbnail, kSaveJpegThumbnails, kJpegAspectRatio);
+    tab_content_manager_->SetUIResourceProvider(
+        ui_resource_provider_.GetWeakPtr());
+
+    EXPECT_CALL(ui_resource_provider_, CreateUIResource(::testing::_))
+        .WillRepeatedly(::testing::Return(1));
+    scoped_feature_list_.InitWithFeatures({thumbnail::kThumbnailCacheRefactor},
+                                          {});
+  }
+
+  TabContentManager& tab_content_manager() { return *tab_content_manager_; }
+
+  content::BrowserTaskEnvironment task_environment_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  MockUIResourceProvider ui_resource_provider_;
+  std::unique_ptr<TabContentManager> tab_content_manager_;
+};
+
+TEST_F(TabContentManagerTest, UpdateTabIdsForStaticLayerCache) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  constexpr int kTabId1 = 6;
+  constexpr int kTabId2 = 7;
+  EXPECT_DCHECK(
+      {
+        EXPECT_FALSE(
+            tab_content_manager().GetOrCreateStaticLayer(kTabId1, true));
+      },
+      "");
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId1)); }, "");
+  EXPECT_DCHECK(
+      {
+        EXPECT_FALSE(
+            tab_content_manager().GetOrCreateStaticLayer(kTabId2, true));
+      },
+      "");
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId2)); }, "");
+
+  auto jarr = base::android::ToJavaIntArray(env, std::vector<int>({kTabId1}));
+  tab_content_manager().UpdateVisibleIds(
+      env, base::android::JavaParamRef<jintArray>(env, jarr.obj()), kTabId1);
+  EXPECT_TRUE(tab_content_manager().GetStaticLayer(kTabId1));
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId2)); }, "");
+
+  tab_content_manager().UpdateVisibleIds(
+      env, base::android::JavaParamRef<jintArray>(env, jarr.obj()), -1);
+  EXPECT_TRUE(tab_content_manager().GetStaticLayer(kTabId1));
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId2)); }, "");
+
+  jarr =
+      base::android::ToJavaIntArray(env, std::vector<int>({kTabId1, kTabId2}));
+  tab_content_manager().UpdateVisibleIds(
+      env, base::android::JavaParamRef<jintArray>(env, jarr.obj()), -1);
+  EXPECT_TRUE(tab_content_manager().GetStaticLayer(kTabId1));
+  EXPECT_TRUE(tab_content_manager().GetStaticLayer(kTabId2));
+
+  jarr = base::android::ToJavaIntArray(env, std::vector<int>({kTabId2}));
+  tab_content_manager().UpdateVisibleIds(
+      env, base::android::JavaParamRef<jintArray>(env, jarr.obj()), -1);
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId1)); }, "");
+  EXPECT_TRUE(tab_content_manager().GetStaticLayer(kTabId2));
+
+  jarr = base::android::ToJavaIntArray(env, std::vector<int>({}));
+  tab_content_manager().UpdateVisibleIds(
+      env, base::android::JavaParamRef<jintArray>(env, jarr.obj()), -1);
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId1)); }, "");
+  EXPECT_DCHECK(
+      { EXPECT_FALSE(tab_content_manager().GetStaticLayer(kTabId2)); }, "");
+}
+
+}  // namespace android
diff --git a/chrome/browser/android/contextualsearch/OWNERS b/chrome/browser/android/contextualsearch/OWNERS
index 7e9a0c0..102973a 100644
--- a/chrome/browser/android/contextualsearch/OWNERS
+++ b/chrome/browser/android/contextualsearch/OWNERS
@@ -1,6 +1,5 @@
 gangwu@chromium.org
 
 # Secondary
-bttk@chromium.org
 donnd@chromium.org
 twellington@chromium.org
diff --git a/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc b/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
index 845c0c6..aacba35 100644
--- a/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
+++ b/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
@@ -433,12 +433,11 @@
 
 IN_PROC_BROWSER_TEST_P(AppListIphBrowserTestWithTestConfig, ClickLink) {
   OpenAppListAndWaitForIphView();
-  raw_ptr<views::StyledLabel> description_label =
-      static_cast<views::StyledLabel*>(search_box_view()->GetViewByID(
-          ash::LauncherSearchIphView::ViewId::kDescriptionLabel));
+  raw_ptr<views::View> link_label = search_box_view()->GetViewByID(
+      ash::LauncherSearchIphView::ViewId::kDescriptionLinkLabel);
 
   ui_test_utils::TabAddedWaiter tab_added_waiter(browser());
-  description_label->ClickFirstLinkForTesting();
+  Click(link_label);
   tab_added_waiter.Wait();
   EXPECT_EQ(GURL("https://www.google.com/"),
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 129fea8..11571e15 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -100,6 +100,8 @@
 
 void DisplayOverlayController::AddOverlay(DisplayMode display_mode) {
   RemoveOverlayIfAny();
+  touch_injector_->window()->AddObserver(this);
+
   auto* shell_surface_base =
       exo::GetShellSurfaceBaseForWindow(touch_injector_->window());
   if (!shell_surface_base) {
@@ -138,6 +140,8 @@
 
     shell_surface_base->RemoveOverlay();
   }
+
+  touch_injector_->window()->RemoveObserver(this);
 }
 
 void DisplayOverlayController::SetEventTarget(views::Widget* overlay_widget,
@@ -454,7 +458,10 @@
 views::Widget* DisplayOverlayController::GetOverlayWidget() {
   auto* shell_surface_base =
       exo::GetShellSurfaceBaseForWindow(touch_injector_->window());
-  DCHECK(shell_surface_base);
+  // Shell surface is null for test.
+  if (!shell_surface_base) {
+    return nullptr;
+  }
 
   return shell_surface_base ? static_cast<views::Widget*>(
                                   shell_surface_base->GetFocusTraversable())
@@ -738,23 +745,22 @@
 void DisplayOverlayController::OnWidgetBoundsChanged(
     views::Widget* widget,
     const gfx::Rect& new_bounds) {
-  touch_injector_->UpdateForOverlayBoundsChanged(gfx::RectF(new_bounds));
+  UpdateForBoundsChanged(new_bounds);
+}
 
-  // Overlay |widget| is null for test.
-  if (!widget) {
+void DisplayOverlayController::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  // Disregard the bounds from animation and only care final window bounds.
+  if (reason == ui::PropertyChangeReason::FROM_ANIMATION) {
     return;
   }
 
-  auto mode = display_mode_;
-  SetDisplayMode(DisplayMode::kNone);
-  // Transition to |kView| mode except while on |kEducation| mode since
-  // displaying this UI needs to be ensured as the user shouldn't be able to
-  // manually access said view.
-  if (mode != DisplayMode::kEducation) {
-    mode = DisplayMode::kView;
-  }
-
-  SetDisplayMode(mode);
+  auto bounds = CalculateWindowContentBounds(window);
+  UpdateForBoundsChanged(
+      gfx::Rect(bounds.x(), bounds.y(), bounds.width(), bounds.height()));
 }
 
 bool DisplayOverlayController::HasMenuView() const {
@@ -874,6 +880,25 @@
   return nudge_view_ || nudge_view_alpha_;
 }
 
+void DisplayOverlayController::UpdateForBoundsChanged(const gfx::Rect& bounds) {
+  touch_injector_->UpdateForOverlayBoundsChanged(gfx::RectF(bounds));
+
+  // Overlay widget is null for test.
+  if (!GetOverlayWidget()) {
+    return;
+  }
+
+  auto mode = display_mode_;
+  SetDisplayMode(DisplayMode::kNone);
+  // Transition to |kView| mode except while on |kEducation| mode since the
+  // educational banner needs to remain visible until dismissed by the user.
+  if (mode != DisplayMode::kEducation) {
+    mode = DisplayMode::kView;
+  }
+
+  SetDisplayMode(mode);
+}
+
 void DisplayOverlayController::DismissEducationalViewForTesting() {
   OnEducationalViewDismissed();
 }
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
index 3d220b7..af30246 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -8,6 +8,8 @@
 #include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
+#include "ui/aura/window_observer.h"
+#include "ui/compositor/property_change_reason.h"
 #include "ui/events/event.h"
 #include "ui/events/event_handler.h"
 #include "ui/gfx/geometry/point.h"
@@ -41,7 +43,8 @@
 // |ActionEditMenu| and |MessageView| by listening to the |LocatedEvent|.
 class DisplayOverlayController : public ui::EventHandler,
                                  public ash::ColorModeObserver,
-                                 public views::WidgetObserver {
+                                 public views::WidgetObserver,
+                                 public aura::WindowObserver {
  public:
   DisplayOverlayController(TouchInjector* touch_injector, bool first_launch);
   DisplayOverlayController(const DisplayOverlayController&) = delete;
@@ -98,6 +101,12 @@
   void OnWidgetBoundsChanged(views::Widget* widget,
                              const gfx::Rect& new_bounds) override;
 
+  // aura::WindowObserver:
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+
   const TouchInjector* touch_injector() const { return touch_injector_; }
 
  private:
@@ -178,6 +187,8 @@
 
   bool ShowingNudge();
 
+  void UpdateForBoundsChanged(const gfx::Rect& bounds);
+
   // For test:
   gfx::Rect GetInputMappingViewBoundsForTesting();
   void DismissEducationalViewForTesting();
diff --git a/chrome/browser/ash/arc/process/arc_process_service.cc b/chrome/browser/ash/arc/process/arc_process_service.cc
index e12d1c6..0fb060e 100644
--- a/chrome/browser/ash/arc/process/arc_process_service.cc
+++ b/chrome/browser/ash/arc/process/arc_process_service.cc
@@ -43,7 +43,8 @@
 
 namespace {
 
-static constexpr char kInitName[] = "/init";
+static constexpr char kInitNameP[] = "/init";
+static constexpr char kInitNameR[] = "/system/bin/init";
 static constexpr bool kNotFocused = false;
 static constexpr int64_t kNoActivityTimeInfo = 0L;
 
@@ -57,7 +58,7 @@
     }
     // TODO(nya): Add more constraints to avoid mismatches.
     const std::string& process_name = entry.cmd_line_args()[0];
-    if (process_name == kInitName) {
+    if (process_name == kInitNameP || process_name == kInitNameR) {
       return entry.pid();
     }
   }
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index 8a4bd53..421725e9 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -36,6 +36,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/gl/gl_switches.h"
 #include "url/url_constants.h"
 
 namespace extensions {
@@ -52,6 +53,11 @@
     // Specify smallish window size to make testing of tab capture less CPU
     // intensive.
     command_line->AppendSwitchASCII(::switches::kWindowSize, "300,300");
+    // TODO(https://crbug.com/1424557): Remove this after fixing feature
+    // detection in 0c tab capture path as it'll no longer be needed.
+    if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+      command_line->AppendSwitch(::switches::kUseGpuInTests);
+    }
   }
 
   void AddExtensionToCommandLineAllowlist() {
diff --git a/chrome/browser/favicon/favicon_utils.cc b/chrome/browser/favicon/favicon_utils.cc
index b3efd15..5b8e339 100644
--- a/chrome/browser/favicon/favicon_utils.cc
+++ b/chrome/browser/favicon/favicon_utils.cc
@@ -20,9 +20,11 @@
 #include "content/public/browser/navigation_entry.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/favicon_size.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
@@ -60,6 +62,14 @@
   return SkColorSetRGB(hash[0], hash[1], hash[2]);
 }
 
+// Gets the appropriate light or dark rasterized default favicon.
+gfx::Image GetDefaultFaviconForColorScheme(bool is_dark) {
+  const int resource_id =
+      is_dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
+  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+      resource_id);
+}
+
 }  // namespace
 
 void CreateContentFaviconDriverForWebContents(
@@ -131,9 +141,19 @@
       ui::NativeTheme::GetInstanceForNativeUi();
   is_dark = native_theme && native_theme->ShouldUseDarkColors();
 #endif
-  int resource_id = is_dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
-  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-      resource_id);
+  return GetDefaultFaviconForColorScheme(is_dark);
+}
+
+ui::ImageModel GetDefaultFaviconModel(ui::ColorId bg_color) {
+  return ui::ImageModel::FromImageGenerator(
+      base::BindRepeating(
+          [](ui::ColorId bg_color, const ui::ColorProvider* provider) {
+            return *GetDefaultFaviconForColorScheme(
+                        color_utils::IsDark(provider->GetColor(bg_color)))
+                        .ToImageSkia();
+          },
+          bg_color),
+      gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
 }
 
 void SaveFaviconEvenIfInIncognito(content::WebContents* contents) {
diff --git a/chrome/browser/favicon/favicon_utils.h b/chrome/browser/favicon/favicon_utils.h
index 33ba65d..6729f18b8 100644
--- a/chrome/browser/favicon/favicon_utils.h
+++ b/chrome/browser/favicon/favicon_utils.h
@@ -7,6 +7,8 @@
 
 #include "components/favicon/content/content_favicon_driver.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/models/image_model.h"
+#include "ui/color/color_id.h"
 
 namespace content {
 class WebContents;
@@ -38,6 +40,12 @@
 // into account if necessary.
 gfx::Image GetDefaultFavicon();
 
+// Returns the image to use when no favicon is available, taking the background
+// color into account. If no background color is provided the window background
+// color will be used (which is appropriate for most use cases).
+ui::ImageModel GetDefaultFaviconModel(
+    ui::ColorId bg_color = ui::kColorWindowBackground);
+
 // Saves the favicon for the last committed navigation entry to the favicon
 // database.
 void SaveFaviconEvenIfInIncognito(content::WebContents* contents);
diff --git a/chrome/browser/favicon/favicon_utils_unittest.cc b/chrome/browser/favicon/favicon_utils_unittest.cc
index 8b2b47c..2042aa3 100644
--- a/chrome/browser/favicon/favicon_utils_unittest.cc
+++ b/chrome/browser/favicon/favicon_utils_unittest.cc
@@ -6,9 +6,22 @@
 
 #include "content/public/browser/navigation_entry.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_manager.h"
+#include "ui/color/color_recipe.h"
+#include "ui/resources/grit/ui_resources.h"
 
 namespace favicon {
 
+namespace {
+
+constexpr SkColor kLightColor = SK_ColorWHITE;
+constexpr SkColor kDarkColor = SK_ColorBLACK;
+
+}  // namespace
+
 TEST(FaviconUtilsTest, ShouldThemifyFavicon) {
   std::unique_ptr<content::NavigationEntry> entry =
       content::NavigationEntry::Create();
@@ -36,4 +49,115 @@
   EXPECT_TRUE(ShouldThemifyFaviconForEntry(entry.get()));
 }
 
+class DefaultFaviconModelTest : public ::testing::Test {
+ protected:
+  using InitializerType =
+      ui::ColorProviderManager::ColorProviderInitializerList::CallbackType;
+
+  // testing::Test:
+  void SetUp() override {
+    Test::SetUp();
+    AddDefaultInitializer();
+  }
+  void TearDown() override {
+    ui::ColorProviderManager::ResetForTesting();
+    Test::TearDown();
+  }
+
+  void AddDefaultInitializer() {
+    const auto initializer = [&](ui::ColorProvider* provider,
+                                 const ui::ColorProviderManager::Key& key) {
+      ui::ColorMixer& mixer = provider->AddMixer();
+      mixer[ui::kColorWindowBackground] = {
+          key.color_mode == ui::ColorProviderManager::ColorMode::kDark
+              ? kDarkColor
+              : kLightColor};
+    };
+    AddInitializer(base::BindRepeating(initializer));
+  }
+
+  void AddInitializer(InitializerType initializer) {
+    ui::ColorProviderManager& manager =
+        ui::ColorProviderManager::GetForTesting();
+    manager.AppendColorProviderInitializer(base::BindRepeating(initializer));
+  }
+
+  ui::ColorProvider* GetColorProvider(
+      ui::ColorProviderManager::ColorMode color_mode) {
+    ui::ColorProviderManager::Key key;
+    key.color_mode = color_mode;
+    return ui::ColorProviderManager::GetForTesting().GetColorProviderFor(key);
+  }
+
+  gfx::ImageSkia GetDefaultFaviconForColorScheme(bool is_dark) {
+    const int resource_id =
+        is_dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
+    return ui::ResourceBundle::GetSharedInstance()
+        .GetNativeImageNamed(resource_id)
+        .AsImageSkia();
+  }
+};
+
+TEST_F(DefaultFaviconModelTest, UsesCorrectIcon_LightBackground) {
+  auto* color_provider =
+      GetColorProvider(ui::ColorProviderManager::ColorMode::kLight);
+  const auto favicon_image = GetDefaultFaviconModel().Rasterize(color_provider);
+  EXPECT_TRUE(GetDefaultFaviconForColorScheme(/*is_dark=*/false)
+                  .BackedBySameObjectAs(favicon_image));
+}
+
+TEST_F(DefaultFaviconModelTest, UsesCorrectIcon_DarkBackground) {
+  auto* color_provider =
+      GetColorProvider(ui::ColorProviderManager::ColorMode::kDark);
+  const auto favicon_image = GetDefaultFaviconModel().Rasterize(color_provider);
+  EXPECT_TRUE(GetDefaultFaviconForColorScheme(/*is_dark=*/true)
+                  .BackedBySameObjectAs(favicon_image));
+}
+
+TEST_F(DefaultFaviconModelTest, UsesCorrectIcon_LightBackground_Custom) {
+  // Flip the background for a new color id such that it is light in dark mode
+  // and vice versa.
+  const auto initializer = [&](ui::ColorProvider* provider,
+                               const ui::ColorProviderManager::Key& key) {
+    ui::ColorMixer& mixer = provider->AddMixer();
+    mixer[ui::kColorBubbleBackground] = {
+        key.color_mode == ui::ColorProviderManager::ColorMode::kDark
+            ? kLightColor
+            : kDarkColor};
+  };
+  AddInitializer(base::BindRepeating(initializer));
+
+  // Even if the color provider is configured for light mode the default favicon
+  // model should match the dark variant when rasterized.
+  auto* color_provider =
+      GetColorProvider(ui::ColorProviderManager::ColorMode::kLight);
+  const auto favicon_image = GetDefaultFaviconModel(ui::kColorBubbleBackground)
+                                 .Rasterize(color_provider);
+  EXPECT_TRUE(GetDefaultFaviconForColorScheme(/*is_dark=*/true)
+                  .BackedBySameObjectAs(favicon_image));
+}
+
+TEST_F(DefaultFaviconModelTest, UsesCorrectIcon_DarkBackground_Custom) {
+  // Flip the background for a new color id such that it is light in dark mode
+  // and vice versa.
+  const auto initializer = [&](ui::ColorProvider* provider,
+                               const ui::ColorProviderManager::Key& key) {
+    ui::ColorMixer& mixer = provider->AddMixer();
+    mixer[ui::kColorBubbleBackground] = {
+        key.color_mode == ui::ColorProviderManager::ColorMode::kDark
+            ? kLightColor
+            : kDarkColor};
+  };
+  AddInitializer(base::BindRepeating(initializer));
+
+  // Even if the color provider is configured for dark mode the default favicon
+  // model should match the light variant when rasterized.
+  auto* color_provider =
+      GetColorProvider(ui::ColorProviderManager::ColorMode::kDark);
+  const auto favicon_image = GetDefaultFaviconModel(ui::kColorBubbleBackground)
+                                 .Rasterize(color_provider);
+  EXPECT_TRUE(GetDefaultFaviconForColorScheme(/*is_dark=*/false)
+                  .BackedBySameObjectAs(favicon_image));
+}
+
 }  // namespace favicon
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 956b1e5..f46cd3c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1268,6 +1268,11 @@
     "expiry_milestone": 107
   },
   {
+    "name": "cr2023-mac-font-smoothing",
+    "owners": [ "kerenzhu", "//ui/views/OWNERS", "chrome-mac-dev@google.com" ],
+    "expiry_milestone": 117
+  },
+  {
     "name": "cras-split-alsa-usb-internal",
     "owners": [ "whalechang@google.com", "chromeos-audio-sw@google.com" ],
     "expiry_milestone": 125
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0d38b741..b1af086 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4774,6 +4774,13 @@
 const char kBiometricAuthenticationInSettingsDescription[] =
     "Enables biometric authentication in settings to view/edit/copy a password";
 
+const char kCr2023MacFontSmoothingName[] =
+    "Chrome Refresh 2023 Mac Font Smoothing";
+const char kCr2023MacFontSmoothingDescription[] =
+    "Enables Mac Font Smoothing that simulates optical sizes "
+    "to enhance text readability at smaller scales. "
+    "Only effective when Chrome Refresh 2023 is enabled.";
+
 #if BUILDFLAG(ENABLE_PRINTING)
 const char kCupsIppPrintingBackendName[] = "CUPS IPP Printing Backend";
 const char kCupsIppPrintingBackendDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 453760d8..889763c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2744,6 +2744,9 @@
 extern const char kBiometricAuthenticationInSettingsName[];
 extern const char kBiometricAuthenticationInSettingsDescription[];
 
+extern const char kCr2023MacFontSmoothingName[];
+extern const char kCr2023MacFontSmoothingDescription[];
+
 #if BUILDFLAG(ENABLE_PRINTING)
 extern const char kCupsIppPrintingBackendName[];
 extern const char kCupsIppPrintingBackendDescription[];
diff --git a/chrome/browser/history_clusters/BUILD.gn b/chrome/browser/history_clusters/BUILD.gn
index ea82994..1d086a2 100644
--- a/chrome/browser/history_clusters/BUILD.gn
+++ b/chrome/browser/history_clusters/BUILD.gn
@@ -8,6 +8,7 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable/selectable_rounded_rectangle.xml",
     "java/res/layout/empty_text_view.xml",
     "java/res/layout/history_cluster.xml",
     "java/res/layout/history_cluster_visit.xml",
diff --git a/chrome/browser/history_clusters/java/res/drawable/selectable_rounded_rectangle.xml b/chrome/browser/history_clusters/java/res/drawable/selectable_rounded_rectangle.xml
new file mode 100644
index 0000000..3b799ff
--- /dev/null
+++ b/chrome/browser/history_clusters/java/res/drawable/selectable_rounded_rectangle.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!--
+Level list drawable that uses the same rounded rect bg at each level to satisfy the requirements of CheckableSelectableItemView.
+-->
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:maxLevel="@integer/list_item_level_default" android:drawable="@drawable/rounded_rectangle_surface_1" />
+  <item android:maxLevel="@integer/list_item_level_selected" android:drawable="@drawable/rounded_rectangle_surface_1"/>
+  <item android:maxLevel="@integer/list_item_level_incognito" android:drawable="@drawable/rounded_rectangle_surface_1"/>
+</level-list>
\ No newline at end of file
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
index 7eae6ee..74c23fa 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
@@ -439,7 +439,7 @@
                                 .with(HistoryClustersItemProperties.START_ICON_VISIBILITY,
                                         View.VISIBLE)
                                 .with(HistoryClustersItemProperties.START_ICON_BACKGROUND_RES,
-                                        R.drawable.rounded_rectangle_surface_1)
+                                        R.drawable.selectable_rounded_rectangle)
                                 .with(HistoryClustersItemProperties.CLICK_HANDLER,
                                         (v)
                                                 -> setQueryState(QueryState.forQuery(rawLabel,
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 1b99357..7830fd70 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -188,23 +188,26 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   auto current_routes = GetCurrentRoutes();
-
   std::vector<MediaRoute> added_routes =
       GetRouteSetDifference(routes, current_routes);
   std::vector<MediaRoute> removed_routes =
       GetRouteSetDifference(current_routes, routes);
 
+  // Update the internal_routes_observer_, and SetRoutesForProvider before
+  // AddMirroringMediaControllerHost, since the latter relies on these to be up
+  // to date.
+  internal_routes_observer_->OnRoutesUpdated(routes);
+  routes_query_.SetRoutesForProvider(provider_id, routes);
+
   for (const auto& route : added_routes) {
     if (route.IsLocalMirroringRoute()) {
       AddMirroringMediaControllerHost(route);
     }
   }
-
   for (const auto& route : removed_routes) {
     mirroring_media_controller_hosts_.erase(route.media_route_id());
   }
 
-  routes_query_.SetRoutesForProvider(provider_id, routes);
   routes_query_.NotifyObservers();
 }
 
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index a656efe..1ebc237 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -1089,4 +1089,35 @@
   EXPECT_TRUE(router()->GetCurrentRoutes().empty());
 }
 
+TEST_F(MediaRouterMojoImplTest, GetMirroringMediaControllerHost) {
+  MediaSource tab_source(kTabSourceOne);
+  auto local_mirroring_route =
+      MediaRoute(kRouteId, tab_source, kSinkId, kDescription, true);
+  local_mirroring_route.set_controller_type(RouteControllerType::kGeneric);
+  std::vector<MediaRoute> local_mirroring_routes{local_mirroring_route};
+  EXPECT_CALL(mock_cast_provider_,
+              CreateMediaRouteControllerInternal(kRouteId, _, _, _))
+      .WillOnce(
+          [&](const std::string& route_id,
+              mojo::PendingReceiver<mojom::MediaController>& media_controller,
+              mojo::PendingRemote<mojom::MediaStatusObserver>& observer,
+              MockMediaRouteProvider::CreateMediaRouteControllerCallback&
+                  callback) { std::move(callback).Run(true); });
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, local_mirroring_routes);
+  base::RunLoop().RunUntilIdle();
+
+  // Expect the host to exist for a local mirroring source.
+  EXPECT_NE(nullptr, router()->GetMirroringMediaControllerHost(kRouteId));
+
+  std::vector<MediaRoute> nonlocal_mirroring_routes{
+      MediaRoute(kRouteId2, tab_source, kSinkId, kDescription, false)};
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, nonlocal_mirroring_routes);
+
+  // Expect that the host for kRouteId no longer exists.
+  EXPECT_EQ(nullptr, router()->GetMirroringMediaControllerHost(kRouteId));
+
+  // Expect that no host for kRouteId2 exists, as it is not a local source.
+  EXPECT_EQ(nullptr, router()->GetMirroringMediaControllerHost(kRouteId2));
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/webrtc/capture_handle_browsertest.cc b/chrome/browser/media/webrtc/capture_handle_browsertest.cc
index 267e09a..88f6ba1 100644
--- a/chrome/browser/media/webrtc/capture_handle_browsertest.cc
+++ b/chrome/browser/media/webrtc/capture_handle_browsertest.cc
@@ -30,6 +30,7 @@
 #include "content/public/test/browser_test_base.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/prerender_test_util.h"
+#include "ui/gl/gl_switches.h"
 
 using content::WebContents;
 
@@ -221,6 +222,11 @@
         switches::kEnableExperimentalWebPlatformFeatures);
     command_line->AppendSwitchASCII(
         switches::kAutoSelectTabCaptureSourceByTitle, kCapturedTabTitle);
+    // TODO(https://crbug.com/1424557): Remove this after fixing feature
+    // detection in 0c tab capture path as it'll no longer be needed.
+    if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+      command_line->AppendSwitch(switches::kUseGpuInTests);
+    }
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/media/webrtc/conditional_focus_browsertest.cc b/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
index fb7f31bc..8260d9e1 100644
--- a/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
+++ b/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
@@ -19,6 +19,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "third_party/blink/public/common/switches.h"
+#include "ui/gl/gl_switches.h"
 
 namespace {
 
@@ -73,6 +74,11 @@
         switches::kAutoSelectTabCaptureSourceByTitle, kCapturedPageTitle);
     command_line->AppendSwitchASCII(blink::switches::kConditionalFocusWindowMs,
                                     "5000");
+    // TODO(https://crbug.com/1424557): Remove this after fixing feature
+    // detection in 0c tab capture path as it'll no longer be needed.
+    if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+      command_line->AppendSwitch(switches::kUseGpuInTests);
+    }
   }
 
   WebContents* OpenTestPageInNewTab(const std::string& test_url) {
diff --git a/chrome/browser/media/webrtc/region_capture_browsertest.cc b/chrome/browser/media/webrtc/region_capture_browsertest.cc
index f28a3af..1bac843 100644
--- a/chrome/browser/media/webrtc/region_capture_browsertest.cc
+++ b/chrome/browser/media/webrtc/region_capture_browsertest.cc
@@ -33,6 +33,7 @@
 #include "content/public/test/prerender_test_util.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "third_party/blink/public/common/features.h"
+#include "ui/gl/gl_switches.h"
 
 // TODO(crbug.com/1215089): Enable this test suite on Lacros.
 #if !BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -293,6 +294,11 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    // TODO(https://crbug.com/1424557): Remove this after fixing feature
+    // detection in 0c tab capture path as it'll no longer be needed.
+    if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+      command_line->AppendSwitch(switches::kUseGpuInTests);
+    }
     command_line_ = command_line;
   }
 
diff --git a/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc b/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
index 2e15ea60..11bda1b9 100644
--- a/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
@@ -39,6 +39,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "media/base/media_switches.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/gl/gl_switches.h"
 
 namespace {
 static const char kMainWebrtcTestHtmlPage[] = "/webrtc/webrtc_jsep01_test.html";
@@ -231,6 +232,11 @@
     command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource,
                                     "Entire screen");
     command_line->AppendSwitch(switches::kEnableUserMediaScreenCapturing);
+    // TODO(https://crbug.com/1424557): Remove this after fixing feature
+    // detection in 0c tab capture path as it'll no longer be needed.
+    if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+      command_line->AppendSwitch(switches::kUseGpuInTests);
+    }
   }
 
  protected:
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index ffab773..21ca5c3f 100644
--- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/base_switches.h"
+#include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/path_service.h"
@@ -44,6 +45,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gl/gl_switches.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
@@ -210,6 +212,16 @@
       ->GetButtonLabel(ConfirmInfoBarDelegate::InfoBarButton::BUTTON_CANCEL);
 }
 
+void AdjustCommandLineForZeroCopyCapture(base::CommandLine* command_line) {
+  CHECK(command_line);
+
+  // TODO(https://crbug.com/1424557): Remove this after fixing feature
+  // detection in 0c tab capture path as it'll no longer be needed.
+  if constexpr (!BUILDFLAG(IS_CHROMEOS)) {
+    command_line->AppendSwitch(switches::kUseGpuInTests);
+  }
+}
+
 }  // namespace
 
 // Base class for top level tests for getDisplayMedia().
@@ -411,6 +423,8 @@
         switches::kUseFakeDeviceForMediaStream,
         base::StringPrintf("display-media-type=%s",
                            test_config_.display_surface));
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
   }
 
   bool PreferCurrentTab() const override {
@@ -584,6 +598,8 @@
         switches::kEnableExperimentalWebPlatformFeatures);
     command_line->AppendSwitchASCII(
         switches::kAutoSelectTabCaptureSourceByTitle, kAppWindowTitle);
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
   }
 
   void SetUpOnMainThread() override {
@@ -649,6 +665,8 @@
         switches::kEnableExperimentalWebPlatformFeatures);
     command_line->AppendSwitchASCII(
         switches::kAutoSelectTabCaptureSourceByTitle, kSameOriginRenamedTitle);
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
   }
 
   void SetUpOnMainThread() override {
@@ -818,6 +836,8 @@
         switches::kUseFakeDeviceForMediaStream,
         base::StrCat({"display-media-type=",
                       DisplaySurfaceTypeAsString(display_surface_type_)}));
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
   }
 
   std::string GetVideoTrackType() {
@@ -1114,6 +1134,9 @@
         switches::kEnableExperimentalWebPlatformFeatures);
     command_line->AppendSwitchASCII(
         switches::kAutoSelectTabCaptureSourceByTitle, kCapturedTabTitle);
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
+
     if (!user_shared_audio_) {
       command_line->AppendSwitch(switches::kScreenCaptureAudioDefaultUnchecked);
     }
@@ -1330,6 +1353,8 @@
 
     command_line->AppendSwitchASCII(
         switches::kAutoSelectTabCaptureSourceByTitle, kMainHtmlTitle);
+
+    AdjustCommandLineForZeroCopyCapture(command_line);
   }
 
   std::string GetConstraints(bool prefer_current_tab = false) {
@@ -1367,9 +1392,8 @@
                          GetDisplayMediaSelfBrowserSurfaceBrowserTest,
                          testing::Values("", "include", "exclude"));
 
-// TODO(1428806) Re-enable flaky test.
 IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest,
-                       DISABLED_SelfBrowserSurfaceChangesCapturedTab) {
+                       SelfBrowserSurfaceChangesCapturedTab) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // This test relies on |capturing_tab| appearing earlier in the media picker,
@@ -1386,10 +1410,8 @@
   EXPECT_EQ(IsSelfBrowserSurfaceExclude(), other_tab->IsBeingCaptured());
 }
 
-// TODO(1428806) Re-enable flaky test.
-IN_PROC_BROWSER_TEST_P(
-    GetDisplayMediaSelfBrowserSurfaceBrowserTest,
-    DISABLED_SelfBrowserSurfaceInteractionWithPreferCurrentTab) {
+IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest,
+                       SelfBrowserSurfaceInteractionWithPreferCurrentTab) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // This test relies on |capturing_tab| appearing earlier in the media picker,
diff --git a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
index 0fddc53..b62e6e37 100644
--- a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
+++ b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
@@ -308,7 +308,6 @@
                        base::BindOnce(&PaintPreviewTabService::OnAXTreeWritten,
                                       weak_ptr_factory_.GetWeakPtr(), task)),
         ui::kAXModeWebContentsOnly,
-        /* exclude_offscreen= */ false,
         /* max_nodes= */ 5000,
         /* timeout= */ {});
   }
diff --git a/chrome/browser/partnercustomizations/OWNERS b/chrome/browser/partnercustomizations/OWNERS
index a650820..54c4baf5 100644
--- a/chrome/browser/partnercustomizations/OWNERS
+++ b/chrome/browser/partnercustomizations/OWNERS
@@ -1,2 +1 @@
-bttk@chromium.org
 wenyufu@chromium.org
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 54820fed..b8422fb5 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -5,7 +5,7 @@
 <if expr="is_chromeos">
         <cr-link-row
             label="$i18n{manageAccessibilityFeatures}"
-            on-click="onManageSystemAccessibilityFeaturesTap_"
+            on-click="onManageSystemAccessibilityFeaturesClick_"
             button-aria-description="$i18n{opensInNewTab}"
             sub-label="$i18n{moreFeaturesLinkDescription}" external>
         </cr-link-row>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.ts b/chrome/browser/resources/settings/a11y_page/a11y_page.ts
index 59e0f302..7bc34d89 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.ts
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.ts
@@ -229,7 +229,7 @@
   // </if>
 
   // <if expr="is_chromeos">
-  private onManageSystemAccessibilityFeaturesTap_() {
+  private onManageSystemAccessibilityFeaturesClick_() {
     window.location.href = 'chrome://os-settings/osAccessibility';
   }
   // </if>
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 699bcdd..9fd62348 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -42,7 +42,7 @@
     </style>
     <settings-section page-title="$i18n{aboutPageTitle}" section="about">
       <div class="cr-row two-line first">
-        <img id="product-logo" on-click="onProductLogoTap_"
+        <img id="product-logo" on-click="onProductLogoClick_"
             srcset="chrome://theme/current-channel-logo@1x 1x,
                     chrome://theme/current-channel-logo@2x 2x"
             alt="$i18n{aboutProductLogoAlt}"
@@ -92,7 +92,7 @@
         <div class="separator" hidden="[[!showButtonContainer_]]"></div>
         <span id="buttonContainer" hidden="[[!showButtonContainer_]]">
           <cr-button id="relaunch" hidden="[[!showRelaunch_]]"
-              on-click="onRelaunchTap_">
+              on-click="onRelaunchClick_">
             $i18n{aboutRelaunch}
           </cr-button>
         </span>
@@ -103,12 +103,12 @@
         <div id="promoteUpdater"
             class$="cr-row [[getPromoteUpdaterClass_(promoteUpdaterStatus_.disabled)]]"
             actionable$="[[promoteUpdaterStatus_.actionable]]"
-            on-click="onPromoteUpdaterTap_">
+            on-click="onPromoteUpdaterClick_">
           <div class="flex">
             [[promoteUpdaterStatus_.text]]
             <a href="https://support.google.com/chrome/answer/95414"
                 target="_blank" id="updaterLearnMore"
-                on-click="onLearnMoreTap_"
+                on-click="onLearnMoreClick_"
                 aria-label="$i18nPolymer{aboutLearnMoreUpdating}">
               $i18n{learnMore}
             </a>
@@ -120,23 +120,23 @@
         </div>
       </template>
 </if>
-      <cr-link-row class="hr" id="help" on-click="onHelpTap_"
+      <cr-link-row class="hr" id="help" on-click="onHelpClick_"
           label="$i18n{aboutGetHelpUsingChrome}"
           button-aria-description="$i18n{opensInNewTab}"
           external></cr-link-row>
 <if expr="_google_chrome">
-      <cr-link-row class="hr" id="reportIssue" on-click="onReportIssueTap_"
+      <cr-link-row class="hr" id="reportIssue" on-click="onReportIssueClick_"
           hidden="[[!prefs.feedback_allowed.value]]"
           button-aria-description="$i18n{opensInNewTab}"
           label="$i18n{aboutReportAnIssue}" external></cr-link-row>
       <cr-link-row class="hr" id="getTheMostOutOfChrome"
-          on-click="onGetTheMostOutOfChromeTap_"
+          on-click="onGetTheMostOutOfChromeClick_"
           label="$i18n{getTheMostOutOfChrome}"
           sub-label="$i18n{getTheMostOutOfChromeDescription}"
           role-description="$i18n{subpageArrowRoleDescription}"
           hidden$="[[!showGetTheMostOutOfChromeSection_]]"></cr-link-row>
 </if>
-      <cr-link-row class="hr" on-click="onManagementPageTap_"
+      <cr-link-row class="hr" on-click="onManagementPageClick_"
           start-icon="cr:domain" label="$i18n{managementPage}"
           role-description="$i18n{subpageArrowRoleDescription}"
           hidden$="[[!isManaged_]]"></cr-link-row>
diff --git a/chrome/browser/resources/settings/about_page/about_page.ts b/chrome/browser/resources/settings/about_page/about_page.ts
index 014229ec..4608d09d 100644
--- a/chrome/browser/resources/settings/about_page/about_page.ts
+++ b/chrome/browser/resources/settings/about_page/about_page.ts
@@ -194,7 +194,7 @@
   /**
    * If #promoteUpdater isn't disabled, trigger update promotion.
    */
-  private onPromoteUpdaterTap_() {
+  private onPromoteUpdaterClick_() {
     // This is necessary because #promoteUpdater is not a button, so by default
     // disable doesn't do anything.
     if (this.promoteUpdaterStatus_.disabled) {
@@ -204,17 +204,17 @@
   }
   // </if>
 
-  private onLearnMoreTap_(event: Event) {
+  private onLearnMoreClick_(event: Event) {
     // Stop the propagation of events, so that clicking on links inside
     // actionable items won't trigger action.
     event.stopPropagation();
   }
 
-  private onHelpTap_() {
+  private onHelpClick_() {
     this.aboutBrowserProxy_.openHelpPage();
   }
 
-  private onRelaunchTap_() {
+  private onRelaunchClick_() {
     this.performRestart(RestartType.RELAUNCH);
   }
 
@@ -322,11 +322,11 @@
     return this.currentUpdateStatusEvent_!.status === status;
   }
 
-  private onManagementPageTap_() {
+  private onManagementPageClick_() {
     window.location.href = 'chrome://management';
   }
 
-  private onProductLogoTap_() {
+  private onProductLogoClick_() {
     this.$['product-logo'].animate(
         {
           transform: ['none', 'rotate(-10turn)'],
@@ -338,11 +338,11 @@
   }
 
   // <if expr="_google_chrome">
-  private onReportIssueTap_() {
+  private onReportIssueClick_() {
     this.aboutBrowserProxy_.openFeedbackDialog();
   }
 
-  private onGetTheMostOutOfChromeTap_() {
+  private onGetTheMostOutOfChromeClick_() {
     // TODO(crbug.com/1423278): implement.
   }
   // </if>
diff --git a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
index 9054b8f2..8a2cc22 100644
--- a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
@@ -120,11 +120,11 @@
       </div>
       <div slot="button-container">
         <cr-button id="cancelButton" class="cancel-button"
-            on-click="onCancelTap_">
+            on-click="onCancelClick_">
           $i18n{cancel}
         </cr-button>
         <cr-button id="saveButton" class="action-button"
-            disabled="[[!canSave_]]" on-click="onSaveButtonTap_">
+            disabled="[[!canSave_]]" on-click="onSaveButtonClick_">
           $i18n{save}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.ts
index c23be87..e376e4f 100644
--- a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.ts
@@ -337,14 +337,14 @@
         component => !component.isValid && !component.isValidatable);
   }
 
-  private onCancelTap_(): void {
+  private onCancelClick_(): void {
     this.$.dialog.cancel();
   }
 
   /**
    * Handler for tapping the save button.
    */
-  private onSaveButtonTap_(): void {
+  private onSaveButtonClick_(): void {
     this.notifyValidity_();
 
     this.updateCanSave_();
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.html b/chrome/browser/resources/settings/autofill_page/autofill_section.html
index 3e00d548..5a0b990 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.html
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.html
@@ -29,7 +29,7 @@
     <div class="cr-row continuation">
       <h2 class="flex">$i18n{addresses}</h2>
       <cr-button id="addAddress" class="header-aligned-button"
-          on-click="onAddAddressTap_" aria-label="$i18n{addAddressTitle}"
+          on-click="onAddAddressClick_" aria-label="$i18n{addAddressTitle}"
           hidden$="[[!prefs.autofill.profile_enabled.value]]">
         $i18n{add}
       </cr-button>
@@ -52,7 +52,7 @@
               </iron-icon>
             </div>
             <cr-icon-button class="icon-more-vert address-menu"
-                on-click="onAddressMenuTap_"
+                on-click="onAddressMenuClick_"
                 title="[[moreActionsTitle_(item.metadata.summaryLabel,
                  item.metadata.summarySublabel)]]">
             </cr-icon-button>
@@ -66,9 +66,9 @@
     </div>
     <cr-action-menu id="addressSharedMenu" role-description="$i18n{menu}">
       <button id="menuEditAddress" class="dropdown-item"
-          on-click="onMenuEditAddressTap_">$i18n{edit}</button>
+          on-click="onMenuEditAddressClick_">$i18n{edit}</button>
       <button id="menuRemoveAddress" class="dropdown-item"
-          on-click="onMenuRemoveAddressTap_">$i18n{removeAddress}</button>
+          on-click="onMenuRemoveAddressClick_">$i18n{removeAddress}</button>
     </cr-action-menu>
     <template is="dom-if" if="[[showAddressDialog_]]" restamp>
       <settings-address-edit-dialog address="[[activeAddress]]"
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.ts b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
index 24996565..3f5a5200 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
@@ -150,7 +150,7 @@
   /**
    * Open the address action menu.
    */
-  private onAddressMenuTap_(
+  private onAddressMenuClick_(
       e: DomRepeatEvent<chrome.autofillPrivate.AddressEntry>) {
     const item = e.model.item;
 
@@ -165,7 +165,7 @@
   /**
    * Handles tapping on the "Add address" button.
    */
-  private onAddAddressTap_(e: Event) {
+  private onAddAddressClick_(e: Event) {
     e.preventDefault();
     this.activeAddress = {};
     this.showAddressDialog_ = true;
@@ -182,7 +182,7 @@
   /**
    * Handles tapping on the "Edit" address button.
    */
-  private onMenuEditAddressTap_(e: Event) {
+  private onMenuEditAddressClick_(e: Event) {
     e.preventDefault();
     this.showAddressDialog_ = true;
     this.$.addressSharedMenu.close();
@@ -212,7 +212,7 @@
   /**
    * Handles tapping on the "Remove" address button.
    */
-  private onMenuRemoveAddressTap_() {
+  private onMenuRemoveAddressClick_() {
     this.showAddressRemoveConfirmationDialog_ = true;
     this.$.addressSharedMenu.close();
   }
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
index 4270d7b..6aed65f 100644
--- a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
@@ -121,9 +121,9 @@
       </div>
       <div slot="button-container">
         <cr-button id="cancelButton" class="cancel-button"
-            on-click="onCancelButtonTap_">$i18n{cancel}</cr-button>
+            on-click="onCancelButtonClick_">$i18n{cancel}</cr-button>
         <cr-button id="saveButton" class="action-button"
-            on-click="onSaveButtonTap_"
+            on-click="onSaveButtonClick_"
             disabled="[[!saveEnabled_(nicknameInvalid_,
                 expired_, name_, cardNumber_, nickname_)]]">
           $i18n{save}
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts
index 31b21e74..02a934d 100644
--- a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts
@@ -200,14 +200,14 @@
   /**
    * Handler for tapping the 'cancel' button. Should just dismiss the dialog.
    */
-  private onCancelButtonTap_() {
+  private onCancelButtonClick_() {
     this.$.dialog.cancel();
   }
 
   /**
    * Handler for tapping the save button.
    */
-  private onSaveButtonTap_() {
+  private onSaveButtonClick_() {
     if (!this.saveEnabled_()) {
       return;
     }
diff --git a/chrome/browser/resources/settings/autofill_page/password_check_list_item.html b/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
index eff677445..8dd89acf 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
+++ b/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
@@ -104,7 +104,7 @@
                 id="insecurePassword" focus-row-control
                 focus-type="passwordField" readonly
                 type="[[getInputType_(isPasswordVisible)]]"
-                value="[[password_]]" on-click="onReadonlyInputTap_"
+                value="[[password_]]" on-click="onReadonlyInputClick_"
                 disabled$="[[!isPasswordVisible]]">
           </div>
           <template is="dom-if" if="[[showDetails]]">
diff --git a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
index c0ecad7..918db2a 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
@@ -177,7 +177,7 @@
         .then(password => this.set('item.password', password), _error => {});
   }
 
-  private onReadonlyInputTap_() {
+  private onReadonlyInputClick_() {
     if (this.isPasswordVisible) {
       (this.shadowRoot!.querySelector('#leakedPassword') as HTMLInputElement)
           .select();
diff --git a/chrome/browser/resources/settings/autofill_page/password_list_item.html b/chrome/browser/resources/settings/autofill_page/password_list_item.html
index 997e305a..65ff7eea 100644
--- a/chrome/browser/resources/settings/autofill_page/password_list_item.html
+++ b/chrome/browser/resources/settings/autofill_page/password_list_item.html
@@ -57,7 +57,7 @@
           <input id="password" aria-label=$i18n{editPasswordPasswordLabel}
               type="[[getPasswordInputType(entry.password)]]"
               class="password-field password-input" readonly
-              disabled$="[[!entry.password]]" on-click="onReadonlyInputTap_"
+              disabled$="[[!entry.password]]" on-click="onReadonlyInputClick_"
               value="[[getPassword(entry.password)]]"
               focus-row-control focus-type="passwordField">
           <cr-icon-button id="showPasswordButton"
@@ -85,7 +85,7 @@
       <cr-icon-button id="moreActionsButton" class="icon-more-vert"
           hidden$="[[shouldHideMoreActionsButton_(
               isPasswordViewPageEnabled_, shouldHideActionButtons)]]"
-          on-click="onPasswordMoreActionsButtonTap_" title="$i18n{moreActions}"
+          on-click="onPasswordMoreActionsButtonClick_" title="$i18n{moreActions}"
           focus-row-control focus-type="moreActionsButton"
           aria-label$="[[getMoreActionsLabel_(entry)]]"></cr-icon-button>
     </div>
diff --git a/chrome/browser/resources/settings/autofill_page/password_list_item.ts b/chrome/browser/resources/settings/autofill_page/password_list_item.ts
index b6ac43b6..5ba7f8f 100644
--- a/chrome/browser/resources/settings/autofill_page/password_list_item.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_list_item.ts
@@ -118,7 +118,7 @@
   /**
    * Selects the password on tap if revealed.
    */
-  private onReadonlyInputTap_() {
+  private onReadonlyInputClick_() {
     if (this.entry.password) {
       (this.shadowRoot!.querySelector('#password') as HTMLInputElement)
           .select();
@@ -139,7 +139,7 @@
         }));
   }
 
-  private onPasswordMoreActionsButtonTap_() {
+  private onPasswordMoreActionsButtonClick_() {
     this.dispatchEvent(new CustomEvent('password-more-actions-clicked', {
       bubbles: true,
       composed: true,
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
index e83ba81..0d88b96 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
@@ -30,7 +30,7 @@
     --password-list-item-padding-inline-start: var(--cr-section-indent-padding);
   }
 </style>
-<div id="moveMultiplePasswordsBanner" on-click="onMoveMultiplePasswordsTap_"
+<div id="moveMultiplePasswordsBanner" on-click="onMoveMultiplePasswordsClick_"
     class="cr-row two-line"
     hidden$="[[!shouldShowMoveMultiplePasswordsBanner_]]">
   <img id="googleGIcon" alt=""
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
index 1b23096..04e98ea 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
@@ -379,7 +379,7 @@
         loadTimeData.getString('googlePasswordManagerUrl'));
   }
 
-  private onMoveMultiplePasswordsTap_() {
+  private onMoveMultiplePasswordsClick_() {
     this.showMoveMultiplePasswordsDialog_ = true;
   }
 
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.html b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.html
index 67acd048..bfb4e1f 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.html
@@ -27,11 +27,11 @@
         </div>
         <div slot="button-container">
           <cr-button class="secondary-button header-aligned-button"
-              on-click="onCancelButtonTap_" id="cancelButton">
+              on-click="onCancelButtonClick_" id="cancelButton">
             $i18n{cancel}
           </cr-button>
           <cr-button class="action-button header-aligned-button"
-              on-click="onExportTap_" id="exportPasswordsButton">
+              on-click="onExportClick_" id="exportPasswordsButton">
             $i18n{exportPasswords}
           </cr-button>
         </div>
@@ -46,7 +46,7 @@
         </div>
         <div slot="button-container">
           <cr-button id="cancel_progress_button" class="header-aligned-button"
-              on-click="onCancelProgressButtonTap_">
+              on-click="onCancelProgressButtonClick_">
             $i18n{cancel}
           </cr-button>
         </div>
@@ -64,12 +64,12 @@
           </ul>
         </div>
         <div slot="button-container">
-          <cr-button class="header-aligned-button" on-click="onCancelButtonTap_"
+          <cr-button class="header-aligned-button" on-click="onCancelButtonClick_"
               id="cancelErrorButton">
             $i18n{cancel}
           </cr-button>
           <cr-button class="action-button header-aligned-button"
-              on-click="onExportTap_" id="tryAgainButton">
+              on-click="onExportClick_" id="tryAgainButton">
             $i18n{exportPasswordsTryAgain}
           </cr-button>
         </div>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
index 11764811..4b31fdb 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_export_dialog.ts
@@ -204,7 +204,7 @@
             'passwords-export-dialog-close', {bubbles: true, composed: true})));
   }
 
-  private onExportTap_() {
+  private onExportClick_() {
     // <if expr="is_chromeos">
     if (loadTimeData.getBoolean('useSystemAuthenticationForPasswordManager')) {
       this.exportPasswords_();
@@ -267,7 +267,7 @@
   /**
    * Handler for tapping the 'cancel' button. Should just dismiss the dialog.
    */
-  private onCancelButtonTap_() {
+  private onCancelButtonClick_() {
     this.close();
   }
 
@@ -275,7 +275,7 @@
    * Handler for tapping the 'cancel' button on the progress dialog. It should
    * cancel the export and dismiss the dialog.
    */
-  private onCancelProgressButtonTap_() {
+  private onCancelProgressButtonClick_() {
     this.passwordManager_.cancelExportPasswords();
     this.close();
   }
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
index 65a4421..d385127 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
@@ -13,20 +13,20 @@
     <cr-action-menu id="menu" role-description="$i18n{menu}">
       <button id="menuCopyPassword" class="dropdown-item"
           hidden$="[[activePassword_.entry.federationText]]"
-          on-click="onMenuCopyPasswordButtonTap_">$i18n{copyPassword}</button>
+          on-click="onMenuCopyPasswordButtonClick_">$i18n{copyPassword}</button>
       <button id="menuEditPassword" class="dropdown-item"
-          on-click="onMenuEditPasswordTap_">
+          on-click="onMenuEditPasswordClick_">
         [[getMenuEditPasswordName_(activePassword_)]]
       </button>
       <button id="menuSendPassword" class="dropdown-item"
           hidden$="[[!showPasswordSendButton_]]"
-          on-click="onMenuSendPasswordTap_">
+          on-click="onMenuSendPasswordClick_">
           $i18n{sendPassword}
       </button>
       <button id="menuRemovePassword" class="dropdown-item"
           on-click="onMenuRemovePasswordClick_">$i18n{removePassword}</button>
       <button id="menuMovePasswordToAccount"
-          on-click="onMenuMovePasswordToAccountTap_"
+          on-click="onMenuMovePasswordToAccountClick_"
           hidden$="[[!shouldShowMoveToAccountOption_(activePassword_,
               allowMoveToAccountOption, firstSignedInAccountEmail_)]]"
           class="dropdown-item">$i18n{movePasswordToAccount}</button>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
index 7dfeb0f..83546588 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.ts
@@ -258,7 +258,7 @@
     return !this.activePassword_ || !this.activePassword_.entry.federationText;
   }
 
-  private onMenuEditPasswordTap_() {
+  private onMenuEditPasswordClick_() {
     if (this.isPasswordEditable_()) {
       this.requestPlaintextPassword(
               this.activePassword_!.entry.id,
@@ -296,14 +296,14 @@
     this.activePassword_ = null;
   }
 
-  private onMenuSendPasswordTap_() {
+  private onMenuSendPasswordClick_() {
     // TODO(crbug.com/1298608): Implement the logic.
   }
 
   /**
    * Copy selected password to clipboard.
    */
-  private onMenuCopyPasswordButtonTap_() {
+  private onMenuCopyPasswordButtonClick_() {
     // Copy to clipboard occurs inside C++ and we don't expect getting
     // result back to javascript.
     this.requestPlaintextPassword(
@@ -377,7 +377,7 @@
   /**
    * Should only be called when |activePassword_| has a device copy.
    */
-  private onMenuMovePasswordToAccountTap_() {
+  private onMenuMovePasswordToAccountClick_() {
     this.$.menu.close();
     this.showPasswordMoveToAccountDialog_ = true;
   }
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index e38c90f1..4b0c3e8 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -193,7 +193,7 @@
       </h2>
       <template is="dom-if" if="[[!passwordManagerDisabledByPolicy_]]">
         <cr-button id="addPasswordButton" class="header-aligned-button"
-            on-click="onAddPasswordTap_" aria-label="$i18n{addPasswordTitle}">
+            on-click="onAddPasswordClick_" aria-label="$i18n{addPasswordTitle}">
           $i18n{add}
         </cr-button>
       </template>
@@ -202,7 +202,7 @@
                     hasSavedPasswords_, showImportPasswords_)]]">
         <cr-icon-button class="icon-more-vert header-aligned-button"
             id="exportImportMenuButton"
-            on-click="onImportExportMenuTap_" title="$i18n{moreActions}"
+            on-click="onImportExportMenuClick_" title="$i18n{moreActions}"
             focus-type="exportImportMenuButton"
             aria-describedby="savedPasswordsHeading"></cr-icon-button>
       </template>
@@ -291,11 +291,11 @@
     </passwords-list-handler>
     <cr-action-menu id="exportImportMenu" role-description="$i18n{menu}">
       <button id="menuImportPassword" class="dropdown-item"
-          on-click="onImportTap_" hidden="[[!showImportPasswords_]]">
+          on-click="onImportClick_" hidden="[[!showImportPasswords_]]">
         $i18n{importMenuItem}
       </button>
       <button id="menuExportPassword" class="dropdown-item"
-          on-click="onExportTap_" disabled="[[!hasSavedPasswords_]]">
+          on-click="onExportClick_" disabled="[[!hasSavedPasswords_]]">
         $i18n{exportMenuItem}
       </button>
     </cr-action-menu>
@@ -335,7 +335,7 @@
             </a>
           </div>
           <cr-icon-button class="icon-clear" id="removeExceptionButton"
-              on-click="onRemoveExceptionButtonTap_" tabindex$="[[tabIndex]]"
+              on-click="onRemoveExceptionButtonClick_" tabindex$="[[tabIndex]]"
               title="$i18n{deletePasswordException}"></cr-icon-button>
         </div>
       </template>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
index 0d68c97..2af4b87 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
@@ -519,7 +519,7 @@
   /**
    * Fires an event that should delete the password exception.
    */
-  private onRemoveExceptionButtonTap_(
+  private onRemoveExceptionButtonClick_(
       e: DomRepeatEvent<chrome.passwordsPrivate.ExceptionEntry>) {
     const exception = e.model.item;
     this.passwordManager_.removeException(exception.id);
@@ -528,7 +528,7 @@
   /**
    * Opens the export/import action menu.
    */
-  private onImportExportMenuTap_() {
+  private onImportExportMenuClick_() {
     const target = this.shadowRoot!.querySelector('#exportImportMenuButton') as
         HTMLElement;
     this.$.exportImportMenu.showAt(target);
@@ -537,7 +537,7 @@
   /**
    * Opens the passwords import dialog.
    */
-  private onImportTap_() {
+  private onImportClick_() {
     recordPasswordsImportInteraction(
         PasswordsImportDesktopInteractions.DIALOG_OPENED_FROM_THREE_DOT_MENU);
     this.showPasswordsImportDialog_ = true;
@@ -551,7 +551,7 @@
   /**
    * Opens the export passwords dialog.
    */
-  private onExportTap_() {
+  private onExportClick_() {
     this.showPasswordsExportDialog_ = true;
     this.$.exportImportMenu.close();
   }
@@ -560,7 +560,7 @@
     this.showPasswordsExportDialog_ = false;
   }
 
-  private onAddPasswordTap_() {
+  private onAddPasswordClick_() {
     chrome.metricsPrivate.recordEnumerationValue(
         'PasswordManager.AddCredentialFromSettings.UserAction2',
         AddCredentialFromSettingsUserInteractions.ADD_DIALOG_OPENED,
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.html
index d01c417..11c0be3 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.html
@@ -200,6 +200,16 @@
         on-input="onBrailleColumnsInput_" on-focusout="onBrailleColumnsFocusout_">
     </cr-input>
   </div>
+  <div class="settings-box continuation indented">
+    <div class="start settings-box-text" aria-hidden="true">
+      $i18n{chromeVoxVirtualBrailleDisplayStyleLabel}
+    </div>
+    <settings-dropdown-menu id="virtualBrailleDisplayStyleDropdown"
+        label="$i18n{chromeVoxVirtualBrailleDisplayStyleLabel}"
+        pref="{{prefs.settings.a11y.chromevox.braille_side_by_side}}"
+        menu-options="[[virtualBrailleDisplayStyleOptions_]]">
+    </settings-dropdown-menu>
+  </div>
 </div>
 <cr-expand-button id="developerOptionsExpandButton" class="hr"
     expanded="{{developerOptionsExpanded_}}">
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.ts b/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.ts
index 3cc632f..b7eae6e 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.ts
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/chromevox_subpage.ts
@@ -170,6 +170,28 @@
       },
 
       /**
+       * Dropdown menu choices for virtual braille display style options.
+       */
+      virtualBrailleDisplayStyleOptions_: {
+        readOnly: true,
+        type: Array,
+        value() {
+          return [
+            {
+              value: false,
+              name: loadTimeData.getString(
+                  'chromeVoxVirtualBrailleDisplayStyleInterleave'),
+            },
+            {
+              value: true,
+              name: loadTimeData.getString(
+                  'chromeVoxVirtualBrailleDisplayStyleSideBySide'),
+            },
+          ];
+        },
+      },
+
+      /**
        * Dropdown menu choices for voice options.
        */
       voiceOptions_: {
@@ -261,6 +283,7 @@
   private punctuationEchoOptions_: DropdownMenuOptionList;
   private audioStrategyOptions_: DropdownMenuOptionList;
   private voiceOptions_: DropdownMenuOptionList;
+  private virtualBrailleDisplayStyleOptions_: DropdownMenuOptionList;
   private chromeVoxBrowserProxy_: ChromeVoxSubpageBrowserProxy;
 
   // TODO(270619855): Add tests to verify these controls change their prefs.
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index e3c0326..a7de2fa 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -289,7 +289,7 @@
         </paper-spinner-lite>
         <cr-button class="cancel-button" autofocus
             disabled="[[clearingInProgress_]]"
-            on-click="onCancelTap_">$i18n{cancel}</cr-button>
+            on-click="onCancelClick_">$i18n{cancel}</cr-button>
         <cr-button id="clearBrowsingDataConfirm"
             class="action-button" on-click="clearBrowsingData_"
             disabled="[[isClearButtonDisabled_(clearingInProgress_,
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
index b78d2a83..3fcf6a8 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
@@ -429,7 +429,7 @@
     }
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     this.$.clearBrowsingDataDialog.cancel();
   }
 
diff --git a/chrome/browser/resources/settings/controls/controlled_radio_button.html b/chrome/browser/resources/settings/controls/controlled_radio_button.html
index 02264b7..db3d40d 100644
--- a/chrome/browser/resources/settings/controls/controlled_radio_button.html
+++ b/chrome/browser/resources/settings/controls/controlled_radio_button.html
@@ -12,7 +12,7 @@
   cr-policy-pref-indicator {
     margin-inline-start: var(--settings-controlled-by-spacing);
     /* Enable pointer events for the indicator so :hover works. Disable
-     * clicks/taps via onIndicatorTap_ so outer on-tap doesn't trigger. */
+     * clicks/taps via onIndicatorClick_ so outer on-tap doesn't trigger. */
     pointer-events: all;
   }
 </style>
@@ -39,7 +39,7 @@
 </div>
 
 <template is="dom-if" if="[[showIndicator_(disabled, name, pref.*)]]">
-  <cr-policy-pref-indicator pref="[[pref]]" on-click="onIndicatorTap_"
+  <cr-policy-pref-indicator pref="[[pref]]" on-click="onIndicatorClick_"
       icon-aria-label="[[label]]">
   </cr-policy-pref-indicator>
 </template>
diff --git a/chrome/browser/resources/settings/controls/controlled_radio_button.ts b/chrome/browser/resources/settings/controls/controlled_radio_button.ts
index a11cfb66d..10d0175 100644
--- a/chrome/browser/resources/settings/controls/controlled_radio_button.ts
+++ b/chrome/browser/resources/settings/controls/controlled_radio_button.ts
@@ -69,7 +69,7 @@
     return this.name === prefToString(this.pref);
   }
 
-  private onIndicatorTap_(e: Event) {
+  private onIndicatorClick_(e: Event) {
     // Disallow <controlled-radio-button on-click="..."> when disabled.
     e.preventDefault();
     e.stopPropagation();
diff --git a/chrome/browser/resources/settings/controls/password_prompt_dialog.html b/chrome/browser/resources/settings/controls/password_prompt_dialog.html
index 14501ba..09cadaf 100644
--- a/chrome/browser/resources/settings/controls/password_prompt_dialog.html
+++ b/chrome/browser/resources/settings/controls/password_prompt_dialog.html
@@ -25,7 +25,7 @@
     </cr-input>
   </div>
   <div slot="button-container">
-    <cr-button class="cancel-button" on-click="onCancelTap_">
+    <cr-button class="cancel-button" on-click="onCancelClick_">
       $i18n{cancel}
     </cr-button>
 
diff --git a/chrome/browser/resources/settings/controls/password_prompt_dialog.ts b/chrome/browser/resources/settings/controls/password_prompt_dialog.ts
index 23fa2e1..b03114d1 100644
--- a/chrome/browser/resources/settings/controls/password_prompt_dialog.ts
+++ b/chrome/browser/resources/settings/controls/password_prompt_dialog.ts
@@ -108,7 +108,7 @@
     }, 1);
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     if (this.$.dialog.open) {
       this.$.dialog.close();
     }
diff --git a/chrome/browser/resources/settings/controls/settings_toggle_button.ts b/chrome/browser/resources/settings/controls/settings_toggle_button.ts
index 2717620f..5b0e41b3 100644
--- a/chrome/browser/resources/settings/controls/settings_toggle_button.ts
+++ b/chrome/browser/resources/settings/controls/settings_toggle_button.ts
@@ -105,7 +105,7 @@
   override ready() {
     super.ready();
 
-    this.addEventListener('click', this.onHostTap_);
+    this.addEventListener('click', this.onHostClick_);
   }
 
   private fire_(eventName: string, detail?: any) {
@@ -140,7 +140,7 @@
    * Handles non cr-toggle button clicks (cr-toggle handles its own click events
    * which don't bubble).
    */
-  private onHostTap_(e: Event) {
+  private onHostClick_(e: Event) {
     e.stopPropagation();
     if (this.controlDisabled()) {
       return;
diff --git a/chrome/browser/resources/settings/default_browser_page/default_browser_page.html b/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
index b63eceb..dd968c11 100644
--- a/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
+++ b/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
@@ -7,7 +7,7 @@
           <div class="secondary">$i18n{defaultBrowserMakeDefault}</div>
         </div>
         <div class="separator"></div>
-        <cr-button on-click="onSetDefaultBrowserTap_">
+        <cr-button on-click="onSetDefaultBrowserClick_">
           $i18n{defaultBrowserMakeDefaultButton}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/default_browser_page/default_browser_page.ts b/chrome/browser/resources/settings/default_browser_page/default_browser_page.ts
index 812da8c..a033ca9 100644
--- a/chrome/browser/resources/settings/default_browser_page/default_browser_page.ts
+++ b/chrome/browser/resources/settings/default_browser_page/default_browser_page.ts
@@ -78,7 +78,7 @@
     }
   }
 
-  private onSetDefaultBrowserTap_() {
+  private onSetDefaultBrowserClick_() {
     this.browserProxy_.setAsDefaultBrowser();
   }
 }
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html
index 34c2863..f525389b 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.html
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -36,7 +36,7 @@
         <div class="flex">$i18n{openFileTypesAutomatically}</div>
         <div class="separator"></div>
         <cr-button id="resetAutoOpenFileTypes"
-            on-click="onClearAutoOpenFileTypesTap_">
+            on-click="onClearAutoOpenFileTypesClick_">
           $i18n{clear}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.ts b/chrome/browser/resources/settings/downloads_page/downloads_page.ts
index db99b469..ef181d6e 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.ts
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.ts
@@ -105,7 +105,7 @@
   }
   // </if>
 
-  private onClearAutoOpenFileTypesTap_() {
+  private onClearAutoOpenFileTypesClick_() {
     this.browserProxy_.resetAutoOpenFileTypes();
   }
 }
diff --git a/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html b/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html
index 07df4fc..8edc0c1 100644
--- a/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html
+++ b/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html
@@ -6,7 +6,7 @@
     <div class="list-item">
       <div class="start">[[applicationName]]</div>
       <div class="separator"></div>
-      <cr-button class="action-button" on-click="onActionTap_">
+      <cr-button class="action-button" on-click="onActionClick_">
         [[getActionName_(actionType)]]
       </cr-button>
     </div>
diff --git a/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.ts b/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.ts
index 929e7ec9..a2a663c0 100644
--- a/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.ts
+++ b/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.ts
@@ -89,7 +89,7 @@
    * Executes the action for this incompatible application, depending on
    * actionType.
    */
-  private onActionTap_() {
+  private onActionClick_() {
     if (this.actionType === ActionTypes.UNINSTALL) {
       this.browserProxy_.startApplicationUninstallation(this.applicationName);
     } else if (
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index 27c8bdd8..d62dc15 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -51,10 +51,10 @@
         </iron-list>
       </div>
       <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelButtonTap_">
+        <cr-button class="cancel-button" on-click="onCancelButtonClick_">
           $i18n{cancel}
         </cr-button>
-        <cr-button class="action-button" on-click="onActionButtonTap_"
+        <cr-button class="action-button" on-click="onActionButtonClick_"
             disabled="[[disableActionButton_]]">
           $i18n{add}
         </cr-button>
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts b/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
index 009f380..8869694 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.ts
@@ -165,12 +165,12 @@
     this.disableActionButton_ = !this.languagesToAdd_.size;
   }
 
-  private onCancelButtonTap_() {
+  private onCancelButtonClick_() {
     this.$.dialog.close();
   }
 
   /** Enables the checked languages. */
-  private onActionButtonTap_() {
+  private onActionButtonClick_() {
     this.dispatchEvent(new CustomEvent('languages-added', {
       bubbles: true,
       composed: true,
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
index bbaf5ce60..460a05ce 100644
--- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
+++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -25,7 +25,7 @@
           invalid="[[isWordInvalid_(newWordValue_, words_.*)]]"
           error-message="[[getErrorMessage_(newWordValue_, words_.*)]]"
           spellcheck="false">
-        <cr-button on-click="onAddWordTap_" id="addWord" slot="suffix"
+        <cr-button on-click="onAddWordClick_" id="addWord" slot="suffix"
             disabled$="[[disableAddButton_(newWordValue_, words_.*)]]">
           $i18n{addDictionaryWordButton}
         </cr-button>
@@ -41,7 +41,7 @@
           <template>
             <div class="list-item">
               <div id$="word[[index]]" class="word text-elide">[[item]]</div>
-              <cr-icon-button class="icon-clear" on-click="onRemoveWordTap_"
+              <cr-icon-button class="icon-clear" on-click="onRemoveWordClick_"
                   tabindex$="[[tabIndex]]"
                   title="$i18n{deleteDictionaryWordButton}"
                   aria-describedby$="word[[index]]">
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.ts b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.ts
index 7bae8f0..5955ea6 100644
--- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.ts
+++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.ts
@@ -163,7 +163,7 @@
   /**
    * Handles tapping on the Add Word button.
    */
-  private onAddWordTap_() {
+  private onAddWordClick_() {
     this.addWordFromInput_();
     this.$.newWord.focus();
   }
@@ -225,7 +225,7 @@
   /**
    * Handles tapping on a "Remove word" icon button.
    */
-  private onRemoveWordTap_(e: {model: {item: string}}) {
+  private onRemoveWordClick_(e: {model: {item: string}}) {
     this.languageSettingsPrivate_!.removeSpellcheckWord(e.model.item);
   }
 }
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 7d9b536..8784a41 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -66,7 +66,7 @@
     </h2>
     <cr-button id="addLanguages" class="header-aligned-button"
         disabled="[[!canEnableSomeSupportedLanguage_(languages)]]"
-        on-click="onAddLanguagesTap_">
+        on-click="onAddLanguagesClick_">
       $i18n{addLanguages}
     </cr-button>
   </div>
@@ -96,7 +96,7 @@
         <template is="dom-if" if="[[isRestartRequired_(
             item.language.code, languages.prospectiveUILanguage)]]"
             restamp>
-          <cr-button id="restartButton" on-click="onRestartTap_">
+          <cr-button id="restartButton" on-click="onRestartClick_">
             $i18n{restart}
           </cr-button>
         </template>
@@ -104,7 +104,7 @@
         <cr-icon-button class="icon-more-vert"
             title="$i18n{moreActions}"
             id="more-[[item.language.code]]"
-            on-click="onDotsTap_">
+            on-click="onDotsClick_">
         </cr-icon-button>
       </div>
     </template>
@@ -151,24 +151,24 @@
           <hr hidden="[[!shouldShowDialogSeparator_(languages.enabled.*)]]">
         </template>
         <button class="dropdown-item" role="menuitem"
-            on-click="onMoveToTopTap_"
+            on-click="onMoveToTopClick_"
             hidden="[[isNthLanguage_(
                 0, detailLanguage_, languages.enabled.*)]]">
           $i18n{moveToTop}
         </button>
         <button class="dropdown-item" role="menuitem"
-            on-click="onMoveUpTap_"
+            on-click="onMoveUpClick_"
             hidden="[[!showMoveUp_(detailLanguage_, languages.enabled.*)]]">
           $i18n{moveUp}
         </button>
         <button class="dropdown-item" role="menuitem"
-            on-click="onMoveDownTap_"
+            on-click="onMoveDownClick_"
             hidden="[[!showMoveDown_(
                 detailLanguage_, languages.enabled.*)]]">
           $i18n{moveDown}
         </button>
         <button class="dropdown-item" role="menuitem"
-            on-click="onRemoveLanguageTap_"
+            on-click="onRemoveLanguageClick_"
             disabled="[[!detailLanguage_.removable]]">
           $i18n{removeLanguage}
         </button>
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.ts b/chrome/browser/resources/settings/languages_page/languages_page.ts
index 6aaea00..31e48be7 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.ts
+++ b/chrome/browser/resources/settings/languages_page/languages_page.ts
@@ -144,7 +144,7 @@
    * Stamps and opens the Add Languages dialog, registering a listener to
    * disable the dialog's dom-if again on close.
    */
-  private onAddLanguagesTap_(e: Event) {
+  private onAddLanguagesClick_(e: Event) {
     e.preventDefault();
     this.languageSettingsMetricsProxy_.recordPageImpressionMetric(
         LanguageSettingsPageImpressionType.ADD_LANGUAGE);
@@ -353,7 +353,7 @@
   /**
    * Handler for the restart button.
    */
-  private onRestartTap_() {
+  private onRestartClick_() {
     this.performRestart(RestartType.RESTART);
   }
   // </if>
@@ -414,7 +414,7 @@
   /**
    * Moves the language to the top of the list.
    */
-  private onMoveToTopTap_() {
+  private onMoveToTopClick_() {
     this.$.menu.get().close();
     if (this.detailLanguage_!.isForced) {
       // If language is managed, show dialog to inform user it can't be modified
@@ -430,7 +430,7 @@
   /**
    * Moves the language up in the list.
    */
-  private onMoveUpTap_() {
+  private onMoveUpClick_() {
     this.$.menu.get().close();
     if (this.detailLanguage_!.isForced) {
       // If language is managed, show dialog to inform user it can't be modified
@@ -446,7 +446,7 @@
   /**
    * Moves the language down in the list.
    */
-  private onMoveDownTap_() {
+  private onMoveDownClick_() {
     this.$.menu.get().close();
     if (this.detailLanguage_!.isForced) {
       // If language is managed, show dialog to inform user it can't be modified
@@ -462,7 +462,7 @@
   /**
    * Disables the language.
    */
-  private onRemoveLanguageTap_() {
+  private onRemoveLanguageClick_() {
     this.$.menu.get().close();
     if (this.detailLanguage_!.isForced) {
       // If language is managed, show dialog to inform user it can't be modified
@@ -490,7 +490,7 @@
     return '';
   }
 
-  private onDotsTap_(e: DomRepeatEvent<LanguageState>) {
+  private onDotsClick_(e: DomRepeatEvent<LanguageState>) {
     // Set a copy of the LanguageState object since it is not data-bound to
     // the languages model directly.
     this.detailLanguage_ = Object.assign({}, e.model.item);
diff --git a/chrome/browser/resources/settings/languages_page/spell_check_page.html b/chrome/browser/resources/settings/languages_page/spell_check_page.html
index 453f4b87..44e0189 100644
--- a/chrome/browser/resources/settings/languages_page/spell_check_page.html
+++ b/chrome/browser/resources/settings/languages_page/spell_check_page.html
@@ -144,7 +144,7 @@
           </template>
         </div>
       </div>
-      <cr-link-row on-click="onEditDictionaryTap_"
+      <cr-link-row on-click="onEditDictionaryClick_"
           id="spellCheckSubpageTrigger" label="$i18n{manageSpellCheck}"
           role-description="$i18n{subpageArrowRoleDescription}">
       </cr-link-row>
diff --git a/chrome/browser/resources/settings/languages_page/spell_check_page.ts b/chrome/browser/resources/settings/languages_page/spell_check_page.ts
index 0be61dc..a6b69f6 100644
--- a/chrome/browser/resources/settings/languages_page/spell_check_page.ts
+++ b/chrome/browser/resources/settings/languages_page/spell_check_page.ts
@@ -252,7 +252,7 @@
   /**
    * Opens the Custom Dictionary page.
    */
-  private onEditDictionaryTap_() {
+  private onEditDictionaryClick_() {
     Router.getInstance().navigateTo(routes.EDIT_DICTIONARY);
   }
 
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html b/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
index ffd483f..82b05cd 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
@@ -10,9 +10,9 @@
         </cr-input>
       </div>
       <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelTap_"
+        <cr-button class="cancel-button" on-click="onCancelClick_"
             id="cancel">$i18n{cancel}</cr-button>
         <cr-button id="actionButton" class="action-button"
-            on-click="onActionButtonTap_">[[actionButtonText_]]</cr-button>
+            on-click="onActionButtonClick_">[[actionButtonText_]]</cr-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.ts b/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.ts
index fe7daa4..e561e1da 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.ts
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.ts
@@ -112,11 +112,11 @@
     return ['', invalidUrl, tooLong][this.error_];
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     this.$.dialog.close();
   }
 
-  private onActionButtonTap_() {
+  private onActionButtonClick_() {
     const whenDone = this.model ?
         this.browserProxy_.editStartupPage(this.model.modelIndex, this.url_) :
         this.browserProxy_.addStartupPage(this.url_);
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
index 2a39db3..7631f1b 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
@@ -10,17 +10,17 @@
         <div class="text-elide secondary">[[model.url]]</div>
       </div>
       <template is="dom-if" if="[[editable]]">
-        <cr-icon-button class="icon-more-vert" id="dots" on-click="onDotsTap_"
+        <cr-icon-button class="icon-more-vert" id="dots" on-click="onDotsClick_"
             title="$i18n{moreActions}" focus-row-control focus-type="menu">
         </cr-icon-button>
         <cr-lazy-render id="menu">
           <template>
             <cr-action-menu role-description="$i18n{menu}">
-              <button class="dropdown-item" on-click="onEditTap_">
+              <button class="dropdown-item" on-click="onEditClick_">
                 $i18n{edit}
               </button>
               <button class="dropdown-item" id="remove"
-                  on-click="onRemoveTap_">
+                  on-click="onRemoveClick_">
                 $i18n{onStartupRemove}
               </button>
             </cr-action-menu>
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.ts b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.ts
index 3e2365a..4672b94 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.ts
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.ts
@@ -56,13 +56,13 @@
   editable: boolean;
   model: StartupPageInfo;
 
-  private onRemoveTap_() {
+  private onRemoveClick_() {
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
     StartupUrlsPageBrowserProxyImpl.getInstance().removeStartupPage(
         this.model.modelIndex);
   }
 
-  private onEditTap_(e: Event) {
+  private onEditClick_(e: Event) {
     e.preventDefault();
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
     this.dispatchEvent(new CustomEvent(EDIT_STARTUP_URL_EVENT, {
@@ -75,7 +75,7 @@
     }));
   }
 
-  private onDotsTap_() {
+  private onDotsClick_() {
     const actionMenu =
         this.shadowRoot!
             .querySelector<CrLazyRenderElement<CrActionMenuElement>>(
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
index ac94878..f51e1bd 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -30,13 +30,13 @@
       <template is="dom-if" if="[[shouldAllowUrlsEdit_(
           prefs.session.startup_urls.enforcement)]]" restamp>
         <div class="list-item" id="addPage">
-          <a is="action-link" class="list-button" on-click="onAddPageTap_">
+          <a is="action-link" class="list-button" on-click="onAddPageClick_">
             $i18n{onStartupAddNewPage}
           </a>
         </div>
         <div class="list-item" id="useCurrentPages">
           <a is="action-link" class="list-button"
-              on-click="onUseCurrentPagesTap_">
+              on-click="onUseCurrentPagesClick_">
             $i18n{onStartupUseCurrent}
           </a>
         </div>
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.ts b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.ts
index 194e908..70e010b 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.ts
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.ts
@@ -99,7 +99,7 @@
     });
   }
 
-  private onAddPageTap_(e: Event) {
+  private onAddPageClick_(e: Event) {
     e.preventDefault();
     this.showStartupUrlDialog_ = true;
     this.startupUrlDialogAnchor_ =
@@ -115,7 +115,7 @@
     }
   }
 
-  private onUseCurrentPagesTap_() {
+  private onUseCurrentPagesClick_() {
     this.browserProxy_.useCurrentPages();
   }
 
diff --git a/chrome/browser/resources/settings/people_page/import_data_dialog.html b/chrome/browser/resources/settings/people_page/import_data_dialog.html
index fa352e0..1a1b47ad 100644
--- a/chrome/browser/resources/settings/people_page/import_data_dialog.html
+++ b/chrome/browser/resources/settings/people_page/import_data_dialog.html
@@ -108,7 +108,7 @@
                 importStatusEnum_.SUCCEEDED, importStatus_)]]"
             disabled="[[shouldDisableImport_(
                 importStatus_, noImportDataTypeSelected_)]]"
-            on-click="onActionButtonTap_">
+            on-click="onActionButtonClick_">
           [[getActionButtonText_(selected_)]]
         </cr-button>
 
diff --git a/chrome/browser/resources/settings/people_page/import_data_dialog.ts b/chrome/browser/resources/settings/people_page/import_data_dialog.ts
index 1d05aef..f8216c36 100644
--- a/chrome/browser/resources/settings/people_page/import_data_dialog.ts
+++ b/chrome/browser/resources/settings/people_page/import_data_dialog.ts
@@ -153,7 +153,7 @@
     this.selected_ = this.browserProfiles_[this.$.browserSelect.selectedIndex];
   }
 
-  private onActionButtonTap_() {
+  private onActionButtonClick_() {
     const checkboxes = this.shadowRoot!.querySelectorAll('settings-checkbox');
     if (this.isImportFromFileSelected_()) {
       this.browserProxy_.importFromBookmarksFile();
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index a63d8531..122499a 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -70,7 +70,7 @@
             syncStatus.syncSystemEnabled, signinAllowed_)]]" restamp>
           <div id="profile-row" class="cr-row first two-line"
                   actionable$="[[isProfileActionable_]]"
-                  on-click="onProfileTap_">
+                  on-click="onProfileClick_">
             <template is="dom-if" if="[[syncStatus]]">
               <div id="profile-icon"
                   style="background-image: [[getIconImageSet_(
@@ -107,7 +107,7 @@
         <cr-link-row id="sync-setup"
             label="$i18n{syncAndNonPersonalizedServices}"
             sub-label="[[getSyncAndGoogleServicesSubtext_(syncStatus)]]"
-            on-click="onSyncTap_"
+            on-click="onSyncClick_"
             role-description="$i18n{subpageArrowRoleDescription}">
         </cr-link-row>
 
@@ -121,14 +121,14 @@
 
           <cr-link-row id="edit-profile"
               label="$i18n{profileNameAndPicture}"
-              on-click="onProfileTap_" ></cr-link-row>
+              on-click="onProfileClick_" ></cr-link-row>
         </template>
 </if>
 
 <if expr="not is_chromeos">
         <cr-link-row id="importDataDialogTrigger"
             label="$i18n{importTitle}"
-            on-click="onImportDataTap_"></cr-link-row>
+            on-click="onImportDataClick_"></cr-link-row>
 </if>
 
       </div>
diff --git a/chrome/browser/resources/settings/people_page/people_page.ts b/chrome/browser/resources/settings/people_page/people_page.ts
index 8ccbf35..face78cf 100644
--- a/chrome/browser/resources/settings/people_page/people_page.ts
+++ b/chrome/browser/resources/settings/people_page/people_page.ts
@@ -338,7 +338,7 @@
   }
   // </if>
 
-  private onProfileTap_() {
+  private onProfileClick_() {
     // <if expr="chromeos_ash">
     if (loadTimeData.getBoolean('isAccountManagerEnabled')) {
       // Post-SplitSettings. The browser C++ code loads OS settings in a window.
@@ -359,13 +359,13 @@
     }
   }
 
-  private onSyncTap_() {
+  private onSyncClick_() {
     // Users can go to sync subpage regardless of sync status.
     Router.getInstance().navigateTo(routes.SYNC);
   }
 
   // <if expr="not is_chromeos">
-  private onImportDataTap_() {
+  private onImportDataClick_() {
     Router.getInstance().navigateTo(routes.IMPORT_DATA);
   }
 
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index 8ae1b47..3e54b152 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -126,7 +126,7 @@
         </div>
         <div class="secondary">[[subLabel_]]</div>
       </div>
-      <cr-button class="action-button cr-button-gap" on-click="onSigninTap_"
+      <cr-button class="action-button cr-button-gap" on-click="onSigninClick_"
           id="signIn"
           disabled="[[shouldDisableSyncButton_(showSetupButtons_,
                   syncStatus.firstSetupInProgress,
@@ -170,7 +170,7 @@
         <cr-icon-button class="icon-arrow-dropdown cr-button-gap"
             hidden="[[!shouldAllowAccountSwitch_(syncStatus.signedIn,
                 syncStatus.domain)]]"
-            on-click="onMenuButtonTap_" id="dropdown-arrow"
+            on-click="onMenuButtonClick_" id="dropdown-arrow"
             aria-label="$i18n{useAnotherAccount}">
         </cr-icon-button>
         <div class="separator"
@@ -178,7 +178,7 @@
                 syncStatus.domain)]]"></div>
 </if>
         <cr-button id="sync-button" class="action-button cr-button-gap"
-            hidden="[[syncStatus.signedIn]]" on-click="onSyncButtonTap_"
+            hidden="[[syncStatus.signedIn]]" on-click="onSyncButtonClick_"
             disabled="[[shouldDisableSyncButton_(showSetupButtons_,
                     syncStatus.firstSetupInProgress,
                     prefs.signin.allowed_on_next_startup.value)]]">
@@ -188,14 +188,14 @@
             class="cr-button-gap"
             hidden="[[!shouldShowTurnOffButton_(syncStatus.signedIn,
                 syncStatus.domain, showSetupButtons_)]]"
-            on-click="onTurnOffButtonTap_"
+            on-click="onTurnOffButtonClick_"
             disabled="[[syncStatus.firstSetupInProgress]]">
           $i18n{turnOffSync}
         </cr-button>
         <cr-button id="sync-error-button" class="action-button cr-button-gap"
             hidden="[[!shouldShowErrorActionButton_(syncStatus,
                 showSetupButtons_)]]"
-            on-click="onErrorButtonTap_"
+            on-click="onErrorButtonClick_"
             disabled="[[syncStatus.firstSetupInProgress]]">
           [[syncStatus.statusActionText]]
         </cr-button>
@@ -216,19 +216,19 @@
         <cr-action-menu id="menu" auto-reposition
             role-description="$i18n{menu}">
           <template is="dom-repeat" items="[[storedAccounts_]]">
-            <button class="dropdown-item" on-click="onAccountTap_">
+            <button class="dropdown-item" on-click="onAccountClick_">
               <img class="account-icon small" alt=""
                   src="[[getAccountImageSrc_(item.avatarImage)]]">
               <span>[[item.email]]</span>
             </button>
           </template>
-          <button class="dropdown-item" on-click="onSigninTap_"
+          <button class="dropdown-item" on-click="onSigninClick_"
               disabled="[[syncStatus.firstSetupInProgress]]" id="sign-in-item">
             <img class="account-icon small" alt=""
                 src="chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE">
             <span>$i18n{useAnotherAccount}</span>
           </button>
-          <button class="dropdown-item" on-click="onSignoutTap_"
+          <button class="dropdown-item" on-click="onSignoutClick_"
               disabled="[[syncStatus.firstSetupInProgress]]" id="sign-out-item">
             <iron-icon icon="settings:exit-to-app"></iron-icon>
             <span>$i18n{peopleSignOut}</span>
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.ts b/chrome/browser/resources/settings/people_page/sync_account_control.ts
index 4f253377..4fce9e7 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.ts
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.ts
@@ -349,7 +349,7 @@
     return this.syncStatus.signedIn || this.storedAccounts_.length > 0;
   }
 
-  private onErrorButtonTap_() {
+  private onErrorButtonClick_() {
     const router = Router.getInstance();
     const routes = router.getRoutes();
     switch (this.syncStatus.statusAction) {
@@ -371,7 +371,7 @@
     }
   }
 
-  private onSigninTap_() {
+  private onSigninClick_() {
     // <if expr="not chromeos_ash">
     this.syncBrowserProxy_.startSignIn();
     // </if>
@@ -387,13 +387,13 @@
   }
 
   // <if expr="not chromeos_ash">
-  private onSignoutTap_() {
+  private onSignoutClick_() {
     this.syncBrowserProxy_.signOut(false /* deleteProfile */);
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
   }
   // </if>
 
-  private onSyncButtonTap_() {
+  private onSyncButtonClick_() {
     assert(this.shownAccount_);
     assert(this.storedAccounts_.length > 0);
     const isDefaultPromoAccount =
@@ -403,13 +403,13 @@
         this.shownAccount_!.email, isDefaultPromoAccount);
   }
 
-  private onTurnOffButtonTap_() {
+  private onTurnOffButtonClick_() {
     /* This will route to people_page's disconnect dialog. */
     const router = Router.getInstance();
     router.navigateTo(router.getRoutes().SIGN_OUT);
   }
 
-  private onMenuButtonTap_() {
+  private onMenuButtonClick_() {
     const actionMenu = this.shadowRoot!.querySelector('cr-action-menu');
     assert(actionMenu);
     const anchor =
@@ -427,7 +427,7 @@
     }
   }
 
-  private onAccountTap_(e: DomRepeatEvent<StoredAccount>) {
+  private onAccountClick_(e: DomRepeatEvent<StoredAccount>) {
     this.shownAccount_ = e.model.item;
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
   }
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index a8a12a4e2..1268b16 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -109,9 +109,9 @@
               value="{{existingPassphrase_}}"
               placeholder="$i18n{passphrasePlaceholder}"
               error-message="$i18n{incorrectPassphraseError}"
-              on-keypress="onSubmitExistingPassphraseTap_">
+              on-keypress="onSubmitExistingPassphraseClick_">
             <cr-button id="submitExistingPassphrase" slot="suffix"
-                on-click="onSubmitExistingPassphraseTap_"
+                on-click="onSubmitExistingPassphraseClick_"
                 class="action-button" disabled="[[!existingPassphrase_]]">
               $i18n{submitPassphraseButton}
             </cr-button>
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts
index 6d2993e2..2bd71cf9 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.ts
+++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -548,7 +548,7 @@
   /**
    * Sends the user-entered existing password to re-enable sync.
    */
-  private onSubmitExistingPassphraseTap_(e: KeyboardEvent) {
+  private onSubmitExistingPassphraseClick_(e: KeyboardEvent) {
     if (e.type === 'keypress' && e.key !== 'Enter') {
       return;
     }
@@ -609,7 +609,7 @@
     }
   }
 
-  private onLearnMoreTap_(event: Event) {
+  private onLearnMoreClick_(event: Event) {
     if ((event.target as HTMLElement).tagName === 'A') {
       // Stop the propagation of events, so that clicking on links inside
       // checkboxes or radio buttons won't change the value.
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.html b/chrome/browser/resources/settings/privacy_page/personalization_options.html
index d6b719f..da66e3d 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.html
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.html
@@ -44,7 +44,7 @@
         sub-label="$i18n{enablePersonalizationLoggingDesc}" no-set-pref
         on-settings-boolean-control-change="onMetricsReportingChange_">
       <template is="dom-if" if="[[showRestart_]]" restamp>
-        <cr-button on-click="onRestartTap_" id="restart"
+        <cr-button on-click="onRestartClick_" id="restart"
             slot="more-actions">
           $i18n{restart}
         </cr-button>
@@ -118,7 +118,7 @@
 <if expr="not chromeos_ash">
     <cr-toast id="toast">
       <div>$i18n{restartToApplyChanges}</div>
-      <cr-button on-click="onRestartTap_">$i18n{restart}</cr-button>
+      <cr-button on-click="onRestartClick_">$i18n{restart}</cr-button>
     </cr-toast>
 
     <template is="dom-if" if="[[shouldShowRelaunchDialog]]" restamp>
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.ts b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
index e840bbf..7a189b37 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.ts
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
@@ -274,7 +274,7 @@
     this.showSignoutDialog_ = false;
   }
 
-  private onRestartTap_(e: Event) {
+  private onRestartClick_(e: Event) {
     e.stopPropagation();
     this.performRestart(RestartType.RESTART);
   }
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index e1312ec2..1afe9ff 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -38,7 +38,7 @@
             start-icon="cr:delete"
             label="$i18n{clearBrowsingData}"
             sub-label="$i18n{clearBrowsingDataDescription}"
-            on-click="onClearBrowsingDataTap_"></cr-link-row>
+            on-click="onClearBrowsingDataClick_"></cr-link-row>
         <template is="dom-if" if="[[isPrivacyGuideAvailable]]">
           <cr-link-row id="privacyGuideLinkRow" class="hr"
               start-icon="settings20:wind-rose"
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
index bb3079d..fce931b 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -349,7 +349,7 @@
     this.browserProxy_.setBlockAutoplayEnabled(target.checked);
   }
 
-  private onClearBrowsingDataTap_() {
+  private onClearBrowsingDataClick_() {
     this.interactedWithPage_();
 
     Router.getInstance().navigateTo(routes.CLEAR_BROWSER_DATA);
diff --git a/chrome/browser/resources/settings/reset_page/reset_page.ts b/chrome/browser/resources/settings/reset_page/reset_page.ts
index 8ea601a..1c768f77 100644
--- a/chrome/browser/resources/settings/reset_page/reset_page.ts
+++ b/chrome/browser/resources/settings/reset_page/reset_page.ts
@@ -98,11 +98,11 @@
   }
 
   // <if expr="_google_chrome and is_win">
-  private onChromeCleanupTap_() {
+  private onChromeCleanupClick_() {
     Router.getInstance().navigateTo(routes.CHROME_CLEANUP);
   }
 
-  private onIncompatibleApplicationsTap_() {
+  private onIncompatibleApplicationsClick_() {
     Router.getInstance().navigateTo(routes.INCOMPATIBLE_APPLICATIONS);
   }
   // </if>
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_banner.html b/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
index c3364ed..5b2b25b 100644
--- a/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
+++ b/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
@@ -12,10 +12,10 @@
         </span>
       </div>
       <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onOkTap_" id="ok">
+        <cr-button class="cancel-button" on-click="onOkClick_" id="ok">
           $i18n{ok}
         </cr-button>
-        <cr-button class="action-button" on-click="onResetTap_" id="reset">
+        <cr-button class="action-button" on-click="onResetClick_" id="reset">
           $i18n{resetProfileBannerButton}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_banner.ts b/chrome/browser/resources/settings/reset_page/reset_profile_banner.ts
index 3c8e3ca30..c5819e9 100644
--- a/chrome/browser/resources/settings/reset_page/reset_profile_banner.ts
+++ b/chrome/browser/resources/settings/reset_page/reset_profile_banner.ts
@@ -42,7 +42,7 @@
     this.$.dialog.showModal();
   }
 
-  private onOkTap_() {
+  private onOkClick_() {
     this.$.dialog.cancel();
   }
 
@@ -50,7 +50,7 @@
     ResetBrowserProxyImpl.getInstance().onHideResetProfileBanner();
   }
 
-  private onResetTap_() {
+  private onResetClick_() {
     this.$.dialog.close();
     Router.getInstance().navigateTo(routes.RESET_DIALOG);
   }
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
index d23e2db..be0cc51 100644
--- a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
+++ b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
@@ -24,11 +24,11 @@
       <div slot="button-container">
         <paper-spinner-lite id="resetSpinner" active="[[clearingInProgress_]]">
         </paper-spinner-lite>
-        <cr-button class="cancel-button" on-click="onCancelTap_"
+        <cr-button class="cancel-button" on-click="onCancelClick_"
             id="cancel" disabled="[[clearingInProgress_]]">
           $i18n{cancel}
         </cr-button>
-        <cr-button class="action-button" on-click="onResetTap_"
+        <cr-button class="action-button" on-click="onResetClick_"
             id="reset" disabled="[[clearingInProgress_]]">
           $i18n{resetDialogCommit}
         </cr-button>
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.ts b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.ts
index c8a8f346..698e592 100644
--- a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.ts
+++ b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.ts
@@ -117,7 +117,7 @@
     });
 
     this.shadowRoot!.querySelector('cr-checkbox a')!.addEventListener(
-        'click', this.onShowReportedSettingsTap_.bind(this));
+        'click', this.onShowReportedSettingsClick_.bind(this));
   }
 
   private showDialog_() {
@@ -148,7 +148,7 @@
     }
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     this.cancel();
   }
 
@@ -158,7 +158,7 @@
     }
   }
 
-  private onResetTap_() {
+  private onResetClick_() {
     this.clearingInProgress_ = true;
     this.browserProxy_
         .performResetProfileSettings(
@@ -176,7 +176,7 @@
   /**
    * Displays the settings that will be reported in a new tab.
    */
-  private onShowReportedSettingsTap_(e: Event) {
+  private onShowReportedSettingsClick_(e: Event) {
     this.browserProxy_.showReportedSettings();
     e.stopPropagation();
   }
diff --git a/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html b/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
index 8639bf2..63d2d2e 100644
--- a/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
+++ b/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
@@ -16,15 +16,15 @@
         <span>[[engine.displayName]]</span>
       </div>
       <div class="keyword-column">[[engine.keyword]]</div>
-      <cr-icon-button class="icon-more-vert" on-click="onDotsTap_"
+      <cr-icon-button class="icon-more-vert" on-click="onDotsClick_"
           title="$i18n{moreActions}" focus-row-control focus-type="menu">
       </cr-icon-button>
       <cr-action-menu role-description="$i18n{menu}">
-        <button class="dropdown-item" on-click="onManageTap_"
+        <button class="dropdown-item" on-click="onManageClick_"
             id="manage">
           $i18n{searchEnginesManageExtension}
         </button>
-        <button class="dropdown-item" on-click="onDisableTap_"
+        <button class="dropdown-item" on-click="onDisableClick_"
             id="disable">
           $i18n{disable}
         </button>
diff --git a/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.ts b/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.ts
index b4d883c1..66e839d1 100644
--- a/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.ts
+++ b/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.ts
@@ -51,12 +51,12 @@
   private browserProxy_: ExtensionControlBrowserProxy =
       ExtensionControlBrowserProxyImpl.getInstance();
 
-  private onManageTap_() {
+  private onManageClick_() {
     this.closePopupMenu_();
     this.browserProxy_.manageExtension(this.engine.extension!.id);
   }
 
-  private onDisableTap_() {
+  private onDisableClick_() {
     this.closePopupMenu_();
     this.browserProxy_.disableExtension(this.engine.extension!.id);
   }
@@ -65,7 +65,7 @@
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
   }
 
-  private onDotsTap_() {
+  private onDotsClick_() {
     const dots = this.shadowRoot!.querySelector('cr-icon-button');
     assert(dots);
     this.shadowRoot!.querySelector('cr-action-menu')!.showAt(dots, {
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.html b/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.html
index c32c4f0..dc8f190 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.html
@@ -25,7 +25,7 @@
         <cr-button class="cancel-button" on-click="cancel_" id="cancel">
             $i18n{cancel}</cr-button>
         <cr-button id="actionButton" class="action-button"
-            on-click="onActionButtonTap_">
+            on-click="onActionButtonClick_">
           [[actionButtonText_]]
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.ts b/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.ts
index ceaee2a..1e8f5e0 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.ts
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_edit_dialog.ts
@@ -132,7 +132,7 @@
     this.$.dialog.cancel();
   }
 
-  private onActionButtonTap_() {
+  private onActionButtonClick_() {
     this.browserProxy_.searchEngineEditCompleted(
         this.searchEngine_, this.keyword_, this.queryUrl_);
     this.$.dialog.close();
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
index e460cba..256d145 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
@@ -37,27 +37,27 @@
         <div>[[engine.url]]</div>
       </span>
       <span role="cell">
-        <cr-button class="secondary-button" on-click="onActivateTap_"
+        <cr-button class="secondary-button" on-click="onActivateClick_"
             hidden="[[!engine.canBeActivated]]" id="activate">
           $i18n{searchEnginesActivate}
         </cr-button>
-        <cr-icon-button class="icon-edit" on-click="onEditTap_"
+        <cr-icon-button class="icon-edit" on-click="onEditClick_"
             title="$i18n{edit}" hidden="[[engine.canBeActivated]]"
             disabled$="[[!engine.canBeEdited]]" id="editIconButton">
         </cr-icon-button>
-        <cr-icon-button class="icon-more-vert" on-click="onDotsTap_"
+        <cr-icon-button class="icon-more-vert" on-click="onDotsClick_"
             disabled$="[[engine.default]]" title="$i18n{moreActions}">
         </cr-icon-button>
         <cr-action-menu role-description="$i18n{menu}">
-          <button class="dropdown-item" on-click="onMakeDefaultTap_"
+          <button class="dropdown-item" on-click="onMakeDefaultClick_"
               disabled$="[[!engine.canBeDefault]]" id="makeDefault">
             $i18n{searchEnginesMakeDefault}
           </button>
-          <button class="dropdown-item" on-click="onDeactivateTap_"
+          <button class="dropdown-item" on-click="onDeactivateClick_"
               hidden="[[!engine.canBeDeactivated]]" id="deactivate">
             $i18n{searchEnginesDeactivate}
           </button>
-          <button class="dropdown-item" on-click="onDeleteTap_"
+          <button class="dropdown-item" on-click="onDeleteClick_"
               hidden="[[!engine.canBeRemoved]]" id="delete">
             $i18n{delete}
           </button>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.ts b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.ts
index 0146311e..bc11042 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.ts
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.ts
@@ -70,7 +70,7 @@
     return this.engine.default;
   }
 
-  private onDeleteTap_(e: Event) {
+  private onDeleteClick_(e: Event) {
     e.preventDefault();
     this.closePopupMenu_();
 
@@ -93,7 +93,7 @@
     }));
   }
 
-  private onDotsTap_() {
+  private onDotsClick_() {
     const dots = this.shadowRoot!.querySelector<HTMLElement>(
         'cr-icon-button.icon-more-vert');
     assert(dots);
@@ -102,7 +102,7 @@
     });
   }
 
-  private onEditTap_(e: Event) {
+  private onEditClick_(e: Event) {
     e.preventDefault();
     this.closePopupMenu_();
     const anchor = this.shadowRoot!.querySelector('cr-icon-button');
@@ -117,18 +117,18 @@
     }));
   }
 
-  private onMakeDefaultTap_() {
+  private onMakeDefaultClick_() {
     this.closePopupMenu_();
     this.browserProxy_.setDefaultSearchEngine(this.engine.modelIndex);
   }
 
-  private onActivateTap_() {
+  private onActivateClick_() {
     this.closePopupMenu_();
     this.browserProxy_.setIsActiveSearchEngine(
         this.engine.modelIndex, /*is_active=*/ true);
   }
 
-  private onDeactivateTap_() {
+  private onDeactivateClick_() {
     this.closePopupMenu_();
     this.browserProxy_.setIsActiveSearchEngine(
         this.engine.modelIndex, /*is_active=*/ false);
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
index 2c97cda..aa5cee27 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -74,7 +74,7 @@
           <div class="secondary">$i18n{searchEnginesSiteSearchExplanation}</div>
         </div>
         <cr-button class="secondary-button header-aligned-button"
-                   on-click="onAddSearchEngineTap_" id="addSearchEngine">
+                   on-click="onAddSearchEngineClick_" id="addSearchEngine">
           $i18n{add}
         </cr-button>
     </div>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.ts b/chrome/browser/resources/settings/search_engines_page/search_engines_page.ts
index e7cafb9..68629886 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.ts
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.ts
@@ -248,7 +248,7 @@
     this.extensions = searchEnginesInfo.extensions;
   }
 
-  private onAddSearchEngineTap_(e: Event) {
+  private onAddSearchEngineClick_(e: Event) {
     e.preventDefault();
     this.openEditDialog_(
         null, this.shadowRoot!.querySelector('#addSearchEngine')!);
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index c121054..6bcf2cf1 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -52,7 +52,7 @@
     <!-- Manage search engines -->
     <cr-link-row class="hr" id="enginesSubpageTrigger"
         label="$i18n{searchEnginesManageSiteSearch}"
-        on-click="onManageSearchEnginesTap_"
+        on-click="onManageSearchEnginesClick_"
         role-description="$i18n{subpageArrowRoleDescription}"></cr-link-row>
   </div>
   <template is="dom-if" route-path="/searchEngines">
diff --git a/chrome/browser/resources/settings/search_page/search_page.ts b/chrome/browser/resources/settings/search_page/search_page.ts
index 1bb7fde..1276048 100644
--- a/chrome/browser/resources/settings/search_page/search_page.ts
+++ b/chrome/browser/resources/settings/search_page/search_page.ts
@@ -99,7 +99,7 @@
     }));
   }
 
-  private onManageSearchEnginesTap_() {
+  private onManageSearchEnginesClick_() {
     Router.getInstance().navigateTo(routes.SEARCH_ENGINES);
   }
 
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 950cff9..c8dcbca 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -82,7 +82,7 @@
         clear-label="$i18n{clearSearch}"
         autofocus
         search-prompt="$i18n{searchPrompt}"
-        on-cr-toolbar-menu-tap="onMenuButtonTap_"
+        on-cr-toolbar-menu-tap="onMenuButtonClick_"
         spinner-active="[[toolbarSpinnerActive_]]"
         menu-label="$i18n{menuButtonLabel}"
         on-search-changed="onSearchChanged_"
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.ts b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
index e9b339e..5347751 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.ts
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
@@ -260,7 +260,7 @@
     this.$.drawer.close();
   }
 
-  private onMenuButtonTap_() {
+  private onMenuButtonClick_() {
     this.$.drawer.toggle();
   }
 
diff --git a/chrome/browser/resources/settings/site_settings/add_site_dialog.html b/chrome/browser/resources/settings/site_settings/add_site_dialog.html
index 4b2c630b..3cd8861 100644
--- a/chrome/browser/resources/settings/site_settings/add_site_dialog.html
+++ b/chrome/browser/resources/settings/site_settings/add_site_dialog.html
@@ -23,7 +23,7 @@
         </cr-checkbox>
       </div>
       <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelTap_">
+        <cr-button class="cancel-button" on-click="onCancelClick_">
           $i18n{cancel}
         </cr-button>
         <cr-button class="action-button" id="add" on-click="onSubmit_"
diff --git a/chrome/browser/resources/settings/site_settings/add_site_dialog.ts b/chrome/browser/resources/settings/site_settings/add_site_dialog.ts
index f001d98..54de51a4 100644
--- a/chrome/browser/resources/settings/site_settings/add_site_dialog.ts
+++ b/chrome/browser/resources/settings/site_settings/add_site_dialog.ts
@@ -120,7 +120,7 @@
         });
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     this.$.dialog.cancel();
   }
 
diff --git a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
index 0055a65..8436848 100644
--- a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
+++ b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
@@ -9,10 +9,10 @@
         </cr-input>
       </div>
       <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelTap_"
+        <cr-button class="cancel-button" on-click="onCancelClick_"
             id="cancel">$i18n{cancel}</cr-button>
         <cr-button id="actionButton" class="action-button"
-            on-click="onActionButtonTap_" disabled="[[invalid_]]">
+            on-click="onActionButtonClick_" disabled="[[invalid_]]">
           $i18n{save}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.ts b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.ts
index 219b09a..b40cc4a 100644
--- a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.ts
+++ b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.ts
@@ -73,11 +73,11 @@
     this.$.dialog.showModal();
   }
 
-  private onCancelTap_() {
+  private onCancelClick_() {
     this.$.dialog.close();
   }
 
-  private onActionButtonTap_() {
+  private onActionButtonClick_() {
     if (this.model.origin !== this.origin_) {
       // The way to "edit" an exception is to remove it and and a new one.
       this.browserProxy_.resetCategoryPermissionForPattern(
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.html b/chrome/browser/resources/settings/site_settings/site_entry.html
index 091f67a..ba7afd4e 100644
--- a/chrome/browser/resources/settings/site_settings/site_entry.html
+++ b/chrome/browser/resources/settings/site_settings/site_entry.html
@@ -45,7 +45,7 @@
     <div id="collapseParent" focus-row-container>
       <div class$="list-item [[getClassForIndex_(listIndex)]]">
         <div id="toggleButton" class="start row-aligned two-line text-elide"
-            on-click="onSiteEntryTap_" actionable aria-expanded="false">
+            on-click="onSiteEntryClick_" actionable aria-expanded="false">
           <site-favicon url="[[getSiteGroupIcon_(siteGroup)]]"></site-favicon>
           <div class="middle text-elide" id="displayName">
             <div class="site-representation">
@@ -125,7 +125,7 @@
               <template is="dom-repeat" items="[[siteGroup.origins]]">
                 <div class="list-item hr">
                   <div class="start row-aligned list-item origin-link"
-                       on-click="onOriginTap_"
+                       on-click="onOriginClick_"
                        actionable$="[[!item.isPartitioned]]">
                     <site-favicon url="[[item.origin]]"></site-favicon>
                     <div class="site-representation middle text-elide">
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.ts b/chrome/browser/resources/settings/site_settings/site_entry.ts
index f745001..4dd7105 100644
--- a/chrome/browser/resources/settings/site_settings/site_entry.ts
+++ b/chrome/browser/resources/settings/site_settings/site_entry.ts
@@ -445,7 +445,7 @@
   /**
    * A handler for selecting a site (by clicking on the origin).
    */
-  private onOriginTap_(e: DomRepeatEvent<OriginInfo>) {
+  private onOriginClick_(e: DomRepeatEvent<OriginInfo>) {
     if (this.siteGroup.origins[e.model.index].isPartitioned) {
       return;
     }
@@ -458,7 +458,7 @@
    * A handler for clicking on a site-entry heading. This will either show a
    * list of origins or directly navigates to Site Details if there is only one.
    */
-  private onSiteEntryTap_() {
+  private onSiteEntryClick_() {
     // Individual origins don't expand - just go straight to Site Details.
     if (!this.grouped_(this.siteGroup)) {
       this.navigateToSiteDetails_(this.siteGroup.origins[0].origin);
diff --git a/chrome/browser/resources/settings/site_settings/site_list.html b/chrome/browser/resources/settings/site_settings/site_list.html
index c34ddee..00c927a 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.html
+++ b/chrome/browser/resources/settings/site_settings/site_list.html
@@ -6,31 +6,31 @@
           [[categoryHeader]]
         </h2>
         <cr-button id="addSite" class="header-aligned-button"
-            hidden$="[[!showAddSiteButton_]]" on-click="onAddSiteTap_">
+            hidden$="[[!showAddSiteButton_]]" on-click="onAddSiteClick_">
           $i18n{add}
         </cr-button>
       </div>
 
       <cr-action-menu role-description="$i18n{menu}">
         <button class="dropdown-item" id="allow"
-            on-click="onAllowTap_" hidden$="[[!showAllowAction_]]">
+            on-click="onAllowClick_" hidden$="[[!showAllowAction_]]">
           $i18n{siteSettingsActionAllow}
         </button>
         <button class="dropdown-item" id="block"
-            on-click="onBlockTap_" hidden$="[[!showBlockAction_]]">
+            on-click="onBlockClick_" hidden$="[[!showBlockAction_]]">
           $i18n{siteSettingsActionBlock}
         </button>
         <button class="dropdown-item" id="sessionOnly"
-            on-click="onSessionOnlyTap_"
+            on-click="onSessionOnlyClick_"
             hidden$="[[!showSessionOnlyActionForSite_(actionMenuSite_)]]">
           $i18n{siteSettingsActionSessionOnly}
         </button>
         <button class="dropdown-item" id="edit"
-            on-click="onEditTap_">
+            on-click="onEditClick_">
           $i18n{edit}
         </button>
         <button class="dropdown-item" id="reset"
-            on-click="onResetTap_">
+            on-click="onResetClick_">
           $i18n{siteSettingsActionReset}
         </button>
       </cr-action-menu>
diff --git a/chrome/browser/resources/settings/site_settings/site_list.ts b/chrome/browser/resources/settings/site_settings/site_list.ts
index 5483dc7..10d647e 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.ts
+++ b/chrome/browser/resources/settings/site_settings/site_list.ts
@@ -316,7 +316,7 @@
   /**
    * A handler for the Add Site button.
    */
-  private onAddSiteTap_() {
+  private onAddSiteClick_() {
     assert(!this.readOnlyList);
     this.showAddSiteDialog_ = true;
   }
@@ -458,22 +458,22 @@
         this.category, contentSetting, this.actionMenuSite_!.incognito);
   }
 
-  private onAllowTap_() {
+  private onAllowClick_() {
     this.setContentSettingForActionMenuSite_(ContentSetting.ALLOW);
     this.closeActionMenu_();
   }
 
-  private onBlockTap_() {
+  private onBlockClick_() {
     this.setContentSettingForActionMenuSite_(ContentSetting.BLOCK);
     this.closeActionMenu_();
   }
 
-  private onSessionOnlyTap_() {
+  private onSessionOnlyClick_() {
     this.setContentSettingForActionMenuSite_(ContentSetting.SESSION_ONLY);
     this.closeActionMenu_();
   }
 
-  private onEditTap_() {
+  private onEditClick_() {
     // Close action menu without resetting |this.actionMenuSite_| since it is
     // bound to the dialog.
     this.shadowRoot!.querySelector('cr-action-menu')!.close();
@@ -489,7 +489,7 @@
     }
   }
 
-  private onResetTap_() {
+  private onResetClick_() {
     assert(this.actionMenuSite_);
     this.browserProxy.resetCategoryPermissionForPattern(
         this.actionMenuSite_.origin, this.actionMenuSite_.embeddingOrigin,
diff --git a/chrome/browser/resources/settings/site_settings/site_list_entry.html b/chrome/browser/resources/settings/site_settings/site_list_entry.html
index 18cb0a4..3f0b82e0 100644
--- a/chrome/browser/resources/settings/site_settings/site_list_entry.html
+++ b/chrome/browser/resources/settings/site_settings/site_list_entry.html
@@ -19,7 +19,7 @@
     </style>
     <div class="list-item" focus-row-container>
       <div class="settings-row"
-          actionable$="[[allowNavigateToSiteDetail_]]" on-click="onOriginTap_">
+          actionable$="[[allowNavigateToSiteDetail_]]" on-click="onOriginClick_">
         <site-favicon url="[[computeFaviconOrigin_(model)]]"></site-favicon>
         <div class="middle no-min-width">
           <div class="text-elide">
@@ -56,11 +56,11 @@
       </template>
       <cr-icon-button id="resetSite" class="icon-delete-gray"
           hidden="[[shouldHideResetButton_(model, readOnlyList)]]"
-          on-click="onResetButtonTap_"
+          on-click="onResetButtonClick_"
           aria-label="$i18n{siteSettingsActionReset}" focus-row-control
           focus-type="reset"></cr-icon-button>
       <cr-icon-button id="actionMenuButton" class="icon-more-vert"
           hidden="[[shouldHideActionMenu_(model, readOnlyList)]]"
-          on-click="onShowActionMenuTap_" title="$i18n{moreActions}"
+          on-click="onShowActionMenuClick_" title="$i18n{moreActions}"
           focus-row-control focus-type="menu"></cr-icon-button>
     </div>
diff --git a/chrome/browser/resources/settings/site_settings/site_list_entry.ts b/chrome/browser/resources/settings/site_settings/site_list_entry.ts
index 8a3a9de4..35cb968 100644
--- a/chrome/browser/resources/settings/site_settings/site_list_entry.ts
+++ b/chrome/browser/resources/settings/site_settings/site_list_entry.ts
@@ -157,7 +157,7 @@
   /**
    * A handler for selecting a site (by clicking on the origin).
    */
-  private onOriginTap_() {
+  private onOriginClick_() {
     if (!this.allowNavigateToSiteDetail_) {
       return;
     }
@@ -250,7 +250,7 @@
         !!this.model.controlledBy;
   }
 
-  private onResetButtonTap_() {
+  private onResetButtonClick_() {
     // Use the appropriate method to reset a chooser exception.
     if (this.chooserType !== ChooserType.NONE && this.chooserObject !== null) {
       this.browserProxy.resetChooserExceptionForSite(
@@ -263,7 +263,7 @@
         this.model.incognito);
   }
 
-  private onShowActionMenuTap_() {
+  private onShowActionMenuClick_() {
     // Chooser exceptions do not support the action menu, so do nothing.
     if (this.chooserType !== ChooserType.NONE) {
       return;
diff --git a/chrome/browser/resources/settings/system_page/system_page.html b/chrome/browser/resources/settings/system_page/system_page.html
index 392f055b..93ae524 100644
--- a/chrome/browser/resources/settings/system_page/system_page.html
+++ b/chrome/browser/resources/settings/system_page/system_page.html
@@ -12,13 +12,13 @@
         label="$i18n{hardwareAccelerationLabel}">
       <template is="dom-if" if="[[shouldShowRestart_(
           prefs.hardware_acceleration_mode.enabled.value)]]">
-        <cr-button on-click="onRestartTap_" slot="more-actions">
+        <cr-button on-click="onRestartClick_" slot="more-actions">
           $i18n{restart}
         </cr-button>
       </template>
     </settings-toggle-button>
 
-    <div id="proxy" class="cr-row" on-click="onProxyTap_"
+    <div id="proxy" class="cr-row" on-click="onProxyClick_"
         actionable$="[[isProxyDefault_]]">
       <div class="flex cr-row-text" hidden$="[[!isProxyDefault_]]">
         $i18n{proxySettingsLabel}
diff --git a/chrome/browser/resources/settings/system_page/system_page.ts b/chrome/browser/resources/settings/system_page/system_page.ts
index 24bcbdada..4715f21 100644
--- a/chrome/browser/resources/settings/system_page/system_page.ts
+++ b/chrome/browser/resources/settings/system_page/system_page.ts
@@ -99,13 +99,13 @@
         'refresh-pref', {bubbles: true, composed: true, detail: 'proxy'}));
   }
 
-  private onProxyTap_() {
+  private onProxyClick_() {
     if (this.isProxyDefault_) {
       SystemPageBrowserProxyImpl.getInstance().showProxySettings();
     }
   }
 
-  private onRestartTap_(e: Event) {
+  private onRestartClick_(e: Event) {
     // Prevent event from bubbling up to the toggle button.
     e.stopPropagation();
     this.performRestart(RestartType.RESTART);
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
index 970a3d467..63c383152 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -110,11 +110,8 @@
 
       labels_: {
         type: Array,
-        value: () => [{
-          label: loadTimeData.getString('priceTrackingLabel'),
-          icon: 'bookmarks:price-tracking',
-          active: false,
-        }],
+        value: () => [],
+        computed: 'computePriceTrackingLabel_(trackedProductInfos_.*)',
       },
 
       activeSortIndex_: {
@@ -371,6 +368,23 @@
     this.set(`trackedProductInfos_.${bookmarkId}`, null);
   }
 
+  // TODO(emshack): Once there is more than one bookmark power, remove this
+  // logic and always display the price tracking label button.
+  private computePriceTrackingLabel_() {
+    const showLabel =
+        Object.keys(this.trackedProductInfos_)
+            .some(key => this.get(`trackedProductInfos_.${key}`) !== null);
+    if (showLabel) {
+      return [{
+        label: loadTimeData.getString('priceTrackingLabel'),
+        icon: 'bookmarks:price-tracking',
+        active: false,
+      }];
+    } else {
+      return [];
+    }
+  }
+
   /**
    * Returns the index of the given node id in the currently shown bookmarks,
    * or -1 if not shown.
diff --git a/chrome/browser/thumbnail/cc/thumbnail_cache.cc b/chrome/browser/thumbnail/cc/thumbnail_cache.cc
index 3c863ff..a814ae4 100644
--- a/chrome/browser/thumbnail/cc/thumbnail_cache.cc
+++ b/chrome/browser/thumbnail/cc/thumbnail_cache.cc
@@ -234,8 +234,12 @@
   thumbnail->SetBitmap(bitmap);
 
   RemoveFromReadQueue(tab_id);
-  MakeSpaceForNewItemIfNecessary(tab_id);
-  cache_.Put(tab_id, std::move(thumbnail));
+  if (!base::FeatureList::IsEnabled(kThumbnailCacheRefactor) ||
+      base::Contains(visible_ids_, tab_id)) {
+    MakeSpaceForNewItemIfNecessary(tab_id);
+    cache_.Put(tab_id, std::move(thumbnail));
+    NotifyObserversOfThumbnailAddedToCache(tab_id);
+  }
 
   if (use_approximation_thumbnail_) {
     std::pair<SkBitmap, float> approximation =
@@ -324,7 +328,7 @@
   return true;
 }
 
-void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority,
+void ThumbnailCache::UpdateVisibleIds(const std::vector<TabId>& priority,
                                       TabId primary_tab_id) {
   bool needs_update = false;
   if (primary_tab_id_ != primary_tab_id) {
@@ -885,6 +889,7 @@
     if (base::android::ApplicationStatusListener::GetState() ==
         base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
       thumbnail->CreateUIResource();
+      NotifyObserversOfThumbnailAddedToCache(tab_id);
     }
   }
   WriteThumbnailIfNecessary(tab_id, std::move(compressed_data), scale,
@@ -1058,21 +1063,32 @@
       time_stamp = meta_iter->second.capture_time();
     }
 
-    MakeSpaceForNewItemIfNecessary(tab_id);
-    std::unique_ptr<Thumbnail> thumbnail = Thumbnail::Create(
-        tab_id, time_stamp, scale, ui_resource_provider_, this);
-    thumbnail->SetCompressedBitmap(std::move(compressed_data), content_size);
-    if (kPreferCPUMemory) {
-      thumbnail->CreateUIResource();
-    }
+    if (!base::FeatureList::IsEnabled(kThumbnailCacheRefactor) ||
+        (base::FeatureList::IsEnabled(kThumbnailCacheRefactor) &&
+         base::Contains(visible_ids_, tab_id))) {
+      MakeSpaceForNewItemIfNecessary(tab_id);
+      std::unique_ptr<Thumbnail> thumbnail = Thumbnail::Create(
+          tab_id, time_stamp, scale, ui_resource_provider_, this);
+      thumbnail->SetCompressedBitmap(std::move(compressed_data), content_size);
+      if (kPreferCPUMemory) {
+        thumbnail->CreateUIResource();
+      }
 
-    cache_.Put(tab_id, std::move(thumbnail));
-    NotifyObserversOfThumbnailRead(tab_id);
+      cache_.Put(tab_id, std::move(thumbnail));
+      NotifyObserversOfThumbnailAddedToCache(tab_id);
+      NotifyObserversOfThumbnailRead(tab_id);
+    }
   }
 
   ReadNextThumbnail();
 }
 
+void ThumbnailCache::NotifyObserversOfThumbnailAddedToCache(TabId tab_id) {
+  for (ThumbnailCacheObserver& observer : observers_) {
+    observer.OnThumbnailAddedToCache(tab_id);
+  }
+}
+
 void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) {
   for (ThumbnailCacheObserver& observer : observers_) {
     observer.OnFinishedThumbnailRead(tab_id);
diff --git a/chrome/browser/thumbnail/cc/thumbnail_cache.h b/chrome/browser/thumbnail/cc/thumbnail_cache.h
index 9e35fb9..d61b982 100644
--- a/chrome/browser/thumbnail/cc/thumbnail_cache.h
+++ b/chrome/browser/thumbnail/cc/thumbnail_cache.h
@@ -10,6 +10,7 @@
 #include <list>
 #include <map>
 #include <set>
+#include <vector>
 
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
@@ -40,6 +41,7 @@
 
 class ThumbnailCacheObserver {
  public:
+  virtual void OnThumbnailAddedToCache(TabId tab_id) = 0;
   virtual void OnFinishedThumbnailRead(TabId tab_id) = 0;
 };
 
@@ -73,7 +75,8 @@
 
   void InvalidateThumbnailIfChanged(TabId tab_id, const GURL& url);
   bool CheckAndUpdateThumbnailMetaData(TabId tab_id, const GURL& url);
-  void UpdateVisibleIds(const TabIdList& priority, TabId primary_tab_id);
+  void UpdateVisibleIds(const std::vector<TabId>& priority,
+                        TabId primary_tab_id);
   void DecompressThumbnailFromFile(
       TabId tab_id,
       double jpeg_aspect_ratio,
@@ -176,6 +179,7 @@
                     sk_sp<SkPixelRef> compressed_data,
                     float scale,
                     const gfx::Size& content_size);
+  void NotifyObserversOfThumbnailAddedToCache(TabId tab_id);
   void NotifyObserversOfThumbnailRead(TabId tab_id);
   void RemoveOnMatchedTimeStamp(TabId tab_id, const base::Time& time_stamp);
   static std::pair<SkBitmap, float> CreateApproximation(const SkBitmap& bitmap,
diff --git a/chrome/browser/thumbnail/cc/thumbnail_cache_unittest.cc b/chrome/browser/thumbnail/cc/thumbnail_cache_unittest.cc
index be73747..c50f42c4 100644
--- a/chrome/browser/thumbnail/cc/thumbnail_cache_unittest.cc
+++ b/chrome/browser/thumbnail/cc/thumbnail_cache_unittest.cc
@@ -86,6 +86,8 @@
   ASSERT_TRUE(bitmap.tryAllocN32Pixels(kDimension * kKiB, kDimension));
   bitmap.setImmutable();
 
+  thumbnail_cache().UpdateVisibleIds(std::vector<TabId>({kTabId1, kTabId2}),
+                                     -1);
   EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
       kTabId1, GURL("https://www.foo.com/")));
   thumbnail_cache().Put(kTabId1, bitmap,
@@ -131,6 +133,7 @@
   constexpr int kDimension = 4;
   ASSERT_TRUE(bitmap.tryAllocN32Pixels(kDimension * kKiB, kDimension));
   bitmap.setImmutable();
+  thumbnail_cache().UpdateVisibleIds(std::vector<TabId>({kTabId}), -1);
   EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
       kTabId, GURL("https://www.foo.com/")));
   thumbnail_cache().Put(kTabId, bitmap,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3470da1..4896557 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -4612,6 +4612,8 @@
       "views/device_chooser_content_view.h",
       "views/devtools_process_observer.cc",
       "views/devtools_process_observer.h",
+      "views/download/bubble/download_bubble_partial_view.cc",
+      "views/download/bubble/download_bubble_partial_view.h",
       "views/download/bubble/download_bubble_row_list_view.cc",
       "views/download/bubble/download_bubble_row_list_view.h",
       "views/download/bubble/download_bubble_row_view.cc",
@@ -5628,8 +5630,12 @@
         sources += [
           "views/chrome_browser_main_extra_parts_views_linux.cc",
           "views/chrome_browser_main_extra_parts_views_linux.h",
+          "views/dark_mode_manager_linux.cc",
+          "views/dark_mode_manager_linux.h",
         ]
         deps += [
+          "//components/dbus/thread_linux",
+          "//dbus",
           "//ui/base/cursor",
           "//ui/ozone",
         ]
diff --git a/chrome/browser/ui/DEPS b/chrome/browser/ui/DEPS
index fc3fab23..b56a704e 100644
--- a/chrome/browser/ui/DEPS
+++ b/chrome/browser/ui/DEPS
@@ -42,6 +42,9 @@
   "browser_navigator_browsertest\.cc": [
     "+ash/shell.h",
   ],
+  "dark_mode_manager_linux\.cc": [
+    "+dbus",
+  ],
   "fullscreen_controller_interactive_browsertest\.cc": [
     "+ash/shell.h",
   ],
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index c106787..2ff9eaa 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -59,12 +59,27 @@
     private static boolean sFocusableDrawableStateInNightMode;
     private final ViewBinder<PropertyModel, T, PropertyKey> mContentBinder;
 
+    private static boolean sDimensionsInitialized;
+    private static int sIconWidthPx;
+    private static int sPaddingStart;
+    private static int sPaddingStartLargeIcon;
+    private static int sPaddingEnd;
+    private static int sPaddingEndLargeIcon;
+    private static int sEdgeSize;
+    private static int sEdgeSizeLargeIcon;
+    private static int sSideSpacing;
+
     public BaseSuggestionViewBinder(ViewBinder<PropertyModel, T, PropertyKey> contentBinder) {
         mContentBinder = contentBinder;
     }
 
     @Override
     public void bind(PropertyModel model, BaseSuggestionView<T> view, PropertyKey propertyKey) {
+        if (!sDimensionsInitialized) {
+            initializeDimensions(view.getContext());
+            sDimensionsInitialized = true;
+        }
+
         mContentBinder.bind(model, view.getContentView(), propertyKey);
         ActionChipsBinder.bind(model, view.getActionChipsView(), propertyKey);
 
@@ -176,29 +191,13 @@
         final SuggestionDrawableState sds = model.get(BaseSuggestionViewProperties.ICON);
 
         if (sds != null) {
-            final Resources res = rciv.getContext().getResources();
-            boolean showModernizeVisualUpdate =
-                    OmniboxFeatures.shouldShowModernizeVisualUpdate(rciv.getContext());
-            int iconWidthPx = res.getDimensionPixelSize(showModernizeVisualUpdate
-                            ? R.dimen.omnibox_suggestion_icon_area_size_modern
-                            : R.dimen.omnibox_suggestion_icon_area_size);
-
-            rciv.setLayoutParams(new SuggestionLayout.LayoutParams(iconWidthPx,
+            rciv.setLayoutParams(new SuggestionLayout.LayoutParams(sIconWidthPx,
                     ViewGroup.LayoutParams.WRAP_CONTENT,
                     SuggestionLayout.LayoutParams.SuggestionViewType.DECORATION));
 
-            final int paddingStart = res.getDimensionPixelSize(sds.isLarge
-                            ? R.dimen.omnibox_suggestion_36dp_icon_margin_start
-                            : showModernizeVisualUpdate
-                            ? R.dimen.omnibox_suggestion_24dp_icon_margin_start_modern
-                            : R.dimen.omnibox_suggestion_24dp_icon_margin_start);
-            final int paddingEnd = res.getDimensionPixelSize(sds.isLarge
-                            ? R.dimen.omnibox_suggestion_36dp_icon_margin_end
-                            : R.dimen.omnibox_suggestion_24dp_icon_margin_end);
-            final int edgeSize = res.getDimensionPixelSize(sds.isLarge
-                            ? R.dimen.omnibox_suggestion_36dp_icon_size
-                            : R.dimen.omnibox_suggestion_24dp_icon_size);
-
+            int paddingStart = sds.isLarge ? sPaddingStartLargeIcon : sPaddingStart;
+            int paddingEnd = sds.isLarge ? sPaddingEndLargeIcon : sPaddingEnd;
+            int edgeSize = sds.isLarge ? sEdgeSizeLargeIcon : sEdgeSize;
             rciv.setPadding(paddingStart, 0, paddingEnd, 0);
             rciv.setMinimumHeight(edgeSize);
             rciv.setClipToOutline(sds.useRoundedCorners);
@@ -311,10 +310,8 @@
         if (layoutParams instanceof MarginLayoutParams) {
             int topSpacing = model.get(DropdownCommonProperties.TOP_MARGIN);
             int bottomSpacing = model.get(DropdownCommonProperties.BOTTOM_MARGIN);
-            int sideSpacing = view.getContext().getResources().getDimensionPixelOffset(
-                    R.dimen.omnibox_suggestion_side_spacing);
             ((MarginLayoutParams) layoutParams)
-                    .setMargins(sideSpacing, topSpacing, sideSpacing, bottomSpacing);
+                    .setMargins(sSideSpacing, topSpacing, sSideSpacing, bottomSpacing);
         }
         view.setLayoutParams(layoutParams);
     }
@@ -356,6 +353,29 @@
         view.setClipToOutline(true);
     }
 
+    private static void initializeDimensions(Context context) {
+        boolean showModernizeVisualUpdate =
+                OmniboxFeatures.shouldShowModernizeVisualUpdate(context);
+        Resources resources = context.getResources();
+        sIconWidthPx = resources.getDimensionPixelSize(showModernizeVisualUpdate
+                        ? R.dimen.omnibox_suggestion_icon_area_size_modern
+                        : R.dimen.omnibox_suggestion_icon_area_size);
+
+        sPaddingStart = resources.getDimensionPixelSize(showModernizeVisualUpdate
+                        ? R.dimen.omnibox_suggestion_24dp_icon_margin_start_modern
+                        : R.dimen.omnibox_suggestion_24dp_icon_margin_start);
+        sPaddingStartLargeIcon =
+                resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_36dp_icon_margin_start);
+        sPaddingEnd =
+                resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_24dp_icon_margin_end);
+        sPaddingEndLargeIcon =
+                resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_36dp_icon_margin_end);
+        sEdgeSize = resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_24dp_icon_size);
+        sEdgeSizeLargeIcon =
+                resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_36dp_icon_size);
+        sSideSpacing = resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_side_spacing);
+    }
+
     /** @return Cached ConstantState for testing. */
     @VisibleForTesting
     public static Drawable.ConstantState getFocusableDrawableStateForTesting() {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessor.java
index 7488730..82ccec7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessor.java
@@ -85,9 +85,12 @@
                 () -> onJourneysSuggestionClicked(pedal, position));
         SuggestionDrawableState sds =
                 SuggestionDrawableState.Builder.forDrawableRes(mContext, pedal.icon.iconRes)
-                        .setAllowTint(true)
+                        .setAllowTint(false)
                         .build();
         model.set(BaseSuggestionViewProperties.ICON, sds);
+        // We want to behave like a search suggestion w.r.t. secondary text coloring.
+        model.set(SuggestionViewProperties.IS_SEARCH_SUGGESTION, true);
+        setActionButtons(model, null);
         mJourneysActionShownPosition = position;
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessorTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessorTest.java
index 0f9cf8b..7a8ec28 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessorTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessorTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
@@ -100,6 +101,8 @@
         mProcessor.populateModel(suggestion, propertyModel, 2);
         assertEquals(new SuggestionSpannable(suggestion.getActions().get(0).hint),
                 propertyModel.get(SuggestionViewProperties.TEXT_LINE_2_TEXT));
+        assertTrue(propertyModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
+        assertNull(propertyModel.get(BaseSuggestionViewProperties.ACTION_BUTTONS));
         SuggestionDrawableState sds = propertyModel.get(BaseSuggestionViewProperties.ICON);
         assertNotNull(sds);
         assertEquals(R.drawable.action_journeys, sds.resourceId);
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.cc b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.cc
index f050b92..46581c4 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.cc
+++ b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.cc
@@ -30,6 +30,7 @@
 using ::google_apis::ApiErrorCode;
 using ::google_apis::tasks::ListTaskListsRequest;
 using ::google_apis::tasks::ListTasksRequest;
+using ::google_apis::tasks::PatchTaskRequest;
 using ::google_apis::tasks::Task;
 using ::google_apis::tasks::TaskList;
 using ::google_apis::tasks::TaskLists;
@@ -152,6 +153,20 @@
                  std::move(callback));
 }
 
+void GlanceablesTasksClientImpl::MarkAsCompleted(
+    const std::string& task_list_id,
+    const std::string& task_id) {
+  CHECK(!task_list_id.empty());
+  CHECK(!task_id.empty());
+
+  GetRequestSender()->StartRequestWithAuthRetry(
+      std::make_unique<PatchTaskRequest>(
+          request_sender_.get(),
+          base::BindOnce(&GlanceablesTasksClientImpl::OnMarkedAsCompleted,
+                         weak_factory_.GetWeakPtr(), task_list_id, task_id),
+          task_list_id, task_id, Task::Status::kCompleted));
+}
+
 void GlanceablesTasksClientImpl::FetchTaskListsPage(
     const std::string& page_token,
     GlanceablesTasksClient::GetTaskListsCallback callback) {
@@ -226,6 +241,29 @@
   }
 }
 
+void GlanceablesTasksClientImpl::OnMarkedAsCompleted(
+    const std::string& task_list_id,
+    const std::string& task_id,
+    ApiErrorCode status_code) {
+  if (status_code != ApiErrorCode::HTTP_SUCCESS) {
+    return;
+  }
+
+  const auto task_list_iter = tasks_in_task_lists_.find(task_list_id);
+  if (task_list_iter == tasks_in_task_lists_.end()) {
+    return;
+  }
+  const auto task_iter = std::find_if(
+      task_list_iter->second->begin(), task_list_iter->second->end(),
+      [&task_id](const auto& task) { return task->id == task_id; });
+  if (task_iter == task_list_iter->second->end()) {
+    return;
+  }
+
+  const auto task_index = task_iter - task_list_iter->second->begin();
+  task_list_iter->second->RemoveAt(task_index);
+}
+
 google_apis::RequestSender* GlanceablesTasksClientImpl::GetRequestSender() {
   if (!request_sender_) {
     CHECK(create_request_sender_callback_);
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.h b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.h
index e61c660..9d6bd46 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.h
+++ b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl.h
@@ -56,6 +56,8 @@
       GlanceablesTasksClient::GetTaskListsCallback callback) override;
   void GetTasks(const std::string& task_list_id,
                 GlanceablesTasksClient::GetTasksCallback callback) override;
+  void MarkAsCompleted(const std::string& task_list_id,
+                       const std::string& task_id) override;
 
  private:
   // Fetches one page of task lists data.
@@ -107,6 +109,12 @@
       base::expected<std::unique_ptr<google_apis::tasks::Tasks>,
                      google_apis::ApiErrorCode> result);
 
+  // Callback for `MarkAsCompleted()` request. Removes the task from
+  // `tasks_in_task_lists_` if succeeded.
+  void OnMarkedAsCompleted(const std::string& task_list_id,
+                           const std::string& task_id,
+                           google_apis::ApiErrorCode status_code);
+
   // Returns lazily initialized `request_sender_`.
   google_apis::RequestSender* GetRequestSender();
 
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl_unittest.cc b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl_unittest.cc
index 70b92e3..c7c74fc 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/glanceables/glanceables_tasks_client_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/glanceables/tasks/glanceables_tasks_types.h"
 #include "base/functional/bind.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/repeating_test_future.h"
 #include "base/test/scoped_command_line.h"
@@ -35,6 +36,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/list_model.h"
+#include "ui/base/models/list_model_observer.h"
 
 namespace ash {
 namespace {
@@ -44,10 +46,12 @@
 using ::google_apis::ApiErrorCode;
 using ::google_apis::util::FormatTimeAsString;
 using ::net::test_server::BasicHttpResponse;
+using ::net::test_server::HttpMethod;
 using ::net::test_server::HttpRequest;
 using ::net::test_server::HttpResponse;
 using ::testing::_;
 using ::testing::ByMove;
+using ::testing::Eq;
 using ::testing::Field;
 using ::testing::HasSubstr;
 using ::testing::Not;
@@ -130,6 +134,18 @@
               (const HttpRequest&));
 };
 
+// Observer for `ui::ListModel` changes.
+class TestListModelObserver : public ui::ListModelObserver {
+ public:
+  MOCK_METHOD(void, ListItemsAdded, (size_t start, size_t count), (override));
+  MOCK_METHOD(void, ListItemsRemoved, (size_t start, size_t count), (override));
+  MOCK_METHOD(void,
+              ListItemMoved,
+              (size_t index, size_t target_index),
+              (override));
+  MOCK_METHOD(void, ListItemsChanged, (size_t start, size_t count), (override));
+};
+
 }  // namespace
 
 class GlanceablesTasksClientImplTest : public testing::Test {
@@ -407,4 +423,86 @@
   EXPECT_EQ(root_tasks->GetItemAt(1)->id, "parent-task-from-page-3");
 }
 
+TEST_F(GlanceablesTasksClientImplTest, MarkAsCompleted) {
+  EXPECT_CALL(
+      request_handler(),
+      HandleRequest(Field(&HttpRequest::method, Eq(HttpMethod::METHOD_GET))))
+      .WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
+          {
+            "kind": "tasks#tasks",
+            "items": [
+              {
+                "id": "task-1",
+                "status": "needsAction"
+              },
+              {
+                "id": "task-2",
+                "status": "needsAction"
+              }
+            ]
+          }
+        )"))));
+  EXPECT_CALL(
+      request_handler(),
+      HandleRequest(Field(&HttpRequest::method, Eq(HttpMethod::METHOD_PATCH))))
+      .WillOnce(
+          Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(""))));
+
+  TestFuture<ui::ListModel<GlanceablesTask>*> future;
+  client()->GetTasks("test-task-list-id", future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  auto* const tasks = future.Get();
+  EXPECT_EQ(tasks->item_count(), 2u);
+
+  testing::StrictMock<TestListModelObserver> observer;
+  tasks->AddObserver(&observer);
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer, ListItemsRemoved(/*start=*/1, /*count=*/1))
+      .WillOnce([&run_loop]() { run_loop.Quit(); });
+  client()->MarkAsCompleted("test-task-list-id", "task-2");
+  run_loop.Run();
+
+  EXPECT_EQ(tasks->item_count(), 1u);
+  EXPECT_EQ(tasks->GetItemAt(0)->id, "task-1");
+}
+
+TEST_F(GlanceablesTasksClientImplTest, MarkAsCompletedOnHttpError) {
+  EXPECT_CALL(
+      request_handler(),
+      HandleRequest(Field(&HttpRequest::method, Eq(HttpMethod::METHOD_GET))))
+      .WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
+          {
+            "kind": "tasks#tasks",
+            "items": [
+              {
+                "id": "task-1",
+                "status": "needsAction"
+              },
+              {
+                "id": "task-2",
+                "status": "needsAction"
+              }
+            ]
+          }
+        )"))));
+  EXPECT_CALL(
+      request_handler(),
+      HandleRequest(Field(&HttpRequest::method, Eq(HttpMethod::METHOD_PATCH))))
+      .WillOnce(Return(ByMove(TestRequestHandler::CreateFailedResponse())));
+
+  TestFuture<ui::ListModel<GlanceablesTask>*> future;
+  client()->GetTasks("test-task-list-id", future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  const auto* const tasks = future.Get();
+  EXPECT_EQ(tasks->item_count(), 2u);
+
+  client()->MarkAsCompleted("test-task-list-id", "task-2");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(tasks->item_count(), 2u);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/popup_browsertest.cc b/chrome/browser/ui/popup_browsertest.cc
index b9cebda9..74c8a84 100644
--- a/chrome/browser/ui/popup_browsertest.cc
+++ b/chrome/browser/ui/popup_browsertest.cc
@@ -541,6 +541,42 @@
   EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
 }
 
+IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest, AboutBlankFullscreen) {
+  SetUpWebServer();
+  SetUpWindowManagement();
+  Browser* new_popup =
+      OpenPopup(browser(), "open('about:blank', '_blank', 'popup,fullscreen')");
+  content::WebContents* new_contents =
+      new_popup->tab_strip_model()->GetActiveWebContents();
+  if (ShouldTestWindowManagement()) {
+    WaitForHTMLFullscreen(new_contents);
+  }
+  EXPECT_EQ(EvalJs(new_contents,
+                   "!!document.fullscreenElement && document.fullscreenElement "
+                   "== document.documentElement")
+                .ExtractBool(),
+            ShouldTestWindowManagement());
+  FullscreenController* fullscreen_controller =
+      new_popup->exclusive_access_manager()->fullscreen_controller();
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_EQ(fullscreen_controller->IsTabFullscreen(),
+            ShouldTestWindowManagement());
+  EXPECT_EQ(EvalJs(new_contents, "document.exitFullscreen()").error.empty(),
+            ShouldTestWindowManagement());
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+
+  // Test that a navigation doesn't re-trigger fullscreen.
+  EXPECT_TRUE(EvalJs(new_contents,
+                     "window.location.href = '" +
+                         embedded_test_server()->GetURL("/title1.html").spec() +
+                         "'")
+                  .error.empty());
+  EXPECT_TRUE(content::WaitForLoadStop(new_contents));
+  EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
+  EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
+}
+
 IN_PROC_BROWSER_TEST_P(WindowManagementPopupBrowserTest, FullscreenWithBounds) {
   SetUpWebServer();
   SetUpWindowManagement();
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.cc
index acef06b..4ea363ccf 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.cc
@@ -6,11 +6,16 @@
 
 #include "base/token.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/saved_tab_groups/saved_tab_group_model.h"
 
+namespace content {
+class WebContents;
+}
+
 LocalTabGroupListener::LocalTabGroupListener(
     const tab_groups::TabGroupId local_id,
     const base::GUID saved_guid,
@@ -32,9 +37,39 @@
 
 LocalTabGroupListener::~LocalTabGroupListener() = default;
 
+void LocalTabGroupListener::PauseTracking() {
+  paused_ = true;
+}
+
+void LocalTabGroupListener::ResumeTracking() {
+  paused_ = false;
+
+  // Thoroughly check for consistency between the data structures we're linking.
+  // The saved tabs and the local tabs should match up in the same order.
+  const std::vector<SavedTabGroupTab>& saved_tabs = saved_group()->saved_tabs();
+  const std::vector<content::WebContents*> local_tabs =
+      SavedTabGroupUtils::GetWebContentsesInGroup(local_id_);
+
+  CHECK_EQ(saved_tabs.size(), local_tabs.size());
+  for (size_t i = 0; i < saved_tabs.size(); ++i) {
+    const SavedTabGroupTab& saved_tab = saved_tabs[i];
+    content::WebContents* const local_tab = local_tabs[i];
+
+    const auto map_entry = web_contents_to_tab_id_map_.find(local_tab);
+    CHECK(map_entry != web_contents_to_tab_id_map_.end());
+
+    const SavedTabGroupWebContentsListener& listener = map_entry->second;
+    CHECK_EQ(saved_tab.local_tab_id().value(), listener.token());
+  }
+}
+
 void LocalTabGroupListener::AddWebContents(content::WebContents* web_contents,
                                            TabStripModel* tab_strip_model,
                                            int index) {
+  if (paused_) {
+    return;
+  }
+
   CHECK(model_->Contains(saved_guid_));
   CHECK(tab_strip_model->group_model()->ContainsTabGroup(local_id_));
 
@@ -63,6 +98,10 @@
 
 void LocalTabGroupListener::RemoveWebContentsIfPresent(
     content::WebContents* web_contents) {
+  if (paused_) {
+    return;
+  }
+
   if (web_contents_to_tab_id_map_.count(web_contents) == 0) {
     return;
   }
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.h
index 81df3cc..10151329 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.h
@@ -28,10 +28,21 @@
       std::vector<std::pair<content::WebContents*, base::GUID>> mapping);
   virtual ~LocalTabGroupListener();
 
+  // Pauses listening to changes to the local tab group. Call this before
+  // beginning a multi-step operation that will have no net effect on the group
+  // (e.g. moving it to another window).
+  void PauseTracking();
+
+  // Resumes listening to changes to the local tab group. The tab group must be
+  // in the same configuration it was in when PauseTracking was called (this is
+  // CHECKed).
+  void ResumeTracking();
+
   // Updates the saved group with the new tab and tracks it for further changes.
   void AddWebContents(content::WebContents* web_contents,
                       TabStripModel* tab_strip_model,
                       int index);
+
   // If `web_contents` is in this listener's local tab group, removes it from
   // the saved tab group and stops tracking it.
   void RemoveWebContentsIfPresent(content::WebContents* web_contents);
@@ -45,6 +56,10 @@
  private:
   const SavedTabGroup* saved_group() const { return model_->Get(saved_guid_); }
 
+  // Whether local tab group changes will be ignored (`paused_` is true) or
+  // reflected in the saved group (`paused_` is false).
+  bool paused_ = false;
+
   std::unordered_map<content::WebContents*, SavedTabGroupWebContentsListener>
       web_contents_to_tab_id_map_;
   const raw_ptr<SavedTabGroupModel> model_;
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_controller.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_controller.h
index 5f5e110..859a5b7 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_controller.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_controller.h
@@ -26,6 +26,17 @@
   // removes it. Stops Listening to all tabs.
   virtual void UnsaveGroup(const tab_groups::TabGroupId& group_id) = 0;
 
+  // Pauses listening to the Tab Group in the TabStrip, but maintains the
+  // connection between the two.
+  virtual void PauseTrackingLocalTabGroup(
+      const tab_groups::TabGroupId& group_id) = 0;
+
+  // Resumes listening to a paused Tab Group. The WebContentses in the local
+  // group must match the order they were in when the group tracking was paused.
+  virtual void ResumeTrackingLocalTabGroup(
+      const base::GUID& saved_group_guid,
+      const tab_groups::TabGroupId& group_id) = 0;
+
   // Stops listening to the Tab Group in the TabStrip. Removes the local tab
   // group id and web content tokens.
   virtual void DisconnectLocalTabGroup(
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
index f417886..57eff9c 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
@@ -82,8 +82,9 @@
 
   // If the group already has a local group open, then activate it.
   if (saved_group->local_group_id().has_value()) {
-    Browser* browser_for_activation = listener_.GetBrowserWithTabGroupId(
-        saved_group->local_group_id().value());
+    Browser* browser_for_activation =
+        SavedTabGroupUtils::GetBrowserWithTabGroupId(
+            saved_group->local_group_id().value());
 
     // Only activate the tab group's first tab if it exists in any browser's
     // tabstrip model.
@@ -158,15 +159,18 @@
   DCHECK(first_tab.has_value());
   tab_strip_model_for_creation->ActivateTabAt(first_tab.value());
 
+  // Set the group's visual data after the tab strip is in its final state. This
+  // ensures the tab group's bounds are correctly set. crbug/1408814.
+  UpdateGroupVisualData(saved_group_guid,
+                        saved_group->local_group_id().value());
+
   listener_.ConnectToLocalTabGroup(*model_.Get(saved_group_guid),
                                    local_and_saved_tab_mapping);
-
-  UpdateTabGroupVisualData(tab_group, saved_group);
 }
 
 void SavedTabGroupKeyedService::SaveGroup(
     const tab_groups::TabGroupId& group_id) {
-  Browser* browser = listener_.GetBrowserWithTabGroupId(group_id);
+  Browser* browser = SavedTabGroupUtils::GetBrowserWithTabGroupId(group_id);
   CHECK(browser);
 
   TabStripModel* tab_strip_model = browser->tab_strip_model();
@@ -220,6 +224,19 @@
   model_.Remove(group->saved_guid());
 }
 
+void SavedTabGroupKeyedService::PauseTrackingLocalTabGroup(
+    const tab_groups::TabGroupId& group_id) {
+  listener_.PauseTrackingLocalTabGroup(group_id);
+}
+
+void SavedTabGroupKeyedService::ResumeTrackingLocalTabGroup(
+    const base::GUID& saved_group_guid,
+    const tab_groups::TabGroupId& group_id) {
+  listener_.ResumeTrackingLocalTabGroup(group_id);
+  model_.OnGroupOpenedInTabStrip(saved_group_guid, group_id);
+  UpdateGroupVisualData(saved_group_guid, group_id);
+}
+
 void SavedTabGroupKeyedService::DisconnectLocalTabGroup(
     const tab_groups::TabGroupId& group_id) {
   listener_.DisconnectLocalTabGroup(group_id);
@@ -260,7 +277,7 @@
   listener_.ConnectToLocalTabGroup(*model_.Get(saved_guid),
                                    std::move(web_contents_to_guid_mapping));
 
-  UpdateTabGroupVisualData(tab_group, saved_group);
+  UpdateGroupVisualData(saved_guid, local_group_id);
 }
 
 void SavedTabGroupKeyedService::SavedTabGroupModelLoaded() {
@@ -281,19 +298,22 @@
 const TabStripModel* SavedTabGroupKeyedService::GetTabStripModelWithTabGroupId(
     const tab_groups::TabGroupId& local_group_id) {
   const Browser* const browser =
-      listener_.GetBrowserWithTabGroupId(local_group_id);
+      SavedTabGroupUtils::GetBrowserWithTabGroupId(local_group_id);
   CHECK(browser);
   return browser->tab_strip_model();
 }
 
-void SavedTabGroupKeyedService::UpdateTabGroupVisualData(
-    TabGroup* const tab_group,
-    const SavedTabGroup* saved_group) {
-  // Update the group to use the saved title and color.
-  const tab_groups::TabGroupVisualData visual_data(
-      saved_group->title(), saved_group->color(), /*is_collapsed=*/false);
+void SavedTabGroupKeyedService::UpdateGroupVisualData(
+    const base::GUID saved_group_guid,
+    const tab_groups::TabGroupId group_id) {
+  TabGroup* const tab_group = SavedTabGroupUtils::GetTabGroupWithId(group_id);
+  CHECK(tab_group);
+  const SavedTabGroup* const saved_group = model_.Get(saved_group_guid);
+  CHECK(saved_group);
 
-  // Set the groups visual data after the tab strip is in its final state. This
-  // ensures the tab group's bounds are correctly set. crbug/1408814.
+  // Update the group to use the saved title and color.
+  tab_groups::TabGroupVisualData visual_data(saved_group->title(),
+                                             saved_group->color(),
+                                             /*is_collapsed=*/false);
   tab_group->SetVisualData(visual_data, /*is_customized=*/true);
 }
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h
index 1883a0a..4f22cdf 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h
@@ -14,7 +14,6 @@
 #include "components/tab_groups/tab_group_id.h"
 
 class Profile;
-class TabGroup;
 
 // Serves to instantiate and own the SavedTabGroup infrastructure for the
 // browser.
@@ -44,6 +43,11 @@
                                   const base::GUID& saved_group_guid) override;
   void SaveGroup(const tab_groups::TabGroupId& group_id) override;
   void UnsaveGroup(const tab_groups::TabGroupId& group_id) override;
+  void PauseTrackingLocalTabGroup(
+      const tab_groups::TabGroupId& group_id) override;
+  void ResumeTrackingLocalTabGroup(
+      const base::GUID& saved_group_guid,
+      const tab_groups::TabGroupId& group_id) override;
   void DisconnectLocalTabGroup(const tab_groups::TabGroupId& group_id) override;
   void ConnectLocalTabGroup(const tab_groups::TabGroupId& group_id,
                             const base::GUID& saved_group_guid) override;
@@ -56,14 +60,14 @@
   const TabStripModel* GetTabStripModelWithTabGroupId(
       const tab_groups::TabGroupId& local_group_id);
 
-  // Notifies observers that `tab_group`'s visual data was changed using data
-  // found in `saved_group`.
-  void UpdateTabGroupVisualData(TabGroup* const tab_group,
-                                const SavedTabGroup* saved_group);
-
   // Returns the ModelTypeStoreFactory tied to the current profile.
   syncer::OnceModelTypeStoreFactory GetStoreFactory();
 
+  // Notifies observers that the tab group with id `group_id`'s visual data was
+  // changed using data found in `saved_group_guid`.
+  void UpdateGroupVisualData(base::GUID saved_group_guid,
+                             tab_groups::TabGroupId group_id);
+
   // The profile used to instantiate the keyed service.
   raw_ptr<Profile> profile_ = nullptr;
 
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service_unittest.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service_unittest.cc
index 753fd31..0c80831 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service_unittest.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service_unittest.cc
@@ -2,15 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
 
 #include <memory>
 
-#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/browser_task_environment.h"
@@ -82,8 +85,7 @@
   tab_groups::TabGroupId group_id =
       browser_1->tab_strip_model()->AddToNewGroup({0});
 
-  EXPECT_EQ(browser_1,
-            service()->listener()->GetBrowserWithTabGroupId(group_id));
+  EXPECT_EQ(browser_1, SavedTabGroupUtils::GetBrowserWithTabGroupId(group_id));
 }
 
 TEST_F(SavedTabGroupKeyedServiceUnitTest,
@@ -224,3 +226,82 @@
 
   EXPECT_DEATH(service()->StoreLocalToSavedId(guid_1, tab_group_id_1), "");
 }
+
+TEST_F(SavedTabGroupKeyedServiceUnitTest, PauseResumeTracking) {
+  Browser* browser_1 = AddBrowser();
+
+  // Create a saved tab group with two tabs, one in a saved group.
+  ASSERT_EQ(0, browser_1->tab_strip_model()->count());
+  AddTabToBrowser(browser_1, 0);
+  content::WebContents* grouped_tab_ptr = AddTabToBrowser(browser_1, 1);
+  ASSERT_EQ(2, browser_1->tab_strip_model()->count());
+  tab_groups::TabGroupId group_id =
+      browser_1->tab_strip_model()->AddToNewGroup({1});
+  service()->SaveGroup(group_id);
+  base::GUID saved_group_id = service()->model()->Get(group_id)->saved_guid();
+
+  // We should be listening to one group and one tab in that group.
+  auto& group_listener_map =
+      service()->listener()->GetLocalTabGroupListenerMapForTesting();
+  ASSERT_EQ(1u, group_listener_map.count(group_id));
+  auto& tab_token_mapping =
+      group_listener_map.at(group_id).GetWebContentsTokenMapForTesting();
+  ASSERT_EQ(1u, tab_token_mapping.size());
+  ASSERT_EQ(1u, tab_token_mapping.count(grouped_tab_ptr));
+
+  // Pause tracking.
+  service()->PauseTrackingLocalTabGroup(group_id);
+
+  // Remove the tab in the group.
+  tab_groups::TabGroupVisualData visual_data = *(browser_1->tab_strip_model()
+                                                     ->group_model()
+                                                     ->GetTabGroup(group_id)
+                                                     ->visual_data());
+  std::unique_ptr<content::WebContents> tab =
+      browser_1->tab_strip_model()->DetachWebContentsAtForInsertion(1);
+  // This kills the group.
+  ASSERT_FALSE(
+      browser_1->tab_strip_model()->group_model()->ContainsTabGroup(group_id));
+
+  // Recreate the local group and add the tab to it (same browser is fine).
+  browser_1->tab_strip_model()->group_model()->AddTabGroup(group_id,
+                                                           visual_data);
+  browser_1->tab_strip_model()->InsertWebContentsAt(
+      1, std::move(tab), AddTabTypes::ADD_NONE, group_id);
+
+  // Resume tracking.
+  service()->ResumeTrackingLocalTabGroup(saved_group_id, group_id);
+
+  // Validate that tracking still works.
+  // Check that the local and saved ids are still linked in the model.
+  EXPECT_EQ(saved_group_id, service()->model()->Get(group_id)->saved_guid());
+  // Check that there is still one tab in the model's saved group.
+  EXPECT_EQ(1u, service()->model()->Get(group_id)->saved_tabs().size());
+  // The listener state should be the same as well.
+  EXPECT_EQ(1u, group_listener_map.count(group_id));
+  EXPECT_EQ(1u, tab_token_mapping.size());
+  EXPECT_EQ(1u, tab_token_mapping.count(grouped_tab_ptr));
+}
+
+TEST_F(SavedTabGroupKeyedServiceUnitTest, ResumeTrackingValidatesConsistency) {
+  Browser* browser_1 = AddBrowser();
+
+  // Create a saved tab group with two tabs.
+  ASSERT_EQ(0, browser_1->tab_strip_model()->count());
+  AddTabToBrowser(browser_1, 0);
+  AddTabToBrowser(browser_1, 1);
+  ASSERT_EQ(2, browser_1->tab_strip_model()->count());
+  tab_groups::TabGroupId group_id =
+      browser_1->tab_strip_model()->AddToNewGroup({0, 1});
+  service()->SaveGroup(group_id);
+  base::GUID saved_group_id = service()->model()->Get(group_id)->saved_guid();
+
+  // Pause tracking.
+  service()->PauseTrackingLocalTabGroup(group_id);
+
+  // Swap the order of the tabs.
+  browser_1->tab_strip_model()->MoveWebContentsAt(0, 1, false);
+
+  EXPECT_DEATH(service()->ResumeTrackingLocalTabGroup(saved_group_id, group_id),
+               "");
+}
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
index 72388bf..d165774 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/local_tab_group_listener.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -72,8 +73,8 @@
       local_tab_group_listeners_.contains(new_local_group_id.value())) {
     LocalTabGroupListener& listener =
         local_tab_group_listeners_.at(new_local_group_id.value());
-    const Browser* const browser =
-        GetBrowserWithTabGroupId(new_local_group_id.value());
+    const Browser* const browser = SavedTabGroupUtils::GetBrowserWithTabGroupId(
+        new_local_group_id.value());
     CHECK(browser);
     listener.AddWebContents(contents, browser->tab_strip_model(), index);
   }
@@ -115,16 +116,6 @@
   }
 }
 
-Browser* SavedTabGroupModelListener::GetBrowserWithTabGroupId(
-    tab_groups::TabGroupId group_id) const {
-  for (Browser* browser : *BrowserList::GetInstance()) {
-    if (browser->tab_strip_model()->group_model()->ContainsTabGroup(group_id)) {
-      return browser;
-    }
-  }
-  return nullptr;
-}
-
 void SavedTabGroupModelListener::ConnectToLocalTabGroup(
     const SavedTabGroup& saved_tab_group,
     std::vector<std::pair<content::WebContents*, base::GUID>> mapping) {
@@ -133,11 +124,8 @@
 
   // `mapping` should have one entry per tab in the local group. This may not
   // equal the saved group's size, if the saved group contains invalid URLs.
-  const size_t local_group_size = GetBrowserWithTabGroupId(local_group_id)
-                                      ->tab_strip_model()
-                                      ->group_model()
-                                      ->GetTabGroup(local_group_id)
-                                      ->tab_count();
+  const size_t local_group_size =
+      SavedTabGroupUtils::GetTabGroupWithId(local_group_id)->tab_count();
   CHECK_EQ(local_group_size, mapping.size());
 
   auto [iterator, success] = local_tab_group_listeners_.try_emplace(
@@ -146,6 +134,22 @@
   CHECK(success);
 }
 
+void SavedTabGroupModelListener::PauseTrackingLocalTabGroup(
+    const tab_groups::TabGroupId& group_id) {
+  if (!local_tab_group_listeners_.contains(group_id)) {
+    return;
+  }
+  local_tab_group_listeners_.at(group_id).PauseTracking();
+}
+
+void SavedTabGroupModelListener::ResumeTrackingLocalTabGroup(
+    const tab_groups::TabGroupId& group_id) {
+  if (!local_tab_group_listeners_.contains(group_id)) {
+    return;
+  }
+  local_tab_group_listeners_.at(group_id).ResumeTracking();
+}
+
 void SavedTabGroupModelListener::DisconnectLocalTabGroup(
     tab_groups::TabGroupId tab_group_id) {
   local_tab_group_listeners_.erase(tab_group_id);
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
index 83b8aa7..546edce 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
@@ -40,7 +40,11 @@
       const SavedTabGroupModelListener& other) = delete;
   ~SavedTabGroupModelListener() override;
 
-  Browser* GetBrowserWithTabGroupId(tab_groups::TabGroupId group_id) const;
+  // Start ignoring tab added/removed notifications that pertain to this group.
+  void PauseTrackingLocalTabGroup(const tab_groups::TabGroupId& group_id);
+
+  // Stop ignoring tab added/removed notifications that pertain to this group.
+  void ResumeTrackingLocalTabGroup(const tab_groups::TabGroupId& group_id);
 
   // Start keeping `saved_tab_group` up to date with changes to its
   // corresponding local group.
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
index e94b654f..4c18bfd 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
@@ -3,11 +3,16 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
+
 #include "base/guid.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/saved_tab_groups/saved_tab_group_tab.h"
+#include "components/tab_groups/tab_group_id.h"
 #include "content/public/browser/web_contents.h"
 
 SavedTabGroupTab SavedTabGroupUtils::CreateSavedTabGroupTabFromWebContents(
@@ -30,3 +35,48 @@
   base::WeakPtr<content::NavigationHandle> handle = Navigate(&params);
   return handle ? handle->GetWebContents() : nullptr;
 }
+
+// static
+Browser* SavedTabGroupUtils::GetBrowserWithTabGroupId(
+    tab_groups::TabGroupId group_id) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->tab_strip_model()->group_model()->ContainsTabGroup(group_id)) {
+      return browser;
+    }
+  }
+  return nullptr;
+}
+
+// static
+TabGroup* SavedTabGroupUtils::GetTabGroupWithId(
+    tab_groups::TabGroupId group_id) {
+  Browser* browser = GetBrowserWithTabGroupId(group_id);
+  if (!browser || !browser->tab_strip_model() ||
+      !browser->tab_strip_model()->SupportsTabGroups()) {
+    return nullptr;
+  }
+
+  TabGroupModel* tab_group_model = browser->tab_strip_model()->group_model();
+  CHECK(tab_group_model);
+
+  return tab_group_model->GetTabGroup(group_id);
+}
+
+// static
+std::vector<content::WebContents*> SavedTabGroupUtils::GetWebContentsesInGroup(
+    tab_groups::TabGroupId group_id) {
+  Browser* browser = GetBrowserWithTabGroupId(group_id);
+  if (!browser || !browser->tab_strip_model() ||
+      !browser->tab_strip_model()->SupportsTabGroups()) {
+    return {};
+  }
+
+  const gfx::Range local_tab_group_indices =
+      SavedTabGroupUtils::GetTabGroupWithId(group_id)->ListTabs();
+  std::vector<content::WebContents*> contentses;
+  for (size_t index = local_tab_group_indices.start();
+       index < local_tab_group_indices.end(); index++) {
+    contentses.push_back(browser->tab_strip_model()->GetWebContentsAt(index));
+  }
+  return contentses;
+}
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
index 4ba2029..ba29e89 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_UTILS_H_
 
 #include "base/guid.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
@@ -33,6 +34,16 @@
       Browser* browser,
       Profile* profile,
       WindowOpenDisposition disposition);
+
+  // Returns the Browser that contains a local group with id `group_id`.
+  static Browser* GetBrowserWithTabGroupId(tab_groups::TabGroupId group_id);
+
+  // Finds the TabGroup with id `group_id` across all Browsers.
+  static TabGroup* GetTabGroupWithId(tab_groups::TabGroupId group_id);
+
+  // Returns the list of WebContentses in the local group `group_id` in order.
+  static std::vector<content::WebContents*> GetWebContentsesInGroup(
+      tab_groups::TabGroupId group_id);
 };
 
 #endif  // CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_UTILS_H_
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h
index 40c0782..ef976b39 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h
@@ -26,13 +26,13 @@
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
-  base::Token token() { return token_; }
-  content::WebContents* web_contents() { return web_contents_; }
+  base::Token token() const { return token_; }
+  content::WebContents* web_contents() const { return web_contents_; }
 
  private:
-  base::Token token_;
-  raw_ptr<content::WebContents> web_contents_;
-  raw_ptr<SavedTabGroupModel> model_;
+  const base::Token token_;
+  const raw_ptr<content::WebContents> web_contents_;
+  const raw_ptr<SavedTabGroupModel> model_;
 };
 
 #endif  // CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_WEB_CONTENTS_LISTENER_H_
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
index 350b2b7..486c884f 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
@@ -287,7 +288,7 @@
 void SavedTabGroupButton::MoveGroupToNewWindowPressed(int event_flags) {
   Browser* const browser_with_local_group_id =
       local_group_id_.has_value()
-          ? service_->listener()->GetBrowserWithTabGroupId(
+          ? SavedTabGroupUtils::GetBrowserWithTabGroupId(
                 local_group_id_.value())
           : base::to_address(browser_);
 
@@ -306,7 +307,7 @@
 void SavedTabGroupButton::DeleteGroupPressed(int event_flags) {
   if (local_group_id_.has_value()) {
     const Browser* const browser_with_local_group_id =
-        service_->listener()->GetBrowserWithTabGroupId(local_group_id_.value());
+        SavedTabGroupUtils::GetBrowserWithTabGroupId(local_group_id_.value());
 
     // Keep the opened tab group in the tabstrip but remove the SavedTabGroup
     // data from the model.
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
index dbc9cc4e..d7fad5b 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/themes/theme_service_aura_linux.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/dark_mode_manager_linux.h"
 #include "chrome/browser/ui/views/theme_profile_key.h"
 #include "ui/base/buildflags.h"
 #include "ui/base/cursor/cursor_factory.h"
@@ -56,6 +57,8 @@
     UMA_HISTOGRAM_ENUMERATION("Linux.SystemTheme.Default",
                               linux_ui_theme->GetNativeTheme()->system_theme());
   }
+
+  dark_mode_manager_ = std::make_unique<ui::DarkModeManagerLinux>();
 }
 
 void ChromeBrowserMainExtraPartsViewsLinux::PreCreateThreads() {
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.h b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.h
index 392d14c4..6deb520 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.h
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.h
@@ -13,6 +13,7 @@
 
 namespace ui {
 class LinuxUiGetter;
+class DarkModeManagerLinux;
 }
 
 // Extra parts, which are used by both Ozone/X11/Wayland and inherited by the
@@ -41,6 +42,8 @@
   absl::optional<display::ScopedDisplayObserver> display_observer_;
 
   std::unique_ptr<ui::LinuxUiGetter> linux_ui_getter_;
+
+  std::unique_ptr<ui::DarkModeManagerLinux> dark_mode_manager_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_CHROME_BROWSER_MAIN_EXTRA_PARTS_VIEWS_LINUX_H_
diff --git a/chrome/browser/ui/views/dark_mode_manager_linux.cc b/chrome/browser/ui/views/dark_mode_manager_linux.cc
new file mode 100644
index 0000000..bb638f7
--- /dev/null
+++ b/chrome/browser/ui/views/dark_mode_manager_linux.cc
@@ -0,0 +1,160 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/dark_mode_manager_linux.h"
+
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "components/dbus/thread_linux/dbus_thread_linux.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_proxy.h"
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/linux_ui_factory.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace {
+
+constexpr char kFreedesktopSettingsService[] = "org.freedesktop.portal.Desktop";
+constexpr char kFreedesktopSettingsObjectPath[] =
+    "/org/freedesktop/portal/desktop";
+constexpr char kFreedesktopSettingsInterface[] =
+    "org.freedesktop.portal.Settings";
+constexpr char kSettingChangedSignal[] = "SettingChanged";
+constexpr char kReadMethod[] = "Read";
+constexpr char kSettingsNamespace[] = "org.freedesktop.appearance";
+constexpr char kColorSchemeKey[] = "color-scheme";
+constexpr int kFreedesktopColorSchemeDark = 1;
+
+scoped_refptr<dbus::Bus> CreateBus() {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SESSION;
+  options.connection_type = dbus::Bus::PRIVATE;
+  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
+  return base::MakeRefCounted<dbus::Bus>(options);
+}
+
+}  // namespace
+
+namespace ui {
+
+DarkModeManagerLinux::DarkModeManagerLinux()
+    : bus_(CreateBus()),
+      settings_proxy_(bus_->GetObjectProxy(
+          kFreedesktopSettingsService,
+          dbus::ObjectPath(kFreedesktopSettingsObjectPath))) {
+  // Subscribe to changes in the color scheme preference.
+  settings_proxy_->ConnectToSignal(
+      kFreedesktopSettingsInterface, kSettingChangedSignal,
+      base::BindRepeating(&DarkModeManagerLinux::OnPortalSettingChanged,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&DarkModeManagerLinux::OnSignalConnected,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  // Read initial color scheme preference.
+  dbus::MethodCall method_call(kFreedesktopSettingsInterface, kReadMethod);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kSettingsNamespace);
+  writer.AppendString(kColorSchemeKey);
+  settings_proxy_->CallMethod(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::BindOnce(&DarkModeManagerLinux::OnReadColorSchemeResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  // Read the toolkit preference while asynchronously fetching the
+  // portal preference.
+  if (auto* linux_ui_theme = ui::GetDefaultLinuxUiTheme()) {
+    auto* native_theme = linux_ui_theme->GetNativeTheme();
+    native_theme_observer_.Observe(native_theme);
+    SetColorScheme(native_theme->ShouldUseDarkColors());
+  }
+}
+
+DarkModeManagerLinux::~DarkModeManagerLinux() {
+  settings_proxy_ = nullptr;
+  dbus::Bus* const bus_ptr = bus_.get();
+  bus_ptr->GetDBusTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, std::move(bus_)));
+}
+
+void DarkModeManagerLinux::OnNativeThemeUpdated(
+    ui::NativeTheme* observed_theme) {
+  SetColorScheme(observed_theme->ShouldUseDarkColors());
+}
+
+void DarkModeManagerLinux::OnSignalConnected(const std::string& interface_name,
+                                             const std::string& signal_name,
+                                             bool connected) {
+  // Nothing to do.  Continue using the toolkit setting if !connected.
+}
+
+void DarkModeManagerLinux::OnPortalSettingChanged(dbus::Signal* signal) {
+  dbus::MessageReader reader(signal);
+
+  std::string namespace_changed;
+  std::string key_changed;
+  dbus::MessageReader variant_reader(nullptr);
+  if (!reader.PopString(&namespace_changed) ||
+      !reader.PopString(&key_changed) || !reader.PopVariant(&variant_reader)) {
+    LOG(ERROR) << "Received malformed Setting Changed signal from "
+                  "org.freedesktop.portal.Settings";
+    return;
+  }
+
+  if (namespace_changed != kSettingsNamespace ||
+      key_changed != kColorSchemeKey) {
+    return;
+  }
+
+  uint32_t new_color_scheme;
+  if (!variant_reader.PopUint32(&new_color_scheme)) {
+    LOG(ERROR)
+        << "Failed to read color-scheme value from SettingChanged signal";
+    return;
+  }
+
+  SetColorScheme(new_color_scheme == kFreedesktopColorSchemeDark);
+}
+
+void DarkModeManagerLinux::OnReadColorSchemeResponse(dbus::Response* response) {
+  if (!response) {
+    // Continue using the toolkit setting.
+    return;
+  }
+
+  dbus::MessageReader reader(response);
+  dbus::MessageReader variant_reader(nullptr);
+  if (!reader.PopVariant(&variant_reader)) {
+    LOG(ERROR) << "Failed to read variant from Read method response";
+    return;
+  }
+
+  uint32_t new_color_scheme;
+  if (!variant_reader.PopVariantOfUint32(&new_color_scheme)) {
+    LOG(ERROR) << "Failed to read color-scheme value from Read "
+                  "method response";
+    return;
+  }
+
+  // Ignore future updates from the toolkit theme.
+  native_theme_observer_.Reset();
+
+  SetColorScheme(new_color_scheme == kFreedesktopColorSchemeDark);
+}
+
+void DarkModeManagerLinux::SetColorScheme(bool prefer_dark_theme) {
+  if (prefer_dark_theme_ == prefer_dark_theme) {
+    return;
+  }
+  prefer_dark_theme_ = prefer_dark_theme;
+
+  NativeTheme* web_theme = NativeTheme::GetInstanceForWeb();
+  web_theme->set_use_dark_colors(prefer_dark_theme_);
+  web_theme->set_preferred_color_scheme(
+      prefer_dark_theme_ ? NativeTheme::PreferredColorScheme::kDark
+                         : NativeTheme::PreferredColorScheme::kLight);
+  web_theme->NotifyOnNativeThemeUpdated();
+}
+
+}  // namespace ui
diff --git a/chrome/browser/ui/views/dark_mode_manager_linux.h b/chrome/browser/ui/views/dark_mode_manager_linux.h
new file mode 100644
index 0000000..34b07ff
--- /dev/null
+++ b/chrome/browser/ui/views/dark_mode_manager_linux.h
@@ -0,0 +1,62 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_DARK_MODE_MANAGER_LINUX_H_
+#define CHROME_BROWSER_UI_VIEWS_DARK_MODE_MANAGER_LINUX_H_
+
+#include <string>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "ui/native_theme/native_theme_observer.h"
+
+namespace dbus {
+class Bus;
+class ObjectProxy;
+class Response;
+class Signal;
+}  // namespace dbus
+
+namespace ui {
+
+// Observes the system color scheme preference using
+// org.freedesktop.portal.Settings. Falls back to the toolkit preference if
+// org.freedesktop.portal.Settings is unavailable.  Propagates the dark mode
+// preference to the web theme.
+class DarkModeManagerLinux : public NativeThemeObserver {
+ public:
+  DarkModeManagerLinux();
+  DarkModeManagerLinux(const DarkModeManagerLinux&) = delete;
+  DarkModeManagerLinux& operator=(const DarkModeManagerLinux&) = delete;
+  ~DarkModeManagerLinux() override;
+
+ private:
+  // ui::NativeThemeObserver:
+  void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
+
+  // D-Bus async handlers
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool connected);
+  void OnPortalSettingChanged(dbus::Signal* signal);
+  void OnReadColorSchemeResponse(dbus::Response* response);
+
+  // Sets `prefer_dark_theme_` and propagates to the web theme.
+  void SetColorScheme(bool prefer_dark_theme);
+
+  scoped_refptr<dbus::Bus> bus_;
+  raw_ptr<dbus::ObjectProxy> settings_proxy_;
+
+  bool prefer_dark_theme_ = false;
+
+  base::ScopedObservation<NativeTheme, NativeThemeObserver>
+      native_theme_observer_{this};
+
+  base::WeakPtrFactory<DarkModeManagerLinux> weak_ptr_factory_{this};
+};
+
+}  // namespace ui
+
+#endif  // CHROME_BROWSER_UI_VIEWS_DARK_MODE_MANAGER_LINUX_H_
diff --git a/chrome/browser/ui/views/device_id/pen_id_browsertest_win.cc b/chrome/browser/ui/views/device_id/pen_id_browsertest_win.cc
new file mode 100644
index 0000000..1a930260
--- /dev/null
+++ b/chrome/browser/ui/views/device_id/pen_id_browsertest_win.cc
@@ -0,0 +1,202 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/win/core_winrt_util.h"
+#include "base/win/hstring_reference.h"
+#include "base/win/scoped_hstring.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/desktop_window_tree_host_win_test_api.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
+#include "ui/views/win/pen_event_processor.h"
+#include "ui/views/win/pen_id_handler.h"
+#include "ui/views/win/test_support/fake_ipen_device.h"
+#include "ui/views/win/test_support/fake_ipen_device_statics.h"
+
+using views::FakeIPenDevice;
+using views::FakeIPenDeviceStatics;
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+
+constexpr char kMainTestPageUrlPath[] = "/device_id/test.html";
+
+}  // namespace
+
+class PenIdBrowserTest : public InProcessBrowserTest {
+ public:
+  PenIdBrowserTest() = default;
+  ~PenIdBrowserTest() override = default;
+
+  // Implement InProcessBrowserTest.
+  void SetUpOnMainThread() override;
+  void TearDown() override;
+
+  // Helpers to execute the tests.
+  content::WebContents* GetDefaultWebContents() const;
+  void SimulatePenPointerDragEvent(int pointer_id);
+  void SimulatePenPointerEventAndStop(
+      int pointer_id,
+      base::OnceCallback<void(int)> simulate_event_function);
+  bool MouseEventCallback(int expected_value,
+                          const base::RepeatingClosure& quit_closure,
+                          const blink::WebMouseEvent& evt);
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
+};
+
+void PenIdBrowserTest::SetUpOnMainThread() {
+  https_server_.reset(
+      new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
+  https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(https_server_->Start());
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server_->GetURL(kMainTestPageUrlPath)));
+}
+
+void PenIdBrowserTest::TearDown() {
+  FakeIPenDeviceStatics::GetInstance()->SimulateAllPenDevicesRemoved();
+}
+
+content::WebContents* PenIdBrowserTest::GetDefaultWebContents() const {
+  return browser()->tab_strip_model()->GetActiveWebContents();
+}
+
+void PenIdBrowserTest::SimulatePenPointerEventAndStop(
+    int pointer_id,
+    base::OnceCallback<void(int)> simulate_event_function) {
+  base::RunLoop run_loop;
+  GetDefaultWebContents()
+      ->GetPrimaryMainFrame()
+      ->GetRenderWidgetHost()
+      ->RemoveMouseEventCallback(mouse_event_callback_);
+  mouse_event_callback_ = base::BindRepeating(
+      &PenIdBrowserTest::MouseEventCallback, base::Unretained(this), pointer_id,
+      run_loop.QuitClosure());
+  GetDefaultWebContents()
+      ->GetPrimaryMainFrame()
+      ->GetRenderWidgetHost()
+      ->AddMouseEventCallback(mouse_event_callback_);
+  std::move(simulate_event_function).Run(pointer_id);
+  run_loop.Run();
+}
+
+void PenIdBrowserTest::SimulatePenPointerDragEvent(int pointer_id) {
+  content::WebContents* web_contents = GetDefaultWebContents();
+
+  gfx::Rect container_bounds = web_contents->GetContainerBounds();
+  long x = container_bounds.width() / 2;
+  long y = container_bounds.height() / 2;
+  long offset_x = container_bounds.x();
+  long offset_y = container_bounds.y();
+
+  POINTER_PEN_INFO pen_info;
+  memset(&pen_info, 0, sizeof(POINTER_PEN_INFO));
+  pen_info.pointerInfo.pointerType = PT_PEN;
+  pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN;
+  // Since, SimulatePenEventForTesting considers the coordinates in relation
+  // to the screen, to centralize the click in the middle of the page, it is
+  // necessary to translate the pointer by the page container offset.
+  pen_info.pointerInfo.ptPixelLocationRaw.x = offset_x + x;
+  pen_info.pointerInfo.ptPixelLocationRaw.y = offset_y + y;
+
+  views::test::DesktopWindowTreeHostWinTestApi desktop_window_tree_host(
+      static_cast<views::DesktopWindowTreeHostWin*>(
+          web_contents->GetNativeView()->GetHost()));
+
+  desktop_window_tree_host.SimulatePenEventForTesting(WM_POINTERDOWN,
+                                                      pointer_id, pen_info);
+
+  // Drag the pointer to the first point.
+  x = 9 * container_bounds.width() / 20;
+  pen_info.pointerInfo.ptPixelLocationRaw.x = offset_x + x;
+  desktop_window_tree_host.SimulatePenEventForTesting(WM_POINTERUPDATE,
+                                                      pointer_id, pen_info);
+
+  // Drag the pointer to the second point.
+  x = 8 * container_bounds.width() / 20;
+  pen_info.pointerInfo.ptPixelLocationRaw.x = offset_x + x;
+  desktop_window_tree_host.SimulatePenEventForTesting(WM_POINTERUPDATE,
+                                                      pointer_id, pen_info);
+
+  // Lift the pointer device.
+  pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP;
+  desktop_window_tree_host.SimulatePenEventForTesting(WM_POINTERUP, pointer_id,
+                                                      pen_info);
+}
+
+bool PenIdBrowserTest::MouseEventCallback(
+    int expected_value,
+    const base::RepeatingClosure& quit_closure,
+    const blink::WebMouseEvent& evt) {
+  const int32_t id = evt.device_id;
+  if (evt.pointer_type == ui::EventPointerType::kPen) {
+    EXPECT_EQ(id, expected_value);
+  }
+  quit_closure.Run();
+  return false;
+}
+
+// Perform a pen drag for a pen that has a guid. Verify the correct
+// device id is propagated in the pointer event.
+// The test works by simulating a pen event in the PenDeviceStatics which
+// records a fake pen event for a given pointer id. Then, a pointer event is
+// simulated by injecting a pen event via
+// desktop_window_tree_host.SimulatePenEventForTesting. We bind a callback
+// (MouseEventCallback) so that when the browser sends the pen event, it
+// checks for the right device id.
+IN_PROC_BROWSER_TEST_F(PenIdBrowserTest, PenDeviceTest) {
+  int kPointerId1 = 0;
+  int kPointerId2 = 1;
+  int kPointerId3 = 2;
+  int kDeviceId1 = 0;
+  int kDeviceId2 = 1;
+
+  views::PenIdHandler::ScopedPenIdStaticsForTesting scoper(
+      &FakeIPenDeviceStatics::FakeIPenDeviceStaticsComPtr);
+  const auto fake_pen_device = Microsoft::WRL::Make<FakeIPenDevice>();
+  FakeIPenDeviceStatics::GetInstance()->SimulatePenEventGenerated(
+      kPointerId1, fake_pen_device);
+
+  const auto fake_pen_device_2 = Microsoft::WRL::Make<FakeIPenDevice>();
+  FakeIPenDeviceStatics::GetInstance()->SimulatePenEventGenerated(
+      kPointerId2, fake_pen_device_2);
+
+  FakeIPenDeviceStatics::GetInstance()->SimulatePenEventGenerated(
+      kPointerId3, fake_pen_device);
+
+  // We take advantage of the current implementation of device id to simplify
+  // the test. Device Id starts at 0 and iterates, so we expect the first
+  // device id to be 0 and the next one to be 1.
+  SimulatePenPointerEventAndStop(
+      kDeviceId1, base::BindOnce(&PenIdBrowserTest::SimulatePenPointerDragEvent,
+                                 base::Unretained(this)));
+  SimulatePenPointerEventAndStop(
+      kDeviceId2, base::BindOnce(&PenIdBrowserTest::SimulatePenPointerDragEvent,
+                                 base::Unretained(this)));
+  SimulatePenPointerEventAndStop(
+      kDeviceId1, base::BindOnce(&PenIdBrowserTest::SimulatePenPointerDragEvent,
+                                 base::Unretained(this)));
+}
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.cc
new file mode 100644
index 0000000..32a5f03
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.cc
@@ -0,0 +1,55 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h"
+
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/views/layout/flex_layout.h"
+
+// static
+std::unique_ptr<DownloadBubblePartialView> DownloadBubblePartialView::Create(
+    raw_ptr<Browser> browser,
+    raw_ptr<DownloadBubbleUIController> bubble_controller,
+    raw_ptr<DownloadBubbleNavigationHandler> navigation_handler,
+    std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+    base::OnceClosure on_mouse_entered_closure) {
+  if (rows.empty()) {
+    return nullptr;
+  }
+
+  return base::WrapUnique(new DownloadBubblePartialView(
+      browser, bubble_controller, navigation_handler, std::move(rows),
+      std::move(on_mouse_entered_closure)));
+}
+
+DownloadBubblePartialView::DownloadBubblePartialView(
+    raw_ptr<Browser> browser,
+    raw_ptr<DownloadBubbleUIController> bubble_controller,
+    raw_ptr<DownloadBubbleNavigationHandler> navigation_handler,
+    std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+    base::OnceClosure on_mouse_entered_closure)
+    : on_mouse_entered_closure_(std::move(on_mouse_entered_closure)) {
+  SetNotifyEnterExitOnChild(true);
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical);
+  const int preferred_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_BUBBLE_PREFERRED_WIDTH);
+
+  AddChildView(DownloadBubbleRowListView::CreateWithScroll(
+      /*is_partial_view=*/true, browser, bubble_controller, navigation_handler,
+      std::move(rows), preferred_width));
+}
+
+DownloadBubblePartialView::~DownloadBubblePartialView() = default;
+
+void DownloadBubblePartialView::OnMouseEntered(const ui::MouseEvent& event) {
+  if (on_mouse_entered_closure_) {
+    std::move(on_mouse_entered_closure_).Run();
+  }
+}
+
+BEGIN_METADATA(DownloadBubblePartialView, views::View)
+END_METADATA
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h
new file mode 100644
index 0000000..7b4170f4
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h
@@ -0,0 +1,51 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PARTIAL_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PARTIAL_VIEW_H_
+
+#include <memory>
+
+#include "base/functional/callback_forward.h"
+#include "chrome/browser/download/download_ui_model.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/view.h"
+
+class Browser;
+class DownloadBubbleUIController;
+class DownloadBubbleNavigationHandler;
+
+// This class encapsulates the "partial view" in the download bubble. This gives
+// a compact representation of downloads that recently completed.
+class DownloadBubblePartialView : public views::View {
+ public:
+  METADATA_HEADER(DownloadBubblePartialView);
+
+  static std::unique_ptr<DownloadBubblePartialView> Create(
+      raw_ptr<Browser> browser,
+      raw_ptr<DownloadBubbleUIController> bubble_controller,
+      raw_ptr<DownloadBubbleNavigationHandler> navigation_handler,
+      std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+      base::OnceClosure on_mouse_entered_closure);
+
+  DownloadBubblePartialView(const DownloadBubblePartialView&) = delete;
+  DownloadBubblePartialView& operator=(const DownloadBubblePartialView&) =
+      delete;
+  ~DownloadBubblePartialView() override;
+
+  // views::View
+  void OnMouseEntered(const ui::MouseEvent& event) override;
+
+ private:
+  DownloadBubblePartialView(
+      raw_ptr<Browser> browser,
+      raw_ptr<DownloadBubbleUIController> bubble_controller,
+      raw_ptr<DownloadBubbleNavigationHandler> navigation_handler,
+      std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+      base::OnceClosure on_mouse_entered_closure);
+
+  base::OnceClosure on_mouse_entered_closure_;
+};
+
+#endif
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.cc
index 4098912..e1612a7 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.cc
@@ -7,10 +7,13 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "chrome/browser/download/bubble/download_bubble_prefs.h"
+#include "chrome/browser/download/bubble/download_bubble_ui_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -20,18 +23,51 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/vector_icons.h"
 
-DownloadBubbleRowListView::DownloadBubbleRowListView(
+namespace {
+
+// 7.5 rows * 60 px per row = 450;
+constexpr int kMaxHeightForRowList = 450;
+
+}  // namespace
+
+//  static
+std::unique_ptr<views::View> DownloadBubbleRowListView::CreateWithScroll(
     bool is_partial_view,
     Browser* browser,
-    base::OnceClosure on_mouse_entered_closure)
+    DownloadBubbleUIController* bubble_controller,
+    DownloadBubbleNavigationHandler* navigation_handler,
+    std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+    int fixed_width) {
+  auto row_list_view =
+      std::make_unique<DownloadBubbleRowListView>(is_partial_view, browser);
+  for (DownloadUIModel::DownloadUIModelPtr& model : rows) {
+    // raw pointer is safe as the toolbar owns the bubble, which owns an
+    // individual row view.
+    row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>(
+        std::move(model), row_list_view.get(), bubble_controller,
+        navigation_handler, browser, fixed_width));
+  }
+
+  auto scroll_view = std::make_unique<views::ScrollView>();
+  scroll_view->SetContents(std::move(row_list_view));
+  scroll_view->ClipHeightTo(0, kMaxHeightForRowList);
+  scroll_view->SetHorizontalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kDisabled);
+  scroll_view->SetVerticalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kEnabled);
+  return scroll_view;
+}
+
+DownloadBubbleRowListView::DownloadBubbleRowListView(bool is_partial_view,
+                                                     Browser* browser)
     : is_partial_view_(is_partial_view),
       creation_time_(base::Time::Now()),
-      browser_(browser),
-      on_mouse_entered_closure_(std::move(on_mouse_entered_closure)) {
+      browser_(browser) {
   SetOrientation(views::LayoutOrientation::kVertical);
   SetNotifyEnterExitOnChild(true);
   if (IsIncognitoInfoRowEnabled()) {
@@ -90,12 +126,6 @@
       base::Time::Now() - creation_time_);
 }
 
-void DownloadBubbleRowListView::OnMouseEntered(const ui::MouseEvent& event) {
-  if (on_mouse_entered_closure_) {
-    std::move(on_mouse_entered_closure_).Run();
-  }
-}
-
 bool DownloadBubbleRowListView::IsIncognitoInfoRowEnabled() {
   Profile* profile = browser_->profile();
   return download::IsDownloadBubbleV2Enabled(profile) &&
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h
index 338668e..9861923 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h
@@ -7,6 +7,7 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
+#include "chrome/browser/download/download_ui_model.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/layout/flex_layout_view.h"
 
@@ -15,22 +16,27 @@
 class ImageView;
 }  // namespace views
 
+class DownloadBubbleUIController;
+class DownloadBubbleNavigationHandler;
+
 class DownloadBubbleRowListView : public views::FlexLayoutView {
  public:
   METADATA_HEADER(DownloadBubbleRowListView);
 
-  DownloadBubbleRowListView(
+  static std::unique_ptr<views::View> CreateWithScroll(
       bool is_partial_view,
       Browser* browser,
-      base::OnceClosure on_mouse_entered_closure = base::DoNothing());
+      DownloadBubbleUIController* bubble_controller,
+      DownloadBubbleNavigationHandler* navigation_handler,
+      std::vector<DownloadUIModel::DownloadUIModelPtr> rows,
+      int fixed_width);
+
+  DownloadBubbleRowListView(bool is_partial_view, Browser* browser);
   ~DownloadBubbleRowListView() override;
   DownloadBubbleRowListView(const DownloadBubbleRowListView&) = delete;
   DownloadBubbleRowListView& operator=(const DownloadBubbleRowListView&) =
       delete;
 
-  // views::FlexLayoutView
-  void OnMouseEntered(const ui::MouseEvent& event) override;
-
  private:
   bool IsIncognitoInfoRowEnabled();
 
@@ -38,8 +44,6 @@
   base::Time creation_time_;
   raw_ptr<Browser> browser_ = nullptr;
   raw_ptr<views::ImageView> info_icon_ = nullptr;
-  // Callback invoked when the user first hovers over the view.
-  base::OnceClosure on_mouse_entered_closure_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_ROW_LIST_VIEW_H_
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index 32c67c1..bfc50151 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_security_view.h"
@@ -69,8 +70,6 @@
 constexpr int kProgressRingRadius = 9;
 constexpr int kProgressRingRadiusTouchMode = 12;
 constexpr float kProgressRingStrokeWidth = 1.7f;
-// 7.5 rows * 60 px per row = 450;
-constexpr int kMaxHeightForRowList = 450;
 
 // Close the partial bubble after 5 seconds if the user doesn't interact with
 // it.
@@ -382,12 +381,22 @@
 
 std::unique_ptr<views::View> DownloadToolbarButtonView::GetPrimaryView() {
   if (is_primary_partial_view_) {
-    return CreateRowListView(bubble_controller_->GetPartialView());
-  } else {
-    // raw ptr is safe as the toolbar view owns the bubble.
-    return std::make_unique<DownloadDialogView>(
-        browser_, CreateRowListView(bubble_controller_->GetMainView()), this);
+    return DownloadBubblePartialView::Create(
+        browser_, bubble_controller_.get(), this,
+        bubble_controller_->GetPartialView(),
+        base::BindOnce(&DownloadToolbarButtonView::DeactivateAutoClose,
+                       base::Unretained(this)));
   }
+
+  std::unique_ptr<views::View> rows_with_scroll =
+      DownloadBubbleRowListView::CreateWithScroll(
+          /*is_partial_view=*/false, browser_, bubble_controller_.get(), this,
+          bubble_controller_->GetMainView(),
+          ChromeLayoutProvider::Get()->GetDistanceMetric(
+              views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
+  // raw ptr is safe as the toolbar view owns the bubble.
+  return std::make_unique<DownloadDialogView>(
+      browser_, std::move(rows_with_scroll), this);
 }
 
 void DownloadToolbarButtonView::OpenPrimaryDialog() {
@@ -457,9 +466,6 @@
       switcher_view->AddChildView(std::make_unique<DownloadBubbleSecurityView>(
           bubble_controller_.get(), this, bubble_delegate.get()));
   security_view_->SetVisible(false);
-  bubble_delegate->set_fixed_width(
-      ChromeLayoutProvider::Get()->GetDistanceMetric(
-          views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
   bubble_delegate->set_margins(GetPrimaryViewMargin());
   bubble_delegate->SetEnableArrowKeyTraversal(true);
   bubble_delegate_ = bubble_delegate.get();
@@ -522,36 +528,6 @@
   UpdateIcon();
 }
 
-std::unique_ptr<views::View> DownloadToolbarButtonView::CreateRowListView(
-    std::vector<DownloadUIModel::DownloadUIModelPtr> model_list) {
-  // Do not create empty partial view.
-  if (is_primary_partial_view_ && model_list.empty())
-    return nullptr;
-
-  auto row_list_view = std::make_unique<DownloadBubbleRowListView>(
-      is_primary_partial_view_, browser_,
-      base::BindOnce(&DownloadToolbarButtonView::DeactivateAutoClose,
-                     base::Unretained(this)));
-  const int bubble_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
-      views::DISTANCE_BUBBLE_PREFERRED_WIDTH);
-  for (DownloadUIModel::DownloadUIModelPtr& model : model_list) {
-    // raw pointer is safe as the toolbar owns the bubble, which owns an
-    // individual row view.
-    row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>(
-        std::move(model), row_list_view.get(), bubble_controller_.get(), this,
-        browser_, bubble_width));
-  }
-
-  auto scroll_view = std::make_unique<views::ScrollView>();
-  scroll_view->SetContents(std::move(row_list_view));
-  scroll_view->ClipHeightTo(0, kMaxHeightForRowList);
-  scroll_view->SetHorizontalScrollBarMode(
-      views::ScrollView::ScrollBarMode::kDisabled);
-  scroll_view->SetVerticalScrollBarMode(
-      views::ScrollView::ScrollBarMode::kEnabled);
-  return std::move(scroll_view);
-}
-
 void DownloadToolbarButtonView::ShowPendingDownloadStartedAnimation() {
   if (!has_pending_download_started_animation_) {
     return;
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
index 089dcc02..99c2996 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -112,9 +112,6 @@
 
   // Get the primary view, which may be the full or the partial view.
   std::unique_ptr<View> GetPrimaryView();
-  // Create a scrollable row list view for either the full or the partial view.
-  std::unique_ptr<View> CreateRowListView(
-      std::vector<DownloadUIModel::DownloadUIModelPtr> model_list);
 
   // If |has_pending_download_started_animation_| is true, shows an animation of
   // a download icon moving upwards towards the toolbar icon.
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
index c7d73a7..d5d921b 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
@@ -284,6 +284,8 @@
 
 void BrowserDesktopWindowTreeHostWin::HandleWindowMinimizedOrRestored(
     bool restored) {
+  DesktopWindowTreeHostWin::HandleWindowMinimizedOrRestored(restored);
+
   // This is necessary since OnWidgetVisibilityChanged() doesn't get called on
   // Windows when the window is minimized or restored.
   if (base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index a6969d0..e3cc11e 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -31,6 +31,8 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/sad_tab_helper.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -1333,6 +1335,21 @@
     }
 
     views = GetViewsMatchingDraggedContents(attached_context_);
+
+    // If we're dragging a saved group, resume tracking now that the group is
+    // re-attached.
+    if (header_drag_ &&
+        base::FeatureList::IsEnabled(features::kTabGroupsSave) &&
+        paused_saved_group_id_.has_value()) {
+      Browser* browser = BrowserView::GetBrowserViewForNativeWindow(
+                             GetAttachedBrowserWidget()->GetNativeWindow())
+                             ->browser();
+      SavedTabGroupKeyedService* const saved_tab_group_service =
+          SavedTabGroupServiceFactory::GetForProfile(browser->profile());
+      saved_tab_group_service->ResumeTrackingLocalTabGroup(
+          paused_saved_group_id_.value(), group_.value());
+      paused_saved_group_id_ = absl::nullopt;
+    }
   }
   DCHECK_EQ(views.size(), drag_data_.size());
   for (size_t i = 0; i < drag_data_.size(); ++i) {
@@ -1393,6 +1410,21 @@
 
   TabStripModel* attached_model = attached_context_->GetTabStripModel();
 
+  // If we're dragging a saved tab group, suspend tracking between Detach and
+  // Attach. Otherwise, the group will get emptied out as we close all the tabs.
+  if (header_drag_ && base::FeatureList::IsEnabled(features::kTabGroupsSave)) {
+    Browser* browser = BrowserView::GetBrowserViewForNativeWindow(
+                           GetAttachedBrowserWidget()->GetNativeWindow())
+                           ->browser();
+    SavedTabGroupKeyedService* const saved_tab_group_service =
+        SavedTabGroupServiceFactory::GetForProfile(browser->profile());
+    if (saved_tab_group_service->model()->Contains(group_.value())) {
+      paused_saved_group_id_ =
+          saved_tab_group_service->model()->Get(group_.value())->saved_guid();
+      saved_tab_group_service->PauseTrackingLocalTabGroup(group_.value());
+    }
+  }
+
   for (size_t i = first_tab_index(); i < drag_data_.size(); ++i) {
     int index = attached_model->GetIndexOfWebContents(drag_data_[i].contents);
     DCHECK_NE(TabStripModel::kNoTab, index);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 4d3d313..8bf3945 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "base/guid.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -661,6 +662,10 @@
   // group header, indicating that the entire group is being dragged together.
   absl::optional<tab_groups::TabGroupId> group_;
 
+  // The GUID of the saved tab group whose tracking is paused between paired
+  // Detach() and Attach() calls, if dragging a saved tab group between windows.
+  absl::optional<base::GUID> paused_saved_group_id_;
+
   // True until MoveAttached() is first invoked.
   bool initial_move_;
 
diff --git a/chrome/browser/ui/webui/settings/ash/accessibility_section.cc b/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
index a1456b3..832aab8 100644
--- a/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
+++ b/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
@@ -610,6 +610,12 @@
        IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_ROWS},
       {"chromeVoxVirtualBrailleDisplayColumns",
        IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_COLUMNS},
+      {"chromeVoxVirtualBrailleDisplayStyleLabel",
+       IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_LABEL},
+      {"chromeVoxVirtualBrailleDisplayStyleInterleave",
+       IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_INTERLEAVE},
+      {"chromeVoxVirtualBrailleDisplayStyleSideBySide",
+       IDS_SETTINGS_CHROMEVOX_VIRTUAL_BRAILLE_DISPLAY_STYLE_SIDE_BY_SIDE},
       {"chromeVoxEventLogLink", IDS_SETTINGS_CHROMEVOX_EVENT_LOG_LINK},
       {"chromeVoxEventLogDescription",
        IDS_SETTINGS_CHROMEVOX_EVENT_LOG_DESCRIPTION},
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
index 3cad9b2..8b911c6 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
@@ -230,6 +230,9 @@
   const ::ash::mojom::KeyboardPolicies& GetKeyboardPolicies() override {
     return *keyboard_policies_;
   }
+  const ::ash::mojom::MousePolicies& GetMousePolicies() override {
+    return *mouse_policies_;
+  }
   void SetKeyboardSettings(
       DeviceId id,
       ::ash::mojom::KeyboardSettingsPtr settings) override {
@@ -341,6 +344,8 @@
   std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_;
   ::ash::mojom::KeyboardPoliciesPtr keyboard_policies_ =
       ::ash::mojom::KeyboardPolicies::New();
+  ::ash::mojom::MousePoliciesPtr mouse_policies_ =
+      ::ash::mojom::MousePolicies::New();
 
   raw_ptr<InputDeviceSettingsController::Observer> observer_ = nullptr;
   int num_times_set_keyboard_settings_called_ = 0;
diff --git a/chrome/build/lacros-arm.pgo.txt b/chrome/build/lacros-arm.pgo.txt
index 1af27b1..c7e225d 100644
--- a/chrome/build/lacros-arm.pgo.txt
+++ b/chrome/build/lacros-arm.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-arm-generic-main-1680263957-adb63608cf762ceb277e1ca8bcf3a0107b29bfe9.profdata
\ No newline at end of file
+chrome-chromeos-arm-generic-main-1681142209-16e82d3afd37a5b4b3fa1184089be80bb4d5b8bd.profdata
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index adc7bb7..0d3312a 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1681127858-e54c2281dd7ff28372e53b44da6362c9b1464f53.profdata
+chrome-chromeos-amd64-generic-main-1681142209-512e8beeee05ab376f9612060abc61289c3408c7.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index ea26f1d9..2e1804b 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1681134549-cd38fcc9009e131ba26db70e79f49b384c59adb0.profdata
+chrome-mac-arm-main-1681149594-8673eca10d2a45dfd2c06b51191663449e27ba51.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 143f46fd..cfd45a64 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1681127858-5a08b62503a21cfeeb665ad9dc67224661d4c5e8.profdata
+chrome-win32-main-1681138712-2afdd834afb0e9cf20c0062a4e0c1bfe7a20a27a.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a80b7a2..664f9f4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1681127858-08660bfb9b7079f17ca39843ac32049c618b3768.profdata
+chrome-win64-main-1681138712-0c4808630b3480b8b03fceeb718112845fe387f3.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 91b1e98..7adddbd 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -1448,17 +1448,17 @@
 BASE_FEATURE(kWebShare, "WebShare", base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
-// Whether to enable "dark mode" enhancements in Mac Mojave or Windows 10 for
-// UIs implemented with web technologies.
+// Whether to enable "dark mode" enhancements in Mac Mojave, Windows 10, or
+// Linux for UIs implemented with web technologies.
 BASE_FEATURE(kWebUIDarkMode,
              "WebUIDarkMode",
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) ||
-        // BUILDFLAG(IS_CHROMEOS)
+        // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 );
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc b/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
index 443e14c6..4a081d0 100644
--- a/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
+++ b/chrome/renderer/accessibility/ax_tree_distiller_browsertest.cc
@@ -33,7 +33,7 @@
     const ui::AXMode ax_mode = ui::AXMode::kWebContents | ui::AXMode::kHTML |
                                ui::AXMode::kScreenReader;
     render_frame->CreateAXTreeSnapshotter(ax_mode)->Snapshot(
-        /* exclude_offscreen= */ false, /* max_nodes= */ 0,
+        /* max_nodes= */ 0,
         /* timeout= */ {}, &snapshot);
     ui::AXTree tree(snapshot);
     distiller_ = std::make_unique<AXTreeDistiller>(
diff --git a/chrome/renderer/resources/controlled_frame/controlled_frame.js b/chrome/renderer/resources/controlled_frame/controlled_frame.js
index 0669db3..b27ef1b 100644
--- a/chrome/renderer/resources/controlled_frame/controlled_frame.js
+++ b/chrome/renderer/resources/controlled_frame/controlled_frame.js
@@ -4,7 +4,7 @@
 
 // This module implements chrome-specific <controlledframe> Element.
 
-var ChromeWebViewImpl = require('chromeWebViewImpl').ChromeWebViewImpl;
+var ChromeWebViewImpl = require('chromeWebView').ChromeWebViewImpl;
 var registerElement = require('guestViewContainerElement').registerElement;
 var WebViewElement = require('webViewElement').WebViewElement;
 var WebViewAttributeNames = require('webViewConstants').WebViewAttributeNames;
diff --git a/chrome/services/sharing/nearby/BUILD.gn b/chrome/services/sharing/nearby/BUILD.gn
index 1ed7cef..fe2e3b3 100644
--- a/chrome/services/sharing/nearby/BUILD.gn
+++ b/chrome/services/sharing/nearby/BUILD.gn
@@ -12,6 +12,8 @@
     "nearby_connections_conversions.h",
     "nearby_connections_stream_buffer_manager.cc",
     "nearby_connections_stream_buffer_manager.h",
+    "nearby_presence.cc",
+    "nearby_presence.h",
     "nearby_shared_remotes.cc",
     "nearby_shared_remotes.h",
     "platform.cc",
@@ -40,6 +42,7 @@
   sources = [
     "nearby_connections_stream_buffer_manager_unittest.cc",
     "nearby_connections_unittest.cc",
+    "nearby_presence_unittest.cc",
   ]
 
   deps = [
diff --git a/chrome/services/sharing/nearby/nearby_presence.cc b/chrome/services/sharing/nearby/nearby_presence.cc
new file mode 100644
index 0000000..e9cbdc20
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_presence.cc
@@ -0,0 +1,18 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/nearby/nearby_presence.h"
+
+namespace ash::nearby::presence {
+
+NearbyPresence::NearbyPresence(
+    mojo::PendingReceiver<mojom::NearbyPresence> nearby_presence,
+    base::OnceClosure on_disconnect)
+    : nearby_presence_(this, std::move(nearby_presence)) {
+  nearby_presence_.set_disconnect_handler(std::move(on_disconnect));
+}
+
+NearbyPresence::~NearbyPresence() = default;
+
+}  // namespace ash::nearby::presence
diff --git a/chrome/services/sharing/nearby/nearby_presence.h b/chrome/services/sharing/nearby/nearby_presence.h
new file mode 100644
index 0000000..6a273c0
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_presence.h
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_SHARING_NEARBY_NEARBY_PRESENCE_H_
+#define CHROME_SERVICES_SHARING_NEARBY_NEARBY_PRESENCE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace ash::nearby::presence {
+
+// Implementation of the NearbyPresence mojo interface.
+// This class acts as a bridge to the NearbyPresence library which is pulled in
+// as a third_party dependency. It handles the translation from mojo calls to
+// native callbacks and types that the library expects. This class runs in a
+// sandboxed process and is called from the browser process.
+class NearbyPresence : public mojom::NearbyPresence {
+ public:
+  // Creates a new instance of the NearbyPresence library. This will allocate
+  // and initialize a new instance and hold on to the passed mojo pipes.
+  // |on_disconnect| is called when either mojo interface disconnects and should
+  // destroy this instamce.
+  NearbyPresence(mojo::PendingReceiver<mojom::NearbyPresence> nearby_presence,
+                 base::OnceClosure on_disconnect);
+  NearbyPresence(const NearbyPresence&) = delete;
+  NearbyPresence& operator=(const NearbyPresence&) = delete;
+  ~NearbyPresence() override;
+
+ private:
+  mojo::Receiver<mojom::NearbyPresence> nearby_presence_;
+};
+
+}  // namespace ash::nearby::presence
+
+#endif  // CHROME_SERVICES_SHARING_NEARBY_NEARBY_PRESENCE_H_
diff --git a/chrome/services/sharing/nearby/nearby_presence_unittest.cc b/chrome/services/sharing/nearby/nearby_presence_unittest.cc
new file mode 100644
index 0000000..d5183d5f
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_presence_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/nearby/nearby_presence.h"
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::nearby::presence {
+
+class NearbyPresenceTest : public testing::Test {
+ public:
+  NearbyPresenceTest() {
+    nearby_presence_ = std::make_unique<NearbyPresence>(
+        remote_.BindNewPipeAndPassReceiver(),
+        base::BindOnce(&NearbyPresenceTest::OnDisconnect,
+                       base::Unretained(this)));
+  }
+  ~NearbyPresenceTest() override = default;
+
+  void OnDisconnect() {}
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<NearbyPresence> nearby_presence_;
+  mojo::Remote<mojom::NearbyPresence> remote_;
+};
+
+TEST_F(NearbyPresenceTest, CreatePresenceMojom) {
+  EXPECT_TRUE(nearby_presence_);
+}
+
+}  // namespace ash::nearby::presence
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b0528c8..a78e135 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2511,6 +2511,7 @@
     if (is_win) {
       sources += [
         "../browser/accessibility/live_caption/live_caption_unavailability_notifier_browsertest.cc",
+        "../browser/ui/views/device_id/pen_id_browsertest_win.cc",
         "../browser/ui/views/web_apps/web_app_integration_browsertest_win.cc",
       ]
     }
@@ -6305,6 +6306,7 @@
     "//third_party/zlib/google:zip",
     "//ui/base:test_support",
     "//ui/base/clipboard:clipboard_test_support",
+    "//ui/color:color",
     "//ui/display:test_support",
     "//ui/events:gesture_detection",
     "//ui/events:test_support",
@@ -6625,6 +6627,7 @@
       "../browser/android/bookmarks/partner_bookmarks_shim_unittest.cc",
       "../browser/android/chrome_backup_agent_unittest.cc",
       "../browser/android/compositor/layer/tab_layer_unittest.cc",
+      "../browser/android/compositor/tab_content_manager_unittest.cc",
       "../browser/android/customtabs/detached_resource_request_unittest.cc",
       "../browser/android/customtabs/tab_interaction_recorder_android_unittest.cc",
       "../browser/android/explore_sites/block_site_task_unittest.cc",
@@ -6749,6 +6752,7 @@
       "//chrome/browser/reading_list/android:unit_tests",
       "//chrome/browser/share",
       "//chrome/browser/thumbnail:unit_tests",
+      "//chrome/browser/thumbnail/cc:features",
       "//chrome/browser/touch_to_fill/payments/android:public",
       "//chrome/services/media_gallery_util:unit_tests",
       "//components/back_forward_cache",
diff --git a/chrome/test/data/device_id/test.html b/chrome/test/data/device_id/test.html
new file mode 100644
index 0000000..ff0e5645
--- /dev/null
+++ b/chrome/test/data/device_id/test.html
@@ -0,0 +1,30 @@
+<!--
+    Copyright 2023 The Chromium Authors
+    Use of this source code is governed by a BSD-style license that can be
+    found in the LICENSE file.
+-->
+
+<html>
+    <head>
+        <!-- Centralizes the canvas element in the middle of the frame. -->
+        <style type="text/css">
+            .canvas {
+                display: block;
+                width: 100%;
+                height: 100%;
+                position: absolute;
+                outline-style: solid;
+                outline-width: 4px;
+                outline-color: red;
+                outline-offset: -4px;
+                top: 0;
+                bottom: 0;
+                left: 0;
+                right: 0;
+            }
+        </style>
+    </head>
+    <body onload="init()">
+        <canvas id="test_canvas" class="canvas" ></canvas>
+    </body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/webui/sanity_check_available_apis.js b/chrome/test/data/extensions/webui/sanity_check_available_apis.js
index 78ed7054..3b3d4131 100644
--- a/chrome/test/data/extensions/webui/sanity_check_available_apis.js
+++ b/chrome/test/data/extensions/webui/sanity_check_available_apis.js
@@ -20,6 +20,7 @@
   'loadTimes',
   'management',
   'metricsPrivate',
+  'readAnything',
   'runtime',
   'send',
   'test',
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_utils_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_utils_test.ts
index 7c4fda2..ae3787ad 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_utils_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_utils_test.ts
@@ -135,16 +135,16 @@
 
     // Multiple modifiers
     const actualMultiple = getSortedModifiers(['ctrl', 'shift', 'meta', 'alt']);
-    const expectedMultiple: string[] = ['ctrl', 'alt', 'shift', 'meta'];
+    const expectedMultiple: string[] = ['meta', 'ctrl', 'alt', 'shift'];
     assertTrue(areModifiersEqual(actualMultiple, expectedMultiple));
 
     const actualMultiple2 = getSortedModifiers(['ctrl', 'shift', 'meta']);
-    const expectedMultiple2: string[] = ['ctrl', 'shift', 'meta'];
+    const expectedMultiple2: string[] = ['meta', 'ctrl', 'shift'];
     assertTrue(areModifiersEqual(actualMultiple2, expectedMultiple2));
 
     const actualMultiple3 =
         getSortedModifiers(['shift', 'meta', 'ctrl', 'alt']);
-    const expectedMultiple3: string[] = ['ctrl', 'alt', 'shift', 'meta'];
+    const expectedMultiple3: string[] = ['meta', 'ctrl', 'alt', 'shift'];
     assertTrue(areModifiersEqual(actualMultiple3, expectedMultiple3));
   });
 
@@ -171,7 +171,7 @@
             Modifier.ALT | Modifier.CONTROL, /*keyCode=*/ 221,
             /*keyDisplay=*/ ']')));
     assertArrayEquals(
-        ['ctrl', 'alt', 'shift', 'meta'],
+        ['meta', 'ctrl', 'alt', 'shift'],
         getModifiersForAcceleratorInfo(createStandardAcceleratorInfo(
             Modifier.ALT | Modifier.CONTROL | Modifier.COMMAND | Modifier.SHIFT,
             /*keyCode=*/ 221,
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
index 05a906f7..69775b99 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
@@ -2203,8 +2203,7 @@
     assertTrue(areMatchesShowing());
 
     // The first match is showing. The second match is initially hidden.
-    let matchEls =
-        realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
+    let matchEls = realbox.$.matches.selectableMatchElements;
     assertEquals(1, matchEls.length);
 
     // The suggestion group header and the toggle button are visible.
@@ -2241,8 +2240,7 @@
     testProxy.handler.reset();
 
     // Second match is visible.
-    matchEls =
-        realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
+    matchEls = realbox.$.matches.selectableMatchElements;
     assertEquals(2, matchEls.length);
 
     // Hide the second match by clicking the toggle button.
@@ -2256,8 +2254,7 @@
         1, testProxy.handler.getCallCount('toggleSuggestionGroupIdVisibility'));
 
     // Second match is hidden.
-    matchEls =
-        realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
+    matchEls = realbox.$.matches.selectableMatchElements;
     assertEquals(1, matchEls.length);
 
     testProxy.handler.reset();
@@ -2271,8 +2268,7 @@
     assertEquals(
         1, testProxy.handler.getCallCount('toggleSuggestionGroupIdVisibility'));
     // Second match is visible again.
-    matchEls =
-        realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
+    matchEls = realbox.$.matches.selectableMatchElements;
     assertEquals(2, matchEls.length);
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js
index 5ea4c0f1..aa57d762 100644
--- a/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/chromevox_subpage_tests.js
@@ -147,6 +147,13 @@
       secondaryValue: true,
       type: ControlType.TOGGLE,
     },
+    {
+      id: 'virtualBrailleDisplayStyleDropdown',
+      prefKey: 'settings.a11y.chromevox.braille_side_by_side',
+      defaultValue: true,
+      secondaryValue: false,
+      type: ControlType.DROPDOWN,
+    },
   ];
 
   settingsControls.forEach(control => {
@@ -176,7 +183,7 @@
           // Make sure dropdown is set to the default value.
           await waitAfterNextRender(control);
           const selectElement = control.shadowRoot.querySelector('select');
-          assertEquals(defaultValue, selectElement.value);
+          assertEquals(String(defaultValue), selectElement.value);
           // Update dropdown to secondary value.
           selectElement.value = secondaryValue;
           selectElement.dispatchEvent(
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 6d34daf..097fdf4 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -596,11 +596,6 @@
       mocha.grep('SearchFeedback_OfficialBuild').run();
     });
     GEN('#endif');
-  } else if (testName === 'OsSettingsPage') {
-    // TODO(crbug.com/1411677): times out (flaky) debug builds
-    GEN('#if defined(NDEBUG)');
-    TEST_F(className, 'All', () => mocha.run());
-    GEN('#endif');
   } else {
     TEST_F(className, 'All', () => mocha.run());
   }
diff --git a/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.cc b/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.cc
index d01e83e..b476dbf3 100644
--- a/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.cc
+++ b/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.cc
@@ -234,6 +234,12 @@
   error_status_queue_.push(status);
 }
 
+void FakeHermesEuiccClient::SetNextInstallProfileFromActivationCodeResult(
+    HermesResponseStatus status) {
+  CHECK(status != HermesResponseStatus::kSuccess);
+  next_install_profile_result_ = status;
+}
+
 void FakeHermesEuiccClient::SetInteractiveDelay(base::TimeDelta delay) {
   interactive_delay_ = delay;
 }
@@ -252,6 +258,13 @@
     const std::string& activation_code,
     const std::string& confirmation_code,
     InstallCarrierProfileCallback callback) {
+  if (next_install_profile_result_.has_value()) {
+    std::move(callback).Run(next_install_profile_result_.value(),
+                            /*carrier_profile_path=*/nullptr);
+    next_install_profile_result_ = absl::nullopt;
+    return;
+  }
+
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&FakeHermesEuiccClient::DoInstallProfileFromActivationCode,
diff --git a/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.h b/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.h
index 3b296b7..6cae7cc2 100644
--- a/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.h
+++ b/chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.h
@@ -63,6 +63,8 @@
       const dbus::ObjectPath& euicc_path,
       const dbus::ObjectPath& carrier_profile_path) override;
   void QueueHermesErrorStatus(HermesResponseStatus status) override;
+  void SetNextInstallProfileFromActivationCodeResult(
+      HermesResponseStatus status) override;
   void SetInteractiveDelay(base::TimeDelta delay) override;
   std::string GenerateFakeActivationCode() override;
   bool GetLastRefreshProfilesRestoreSlotArg() override;
@@ -129,6 +131,10 @@
   // Counter to generate fake ids and properties for profiles.
   int fake_profile_counter_ = 0;
 
+  // When set, this will be returned as the result of the next attempt to
+  // install a profile using an activation code.
+  absl::optional<HermesResponseStatus> next_install_profile_result_;
+
   // Queue of error code to be returned from method calls.
   std::queue<HermesResponseStatus> error_status_queue_;
 
diff --git a/chromeos/ash/components/dbus/hermes/hermes_euicc_client.h b/chromeos/ash/components/dbus/hermes/hermes_euicc_client.h
index 73aa66c..e1c0ffa 100644
--- a/chromeos/ash/components/dbus/hermes/hermes_euicc_client.h
+++ b/chromeos/ash/components/dbus/hermes/hermes_euicc_client.h
@@ -98,6 +98,13 @@
     // method call.
     virtual void QueueHermesErrorStatus(HermesResponseStatus status) = 0;
 
+    // Sets the return for the next call to
+    // HermesEuiccClient::InstallProfileFromActivationCode(). The implementation
+    // of this method should only accept error statuses since clients expect
+    // addition information about the installed profile on success.
+    virtual void SetNextInstallProfileFromActivationCodeResult(
+        HermesResponseStatus status) = 0;
+
     // Set delay for interactive methods.
     virtual void SetInteractiveDelay(base::TimeDelta delay) = 0;
 
diff --git a/chromeos/ash/components/nearby/presence/BUILD.gn b/chromeos/ash/components/nearby/presence/BUILD.gn
index 677f767..7d17b33 100644
--- a/chromeos/ash/components/nearby/presence/BUILD.gn
+++ b/chromeos/ash/components/nearby/presence/BUILD.gn
@@ -40,6 +40,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos/ash/components/nearby/presence:presence",
+    "//mojo/public/cpp/bindings:bindings",
     "//testing/gtest",
   ]
 }
diff --git a/chromeos/ash/components/network/cellular_policy_handler.cc b/chromeos/ash/components/network/cellular_policy_handler.cc
index 92b5ab8..6fa75b3 100644
--- a/chromeos/ash/components/network/cellular_policy_handler.cc
+++ b/chromeos/ash/components/network/cellular_policy_handler.cc
@@ -24,7 +24,8 @@
 
 namespace {
 
-const int kInstallRetryLimit = 10;
+constexpr int kInstallRetryLimit = 3;
+constexpr base::TimeDelta kInstallRetryDelay = base::Days(1);
 
 constexpr net::BackoffEntry::Policy kRetryBackoffPolicy = {
     0,               // Number of initial errors to ignore.
@@ -216,7 +217,8 @@
     NET_LOG(ERROR) << "No non-cellular type Internet connectivity.";
     auto current_request = std::move(remaining_install_requests_.front());
     PopRequest();
-    ScheduleRetry(std::move(current_request));
+    ScheduleRetry(std::move(current_request),
+                  InstallRetryReason::kMissingNonCellularConnectivity);
     ProcessRequests();
     return;
   }
@@ -262,7 +264,7 @@
   auto current_request = std::move(remaining_install_requests_.front());
   PopRequest();
   if (!service_path) {
-    ScheduleRetry(std::move(current_request));
+    ScheduleRetry(std::move(current_request), InstallRetryReason::kOther);
     ProcessRequests();
     return;
   }
@@ -285,7 +287,7 @@
   auto current_request = std::move(remaining_install_requests_.front());
   PopRequest();
   if (hermes_status != HermesResponseStatus::kSuccess) {
-    ScheduleRetry(std::move(current_request));
+    ScheduleRetry(std::move(current_request), InstallRetryReason::kOther);
     ProcessRequests();
     return;
   }
@@ -306,7 +308,8 @@
 }
 
 void CellularPolicyHandler::ScheduleRetry(
-    std::unique_ptr<InstallPolicyESimRequest> request) {
+    std::unique_ptr<InstallPolicyESimRequest> request,
+    InstallRetryReason reason) {
   if (request->retry_backoff.failure_count() >= kInstallRetryLimit) {
     NET_LOG(ERROR) << "Install policy eSIM profile with SMDP address: "
                    << request->smdp_address << " failed " << kInstallRetryLimit
@@ -316,7 +319,15 @@
   }
 
   request->retry_backoff.InformOfRequest(/*succeeded=*/false);
-  base::TimeDelta retry_delay = request->retry_backoff.GetTimeUntilRelease();
+
+  if (reason != InstallRetryReason::kMissingNonCellularConnectivity) {
+    request->retry_backoff.SetCustomReleaseTime(base::TimeTicks::Now() +
+                                                kInstallRetryDelay);
+  }
+
+  const base::TimeDelta retry_delay =
+      request->retry_backoff.GetTimeUntilRelease();
+
   NET_LOG(ERROR) << "Install policy eSIM profile failed. Retrying in "
                  << retry_delay;
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
@@ -367,7 +378,7 @@
                  << ". Timed out waiting for EUICC or profile list.";
   auto current_request = std::move(remaining_install_requests_.front());
   PopRequest();
-  ScheduleRetry(std::move(current_request));
+  ScheduleRetry(std::move(current_request), InstallRetryReason::kOther);
   ProcessRequests();
 }
 
diff --git a/chromeos/ash/components/network/cellular_policy_handler.h b/chromeos/ash/components/network/cellular_policy_handler.h
index 36c4048..661ca63 100644
--- a/chromeos/ash/components/network/cellular_policy_handler.h
+++ b/chromeos/ash/components/network/cellular_policy_handler.h
@@ -66,6 +66,13 @@
                    const base::Value::Dict& onc_config);
 
  private:
+  // This enum allows us to treat a retry differently depending on what the
+  // reason for retrying is.
+  enum class InstallRetryReason {
+    kMissingNonCellularConnectivity = 0,
+    kOther,
+  };
+
   friend class CellularPolicyHandlerTest;
 
   // Represents policy eSIM install request parameters. Requests are queued and
@@ -110,7 +117,8 @@
       HermesResponseStatus hermes_status,
       absl::optional<dbus::ObjectPath> profile_path,
       absl::optional<std::string> service_path);
-  void ScheduleRetry(std::unique_ptr<InstallPolicyESimRequest> request);
+  void ScheduleRetry(std::unique_ptr<InstallPolicyESimRequest> request,
+                     InstallRetryReason reason);
   void PushRequestAndProcess(std::unique_ptr<InstallPolicyESimRequest> request);
   void PopRequest();
   absl::optional<dbus::ObjectPath> FindExistingMatchingESimProfile();
diff --git a/chromeos/ash/components/network/cellular_policy_handler_unittest.cc b/chromeos/ash/components/network/cellular_policy_handler_unittest.cc
index e83961f..a1e7b6c 100644
--- a/chromeos/ash/components/network/cellular_policy_handler_unittest.cc
+++ b/chromeos/ash/components/network/cellular_policy_handler_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chromeos/ash/components/dbus/hermes/hermes_clients.h"
+#include "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
 #include "chromeos/ash/components/dbus/shill/shill_clients.h"
 #include "chromeos/ash/components/dbus/shill/shill_device_client.h"
 #include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
@@ -53,7 +54,13 @@
     "Network.Cellular.ESim.Policy.ESimInstall.OperationResult.InitialAttempt";
 const char kInstallViaPolicyRetryOperationHistogram[] =
     "Network.Cellular.ESim.Policy.ESimInstall.OperationResult.Retry";
-const base::TimeDelta kInstallationRetryOnceDelay = base::Minutes(15);
+const char kUnmanagedCellularServicePath[] = "cellular_service_path";
+const char kUnmanagedCellularGuid[] = "unmanaged_cellular_guid";
+const char kUnmanagedCellularName[] = "unmanaged_cellular";
+const char kWifiServicePath[] = "wifi_service_path";
+const char kWifiGuid[] = "wifi_guid";
+const char kWifiName[] = "wifi";
+const base::TimeDelta kInstallationRetryDelay = base::Days(1);
 
 void CheckShillConfiguration(bool is_installed) {
   std::string service_path =
@@ -236,7 +243,7 @@
       EXPECT_FALSE(smdp_address);
       return;
     }
-    EXPECT_TRUE(smdp_address);
+    ASSERT_TRUE(smdp_address);
     EXPECT_FALSE(smdp_address->empty());
   }
 
@@ -368,18 +375,34 @@
 
 TEST_F(CellularPolicyHandlerTest, InstallProfileFailure) {
   SetupEuicc();
-  // Verify esim profile doesn't get installed when installing policy esim
-  // with a invalid SMDP address.
-  const std::string policy = GenerateCellularPolicy("000");
+
   base::HistogramTester histogram_tester;
+
+  // Make the first installation attempt fail, resulting in an immediate retry
+  // delay of |kInstallationRetryDelay|.
+  HermesEuiccClient::Get()
+      ->GetTestInterface()
+      ->SetNextInstallProfileFromActivationCodeResult(
+          HermesResponseStatus::kErrorSendHttpsFailure);
+
+  const std::string policy =
+      GenerateCellularPolicy(HermesEuiccClient::Get()
+                                 ->GetTestInterface()
+                                 ->GenerateFakeActivationCode());
   InstallESimPolicy(policy,
-                    /*activation_code=*/std::string(),
+                    HermesEuiccClient::Get()
+                        ->GetTestInterface()
+                        ->GenerateFakeActivationCode(),
                     /*expect_install_success=*/false);
-  FastForwardBy(kInstallationRetryOnceDelay);
+
+  histogram_tester.ExpectBucketCount(
+      kInstallViaPolicyOperationHistogram,
+      CellularESimInstaller::InstallESimProfileResult::kSuccess,
+      /*expected_count=*/0);
   histogram_tester.ExpectBucketCount(
       kInstallViaPolicyOperationHistogram,
       CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
-      /*expected_count=*/2);
+      /*expected_count=*/1);
   histogram_tester.ExpectBucketCount(
       kInstallViaPolicyInitialOperationHistogram,
       CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
@@ -387,9 +410,31 @@
   histogram_tester.ExpectBucketCount(
       kInstallViaPolicyRetryOperationHistogram,
       CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
-      /*expected_count=*/1);
+      /*expected_count=*/0);
   CheckShillConfiguration(/*is_installed=*/false);
   CheckIccidSmdpPairInPref(/*is_installed=*/false);
+
+  // Fast forward by |kInstallationRetryDelay| to trigger a retry. We use some
+  // buffer time since the retry mechanism doesn't happen synchronously.
+  FastForwardBy(kInstallationRetryDelay + base::Minutes(5));
+  histogram_tester.ExpectBucketCount(
+      kInstallViaPolicyOperationHistogram,
+      CellularESimInstaller::InstallESimProfileResult::kSuccess,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      kInstallViaPolicyOperationHistogram,
+      CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      kInstallViaPolicyInitialOperationHistogram,
+      CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      kInstallViaPolicyRetryOperationHistogram,
+      CellularESimInstaller::InstallESimProfileResult::kHermesInstallFailed,
+      /*expected_count=*/0);
+  CheckShillConfiguration(/*is_installed=*/true);
+  CheckIccidSmdpPairInPref(/*is_installed=*/true);
 }
 
 TEST_F(CellularPolicyHandlerTest, InstallOnSecondEUICC) {
@@ -485,19 +530,12 @@
                         ->GetTestInterface()
                         ->GenerateFakeActivationCode(),
                     /*expect_install_success=*/false);
-  FastForwardBy(kInstallationRetryOnceDelay);
+  FastForwardBy(kInstallationRetryDelay);
   CheckShillConfiguration(/*is_installed=*/false);
   CheckIccidSmdpPairInPref(/*is_installed=*/false);
 }
 
 TEST_F(CellularPolicyHandlerTest, NoInternetConnection) {
-  const char kUnmanagedCellularServicePath[] = "cellular_service_path";
-  const char kUnmanagedCellularGuid[] = "unmanaged_cellular_guid";
-  const char kUnmanagedCellularName[] = "unmanaged_cellular";
-  const char kWifiServicePath[] = "wifi_service_path";
-  const char kWifiGuid[] = "wifi_guid";
-  const char kWifiName[] = "wifi";
-
   SetupEuicc();
   auto* shill_service = ShillServiceClient::Get()->GetTestInterface();
   shill_service->ClearServices();
@@ -513,7 +551,8 @@
                         ->GetTestInterface()
                         ->GenerateFakeActivationCode(),
                     /*expect_install_success=*/false);
-  FastForwardBy(kInstallationRetryOnceDelay);
+  // Fast forward 6 minutes since the first retry should happen in 5 minutes.
+  FastForwardBy(base::Minutes(6));
   CheckShillConfiguration(/*is_installed=*/false);
   CheckIccidSmdpPairInPref(/*is_installed=*/false);
   // Verify that cellular type of internet connectivity should not trigger the
@@ -523,8 +562,8 @@
                             shill::kTypeCellular, shill::kStateOnline,
                             /*visible=*/true);
   base::RunLoop().RunUntilIdle();
-  // Fast forward 30 minutes since next retry should happen in 20 minutes.
-  FastForwardBy(base::Minutes(30));
+  // Fast forward 11 minutes since the first retry should happen in 10 minutes.
+  FastForwardBy(base::Minutes(11));
   CheckShillConfiguration(/*is_installed=*/false);
   CheckIccidSmdpPairInPref(/*is_installed=*/false);
   // Verify that installation succeeds when a non-cellular type internet is
@@ -533,8 +572,8 @@
                             shill::kTypeWifi, shill::kStateOnline,
                             /*visible=*/true);
   base::RunLoop().RunUntilIdle();
-  // Fast forward 50 minutes since next retry should happen in 40 minutes.
-  FastForwardBy(base::Minutes(50));
+  // Fast forward 21 minutes since the first retry should happen in 20 minutes.
+  FastForwardBy(base::Minutes(21));
   CheckShillConfiguration(/*is_installed=*/true);
   CheckIccidSmdpPairInPref(/*is_installed=*/true);
 }
diff --git a/chromeos/ash/services/nearby/public/mojom/BUILD.gn b/chromeos/ash/services/nearby/public/mojom/BUILD.gn
index e40a43c..3767ef9 100644
--- a/chromeos/ash/services/nearby/public/mojom/BUILD.gn
+++ b/chromeos/ash/services/nearby/public/mojom/BUILD.gn
@@ -29,6 +29,7 @@
     "nearby_connections_types.mojom",
     "nearby_decoder.mojom",
     "nearby_decoder_types.mojom",
+    "nearby_presence.mojom",
     "quick_start_decoder.mojom",
     "quick_start_decoder_types.mojom",
     "sharing.mojom",
diff --git a/chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom b/chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom
new file mode 100644
index 0000000..eaa80660
--- /dev/null
+++ b/chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom
@@ -0,0 +1,13 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.nearby.presence.mojom;
+
+// Main interface to control the NearbyPresence library. Implemented in a
+// sandboxed process. This interface is used by the NearbyPresenceService
+// running in the browser process to discover devices.
+interface NearbyPresence {
+// TODO(b/276642472): Add StartScan method and other related data structures as
+// detailed in go/cros-nearby-presence-service.
+};
diff --git a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
index 4b7f5e3..41c7b97c 100644
--- a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
+++ b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
@@ -42,7 +42,7 @@
   data_decoder::DataDecoder::ParseJsonIsolated(
       response_body->substr(strlen(kJsonSafetyPrefix)),
       base::BindOnce(&SearchResponseParser::OnJsonParsed,
-                     base::Unretained(this)));
+                     weak_factory_.GetWeakPtr()));
 }
 
 void SearchResponseParser::OnJsonParsed(
diff --git a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.h b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.h
index bfa80e1c..89d18cb 100644
--- a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.h
+++ b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
 
 namespace base {
@@ -43,6 +44,8 @@
   bool ProcessResult(const base::Value* result, QuickAnswer* quick_answer);
 
   SearchResponseParserCallback complete_callback_;
+
+  base::WeakPtrFactory<SearchResponseParser> weak_factory_{this};
 };
 
 }  // namespace quick_answers
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 24c73eb..dbf626e 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -362,4 +362,7 @@
   "tast.inputs.VirtualKeyboardDeadKeys.*@jacuzzi",
   "tast.inputs.PhysicalKeyboardShapeBasedChineseTyping.array_lacros",
   "tast.inputs.VirtualKeyboardSpeech.lacros",
+
+  # https://crbug.com/1431937
+  "tast.lacros.ShelfLaunch.primary",
 ]
diff --git a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
index 3871a4eb..35cae5fa 100644
--- a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -127,7 +127,8 @@
   bool IsVisible(views::CaptionButtonIcon type) const override {
     switch (type) {
       case views::CAPTION_BUTTON_ICON_MINIMIZE:
-        return frame_->widget_delegate()->CanMinimize();
+        return frame_->widget_delegate()->CanMinimize() &&
+               !TabletState::Get()->InTabletMode();
       case views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: {
         if (!frame_->widget_delegate()->CanMaximize()) {
           return false;
diff --git a/chromeos/ui/frame/caption_buttons/frame_size_button.cc b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
index f60b4f34..d67bb27 100644
--- a/chromeos/ui/frame/caption_buttons/frame_size_button.cc
+++ b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
@@ -320,7 +320,8 @@
     SetButtonsToNormalMode(FrameSizeButtonDelegate::Animate::kYes);
     return;
   }
-  if (event->type() == ui::ET_GESTURE_TAP_DOWN && delegate_->CanSnap()) {
+  if (event->type() == ui::ET_GESTURE_TAP_DOWN && delegate_->CanSnap() &&
+      !TabletState::Get()->InTabletMode()) {
     StartLongTapDelayTimer(*event);
 
     // Go through FrameCaptionButton's handling so that the button gets pressed.
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
index d43f19c5..ffad75c3 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
@@ -281,7 +281,7 @@
 }
 
 // static
-void MultitaskMenuView::SetSkipMouseOutDelayFoTesting(bool val) {
+void MultitaskMenuView::SetSkipMouseOutDelayForTesting(bool val) {
   g_skip_mouse_out_delay_for_testing = val;
 }
 
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_view.h b/chromeos/ui/frame/multitask_menu/multitask_menu_view.h
index 40baad70..d20a001 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_view.h
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_view.h
@@ -60,7 +60,7 @@
   // If the menu is opened because of mouse hover, moving the mouse outside the
   // menu for 3 seconds will result in it auto closing. This function reduces
   // that 3 second dealy to
-  static void SetSkipMouseOutDelayFoTesting(bool val);
+  static void SetSkipMouseOutDelayForTesting(bool val);
 
   // For testing.
   SplitButtonView* half_button_for_testing() {
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
index 2456f5a..3abcb22 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
@@ -129,7 +129,10 @@
 }
 
 // Tests that observer is called on form activity (input event).
-TEST_F(FormActivityTabHelperTest, TestObserverFormActivityFrameMessaging) {
+// TODO(crbug.com/1431960): Disabled test due to bot failure. Re-enable when
+// fixed.
+TEST_F(FormActivityTabHelperTest,
+       DISABLED_TestObserverFormActivityFrameMessaging) {
   LoadHtml(@"<form name='form-name'>"
             "<input type='input' name='field-name' id='fieldid'/>"
             "</form>");
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_journeys.xml b/components/browser_ui/styles/android/java/res/drawable/ic_journeys.xml
index 75b05f0..db0410e 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_journeys.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_journeys.xml
@@ -12,6 +12,6 @@
     android:viewportHeight="24"
     android:tint="@macro/default_icon_color_secondary">
   <path
-      android:fillColor="@android:color/white"
+      android:fillColor="?attr/globalLinkTextColor"
       android:pathData="M19,15c-1.3,0 -2.4,0.84 -2.82,2H11c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2h2c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4H7.82C7.4,3.84 6.3,3 5,3 3.34,3 2,4.34 2,6s1.34,3 3,3c1.3,0 2.4,-0.84 2.82,-2H13c1.1,0 2,0.9 2,2s-0.9,2 -2,2h-2c-2.21,0 -4,1.79 -4,4s1.79,4 4,4h5.18A2.996,2.996 0,0 0,22 18c0,-1.66 -1.34,-3 -3,-3zM5,7c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
 </vector>
diff --git a/components/cast_streaming/test/cast_message_port_sender_impl.cc b/components/cast_streaming/test/cast_message_port_sender_impl.cc
index cf7eab2..904bcf7e 100644
--- a/components/cast_streaming/test/cast_message_port_sender_impl.cc
+++ b/components/cast_streaming/test/cast_message_port_sender_impl.cc
@@ -25,6 +25,7 @@
 CastMessagePortSenderImpl::~CastMessagePortSenderImpl() = default;
 
 void CastMessagePortSenderImpl::MaybeClose() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // We may be called multiple times, but only want to close once.
   if (is_closed_) {
     return;
@@ -45,11 +46,13 @@
 
 void CastMessagePortSenderImpl::SetClient(
     openscreen::cast::MessagePort::Client& client) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   VLOG(2) << __func__;
   client_ = &client;
 }
 
 void CastMessagePortSenderImpl::ResetClient() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   client_ = nullptr;
   MaybeClose();
 }
@@ -58,6 +61,7 @@
     const std::string& sender_id,
     const std::string& message_namespace,
     const std::string& message) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   VLOG(3) << __func__;
   if (!message_port_) {
     return;
diff --git a/components/cast_streaming/test/cast_message_port_sender_impl.h b/components/cast_streaming/test/cast_message_port_sender_impl.h
index 77cfdb8..05f8e9d 100644
--- a/components/cast_streaming/test/cast_message_port_sender_impl.h
+++ b/components/cast_streaming/test/cast_message_port_sender_impl.h
@@ -7,6 +7,7 @@
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "components/cast/message_port/message_port.h"
 #include "third_party/openscreen/src/cast/common/public/message_port.h"
 
@@ -51,6 +52,7 @@
   base::OnceClosure on_close_;
   base::OnceClosure on_system_sender_message_received_;
   bool is_closed_ = false;
+  THREAD_CHECKER(thread_checker_);
 };
 
 }  // namespace cast_streaming
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index c9554890..d745593 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -800,13 +800,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // aura::WindowObserver overrides:
-void ClientControlledShellSurface::OnWindowDestroying(aura::Window* window) {
-  if (client_controlled_state_) {
-    client_controlled_state_->ResetDelegate();
-    client_controlled_state_ = nullptr;
-  }
-  ShellSurfaceBase::OnWindowDestroying(window);
-}
 
 void ClientControlledShellSurface::OnWindowAddedToRootWindow(
     aura::Window* window) {
@@ -1306,10 +1299,8 @@
 }
 
 void ClientControlledShellSurface::OnSurfaceDestroying(Surface* surface) {
-  if (client_controlled_state_) {
+  if (client_controlled_state_)
     client_controlled_state_->ResetDelegate();
-    client_controlled_state_ = nullptr;
-  }
   ShellSurfaceBase::OnSurfaceDestroying(surface);
 }
 
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index e0af90d..81c5f604 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -204,7 +204,6 @@
   void OnDeviceScaleFactorChanged(float old_dsf, float new_dsf) override;
 
   // Overridden from aura::WindowObserver:
-  void OnWindowDestroying(aura::Window* window) override;
   void OnWindowAddedToRootWindow(aura::Window* window) override;
 
   // Overridden from display::DisplayObserver:
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index f084c7d..709642a 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -1241,7 +1241,6 @@
   //    problematic with X11 as all of xwayland shares the same client.
   //  - Transitively kill all the wl_resources rooted at this window's
   //    wl_surface, which is not really supported in wayland.
-  surface_destroyed_callback_.Reset();
   OnSurfaceDestroying(root_surface());
 }
 
@@ -1286,14 +1285,11 @@
 // aura::WindowObserver overrides:
 
 void ShellSurfaceBase::OnWindowDestroying(aura::Window* window) {
-  surface_destroyed_callback_.Reset();
-
   if (window == parent_)
     SetParentInternal(nullptr);
   window->RemoveObserver(this);
-  if (widget_ && window == widget_->GetNativeWindow() && root_surface()) {
+  if (widget_ && window == widget_->GetNativeWindow() && root_surface())
     root_surface()->ThrottleFrameRate(false);
-  }
 }
 
 void ShellSurfaceBase::OnWindowPropertyChanged(aura::Window* window,
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
index c1b309dd..0f8f0d56 100644
--- a/components/exo/shell_surface_unittest.cc
+++ b/components/exo/shell_surface_unittest.cc
@@ -959,6 +959,36 @@
   EXPECT_FALSE(shell_surface.get());
 }
 
+void DestroyedCallbackCounter(int* count) {
+  *count += 1;
+}
+
+TEST_F(ShellSurfaceTest, ForceClose) {
+  gfx::Size buffer_size(64, 64);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface);
+  std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+  surface->Attach(buffer.get());
+  surface->Commit();
+  ASSERT_TRUE(shell_surface->GetWidget());
+
+  int surface_destroyed_ctr = 0;
+  shell_surface->set_surface_destroyed_callback(base::BindOnce(
+      &DestroyedCallbackCounter, base::Unretained(&surface_destroyed_ctr)));
+
+  // Since we did not set the close callback, closing this widget will have no
+  // effect.
+  shell_surface->GetWidget()->Close();
+  EXPECT_TRUE(shell_surface->GetWidget());
+  EXPECT_EQ(surface_destroyed_ctr, 0);
+
+  // CloseNow() will always destroy the widget.
+  shell_surface->GetWidget()->CloseNow();
+  EXPECT_FALSE(shell_surface->GetWidget());
+  EXPECT_EQ(surface_destroyed_ctr, 1);
+}
+
 TEST_F(ShellSurfaceTest, ConfigureCallback) {
   // Must be before shell_surface so it outlives it, for shell_surface's
   // destructor calls Configure() referencing these 4 variables.
diff --git a/components/exo/surface_tree_host.cc b/components/exo/surface_tree_host.cc
index c15c6dea2..a391bb1 100644
--- a/components/exo/surface_tree_host.cc
+++ b/components/exo/surface_tree_host.cc
@@ -125,6 +125,7 @@
   SetRootSurface(nullptr);
   LayerTreeFrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
       std::move(layer_tree_frame_sink_holder_));
+
   CleanUpCallbacks();
 }
 
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 8e876a7..c1ad58c 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -301,7 +301,6 @@
     "clients/security_delegate_binding_test.cc",
     "output_metrics_unittest.cc",
     "server_unittest.cc",
-    "shell_unittest.cc",
     "surface_augmenter_unittest.cc",
     "surface_unittest.cc",
     "wayland_aura_shell_server_test.cc",
diff --git a/components/exo/wayland/shell_unittest.cc b/components/exo/wayland/shell_unittest.cc
deleted file mode 100644
index 6778091..0000000
--- a/components/exo/wayland/shell_unittest.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/exo/wayland/wayland_display_output.h"
-
-#include <xdg-shell-client-protocol.h>
-#include <cstdint>
-
-#include "base/test/bind.h"
-#include "base/test/task_environment.h"
-#include "components/exo/shell_surface_util.h"
-#include "components/exo/wayland/test/client_util.h"
-#include "components/exo/wayland/test/server_util.h"
-#include "components/exo/wayland/test/wayland_server_test.h"
-
-namespace exo::wayland {
-
-namespace {
-
-// A custom shell object which can act as xdg toplevel or remote surface.
-// TODO(oshima): Implement more key events complete and move to a separate file.
-class ShellClientData : public test::TestClient::CustomData {
- public:
-  explicit ShellClientData(test::TestClient* client)
-      : client_(client),
-        surface_(wl_compositor_create_surface(client->compositor())) {}
-  ~ShellClientData() override { Close(); }
-
-  // Xdg Shell related methods.
-  static void OnXdgToplevelClose(void* data, struct xdg_toplevel* toplevel) {
-    static_cast<ShellClientData*>(data)->Close();
-  }
-
-  void CreateXdgToplevel() {
-    constexpr xdg_toplevel_listener xdg_toplevel_listener = {
-        [](void*, xdg_toplevel*, int32_t, int32_t, wl_array*) {},
-        &OnXdgToplevelClose,
-        [](void*, xdg_toplevel*, int32_t, int32_t) {},
-        [](void*, xdg_toplevel*, wl_array*) {},
-    };
-
-    xdg_surface_.reset(
-        xdg_wm_base_get_xdg_surface(client_->xdg_wm_base(), surface_.get()));
-    xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));
-    xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener,
-                              this);
-  }
-
-  // Remote Shell related methods.
-  static void OnRemoteSurfaceClose(void* data, zcr_remote_surface_v2*) {
-    static_cast<ShellClientData*>(data)->Close();
-  }
-
-  void CreateRemoteSurface() {
-    static constexpr zcr_remote_surface_v2_listener remote_surface_v2_listener =
-        {
-            &OnRemoteSurfaceClose,
-            [](void*, zcr_remote_surface_v2*, uint32_t) {},
-            [](void*, zcr_remote_surface_v2*, int, int, int, int) {},
-            [](void*, zcr_remote_surface_v2*, uint32_t, uint32_t, int32_t,
-               int32_t, int32_t, int32_t, uint32_t) {},
-            [](void*, zcr_remote_surface_v2*, uint32_t) {},
-            [](void*, zcr_remote_surface_v2*, int32_t, int32_t, int32_t) {},
-            [](void*, zcr_remote_surface_v2*, int32_t) {},
-            [](void*, zcr_remote_surface_v2*, wl_output*, int32_t, int32_t,
-               int32_t, int32_t, uint32_t) {},
-        };
-
-    remote_surface_.reset(zcr_remote_shell_v2_get_remote_surface(
-        client_->cr_remote_shell_v2(), surface_.get(),
-        ZCR_REMOTE_SHELL_V2_CONTAINER_DEFAULT));
-    zcr_remote_surface_v2_add_listener(remote_surface_.get(),
-                                       &remote_surface_v2_listener, this);
-  }
-
-  void Pin() {
-    zcr_remote_surface_v2_pin(remote_surface_.get(), /*trusted=*/true);
-  }
-
-  // Common to both xdg toplevel and remote surface.
-  void CreateAndAttachBuffer(const gfx::Size& size) {
-    buffer_ = client_->shm_buffer_factory()->CreateBuffer(0, size.width(),
-                                                          size.height());
-    wl_surface_attach(surface_.get(), buffer_->resource(), 0, 0);
-  }
-
-  void Commit() { wl_surface_commit(surface_.get()); }
-
-  void DestroySurface() { wl_surface_destroy(surface_.release()); }
-
-  void Close() {
-    close_called_ = true;
-    if (surface_) {
-      wl_surface_attach(surface_.get(), nullptr, 0, 0);
-    }
-    if (buffer_) {
-      wl_buffer_destroy(buffer_->resource());
-    }
-    buffer_.release();
-    if (xdg_toplevel_) {
-      xdg_toplevel_destroy(xdg_toplevel_.release());
-      xdg_surface_destroy(xdg_surface_.release());
-    }
-    if (remote_surface_) {
-      zcr_remote_surface_v2_destroy(remote_surface_.release());
-    }
-    if (surface_) {
-      wl_surface_destroy(surface_.release());
-    }
-  }
-
-  test::ResourceKey GetSurfaceResourceKey() const {
-    return test::client_util::GetResourceKey(surface_.get());
-  }
-
-  bool close_called() const { return close_called_; }
-
- private:
-  bool close_called_ = false;
-  test::TestClient* const client_;
-  std::unique_ptr<wl_surface> surface_;
-  std::unique_ptr<xdg_surface> xdg_surface_;
-  std::unique_ptr<xdg_toplevel> xdg_toplevel_;
-  std::unique_ptr<zcr_remote_surface_v2> remote_surface_;
-  std::unique_ptr<test::TestBuffer> buffer_;
-};
-
-enum TestCases {
-  XdgWidgetClose,
-  XdgWidgetCloseNow,
-  XdgWindowDelete,
-  RemoteWidgetClose,
-  RemoteWidgetCloseNow,
-  RemoteWindowDelete,
-};
-
-class ShellTest : public test::WaylandServerTest,
-                  public testing::WithParamInterface<TestCases> {
- public:
-  ShellTest() = default;
-  ShellTest(const ShellTest&) = delete;
-  ShellTest& operator=(const ShellTest&) = delete;
-  ~ShellTest() override = default;
-
-  bool IsXdgShell() {
-    return GetParam() == XdgWidgetCloseNow || GetParam() == XdgWindowDelete;
-  }
-
-  bool IsWidgetCloseNow() {
-    return GetParam() == XdgWidgetCloseNow ||
-           GetParam() == RemoteWidgetCloseNow;
-  }
-  bool IsWidgetClose() {
-    return GetParam() == XdgWidgetClose || GetParam() == RemoteWidgetClose;
-  }
-};
-
-}  // namespace
-
-INSTANTIATE_TEST_SUITE_P(Xdg,
-                         ShellTest,
-                         testing::Values(XdgWidgetClose,
-                                         XdgWidgetCloseNow,
-                                         XdgWindowDelete));
-INSTANTIATE_TEST_SUITE_P(Remote,
-                         ShellTest,
-                         testing::Values(RemoteWidgetClose,
-                                         RemoteWidgetCloseNow,
-                                         RemoteWindowDelete));
-
-// Make sure that xdg topevel/remote surfaces can be
-// destroyed via Widget::CloseNow and window deletion.
-// (b/276351837)
-TEST_P(ShellTest, ShellDestruction) {
-  test::ResourceKey surface_key;
-
-  PostToClientAndWait([&](test::TestClient* client) {
-    ASSERT_TRUE(client->InitShmBufferFactory(256 * 256 * 4));
-
-    auto data = std::make_unique<ShellClientData>(client);
-    auto* data_ptr = data.get();
-    client->set_data(std::move(data));
-    if (IsXdgShell()) {
-      data_ptr->CreateXdgToplevel();
-    } else {
-      data_ptr->CreateRemoteSurface();
-    }
-    data_ptr->CreateAndAttachBuffer({256, 256});
-    data_ptr->Commit();
-    surface_key = data_ptr->GetSurfaceResourceKey();
-  });
-
-  Surface* surface = test::server_util::GetUserDataForResource<Surface>(
-      server_.get(), surface_key);
-  auto* shell_surface =
-      GetShellSurfaceBaseForWindow(surface->window()->GetToplevelWindow());
-  ASSERT_TRUE(shell_surface);
-  ASSERT_TRUE(shell_surface->GetWidget()->IsVisible());
-  if (IsWidgetClose()) {
-    shell_surface->GetWidget()->Close();
-    base::RunLoop().RunUntilIdle();
-  } else if (IsWidgetCloseNow()) {
-    shell_surface->GetWidget()->CloseNow();
-  } else {
-    delete shell_surface->GetWidget()->GetNativeWindow();
-  }
-}
-
-using RemoteShellTest = test::WaylandServerTest;
-
-// Calling SetPined w/o commit should not crash (crbug.com/979128).
-TEST_F(RemoteShellTest, DestroyRootSurfaceBeforeCommit) {
-  test::ResourceKey surface_key;
-
-  PostToClientAndWait([&](test::TestClient* client) {
-    ASSERT_TRUE(client->InitShmBufferFactory(256 * 256 * 4));
-
-    auto data = std::make_unique<ShellClientData>(client);
-    auto* data_ptr = data.get();
-    client->set_data(std::move(data));
-    data_ptr->CreateRemoteSurface();
-    data_ptr->CreateAndAttachBuffer({256, 256});
-    surface_key = data_ptr->GetSurfaceResourceKey();
-  });
-  EXPECT_TRUE(test::server_util::GetUserDataForResource<Surface>(server_.get(),
-                                                                 surface_key));
-  PostToClientAndWait([&](test::TestClient* client) {
-    auto* data_ptr = client->GetDataAs<ShellClientData>();
-    data_ptr->DestroySurface();
-    data_ptr->Pin();
-  });
-
-  EXPECT_FALSE(test::server_util::GetUserDataForResource<Surface>(server_.get(),
-                                                                  surface_key));
-}
-
-}  // namespace exo::wayland
diff --git a/components/feature_engagement/README.md b/components/feature_engagement/README.md
index 6566b73..8729b32 100644
--- a/components/feature_engagement/README.md
+++ b/components/feature_engagement/README.md
@@ -181,7 +181,7 @@
 To enable UMA tracking, you need to make the following changes to the metrics
 configuration:
 
-1.  Add feature to the histogram variant `IPHFeatures` in:
+1.  Add feature to the histogram variant `IPHFeature` in:
     `//tools/metrics/histograms/metadata/feature_engagement/histograms.xml`.
     *   The variant name must match the `base::Feature` `name` member of your
         feature.
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 8f74a34d..4a14fab 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -1175,6 +1175,30 @@
   return from_on_device_provider && subtypes.contains(271);
 }
 
+void AutocompleteMatch::FilterOmniboxActions(
+    const std::vector<OmniboxActionId>& allowed_action_ids) {
+  // Short circuit if there's nothing to do.
+  if (actions.empty()) {
+    return;
+  }
+
+  // Find the type of object we can keep.
+  auto allowed_action_id_iter =
+      base::ranges::find_if(allowed_action_ids, [this](auto allowed_action_id) {
+        return GetActionWhere([allowed_action_id](const auto& action) {
+                 return action->ActionId() == allowed_action_id;
+               }) != nullptr;
+      });
+
+  auto allowed_action_id = allowed_action_id_iter != allowed_action_ids.end()
+                               ? *allowed_action_id_iter
+                               : OmniboxActionId::LAST;
+
+  std::erase_if(actions, [&](const auto& action) {
+    return action->ActionId() != allowed_action_id;
+  });
+}
+
 bool AutocompleteMatch::IsTrivialAutocompletion() const {
   return type == AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED ||
          type == AutocompleteMatchType::URL_WHAT_YOU_TYPED ||
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index d64efd5..0e2abb5 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -18,6 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/utf_offset_string_conversions.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/actions/omnibox_action_concepts.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/buildflags.h"
@@ -503,6 +504,11 @@
   // providers.
   bool IsOnDeviceSearchSuggestion() const;
 
+  // Filter OmniboxActions based on the supplied qualifiers.
+  // The order of the supplied qualifiers determines the preference.
+  void FilterOmniboxActions(
+      const std::vector<OmniboxActionId>& allowed_action_ids);
+
   // Returns whether the autocompletion is trivial enough that we consider it
   // an autocompletion for which the omnibox autocompletion code did not add
   // any value.
@@ -583,11 +589,8 @@
   // need to be selected. If no such action is found, returns nullptr.
   template <typename Predicate>
   OmniboxAction* GetActionWhere(Predicate predicate) const {
-    auto it = std::find_if(actions.begin(), actions.end(), predicate);
-    if (it == actions.end()) {
-      return nullptr;
-    }
-    return it->get();
+    auto it = base::ranges::find_if(actions, std::move(predicate));
+    return it != actions.end() ? it->get() : nullptr;
   }
 
   // The provider of this match, used to remember which provider the user had
diff --git a/components/omnibox/browser/autocomplete_match_unittest.cc b/components/omnibox/browser/autocomplete_match_unittest.cc
index 6a4e3fc..16186aa 100644
--- a/components/omnibox/browser/autocomplete_match_unittest.cc
+++ b/components/omnibox/browser/autocomplete_match_unittest.cc
@@ -10,15 +10,31 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "components/omnibox/browser/actions/omnibox_action.h"
+#include "components/omnibox/browser/actions/omnibox_pedal.h"
+#include "components/omnibox/browser/actions/omnibox_pedal_concepts.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/fake_autocomplete_provider.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 namespace {
 
+class FakeOmniboxAction : public OmniboxAction {
+ public:
+  explicit FakeOmniboxAction(OmniboxActionId id)
+      : OmniboxAction(LabelStrings(u"", u"", u"", u""), GURL{}, false),
+        id_(id) {}
+  OmniboxActionId ActionId() const override { return id_; }
+
+ private:
+  ~FakeOmniboxAction() override = default;
+  OmniboxActionId id_{};
+};
+
 void TestSetAllowedToBeDefault(int caseI,
                                const std::string input_text,
                                bool input_prevent_inline_autocomplete,
@@ -997,3 +1013,97 @@
       AutocompleteMatch::BetterDuplicate(create_match(history_provider, 500),
                                          create_match(history_provider, 510)));
 }
+
+TEST_F(AutocompleteMatchTest, FilterOmniboxActions) {
+  scoped_refptr<FakeAutocompleteProvider> provider =
+      new FakeAutocompleteProvider(AutocompleteProvider::Type::TYPE_SEARCH);
+  const OmniboxAction::LabelStrings dummy_labels(u"", u"", u"", u"");
+
+  using OmniboxActionId::ACTION_IN_SUGGEST;
+  using OmniboxActionId::HISTORY_CLUSTERS;
+  using OmniboxActionId::PEDAL;
+
+  struct FilterOmniboxActionsTestData {
+    std::string test_name;
+    // This is what will get added to the AutocompleteMatch.
+    std::vector<OmniboxActionId> actions_attached_to_match;
+    // This is the filter. Order of elements specifies the preference.
+    std::vector<OmniboxActionId> allowed_actions;
+    // This is the expected result.
+    std::vector<OmniboxActionId> resulting_actions;
+  } test_cases[]{
+      {"have nothing, want nothing", {}, {}, {}},
+      {"have nothing, want Pedals", {}, {PEDAL}, {}},
+      {"have Pedals, want nothing", {PEDAL}, {}, {}},
+      {"have Pedals, want Pedals", {PEDAL}, {PEDAL}, {PEDAL}},
+      {"have Pedals, want History Clusters", {PEDAL}, {HISTORY_CLUSTERS}, {}},
+      {"have Pedals, want History Clusters, then Pedals",
+       {PEDAL},
+       {HISTORY_CLUSTERS, PEDAL},
+       {PEDAL}},
+      {"have Pedals and History Clusters, want History Clusters, then Pedals",
+       {PEDAL, HISTORY_CLUSTERS},
+       {HISTORY_CLUSTERS, PEDAL},
+       {HISTORY_CLUSTERS}},
+      {"have Pedals and History Clusters, want Pedals, then History Clusters",
+       {PEDAL, HISTORY_CLUSTERS},
+       {PEDAL, HISTORY_CLUSTERS},
+       {PEDAL}},
+      {"have Pedals and History Clusters, want Actions in Suggest",
+       {PEDAL, HISTORY_CLUSTERS},
+       {ACTION_IN_SUGGEST},
+       {}},
+      {"have Pedals and History Clusters, want Actions in Suggest, then Pedals",
+       {PEDAL, HISTORY_CLUSTERS},
+       {ACTION_IN_SUGGEST, PEDAL},
+       {PEDAL}},
+      {"have Pedals, Actions and History Clusters, want Pedals",
+       {ACTION_IN_SUGGEST, PEDAL, HISTORY_CLUSTERS},
+       {PEDAL},
+       {PEDAL}},
+      {"have multiple, want Actions, then History Clusters, then Pedals",
+       // Mix: 4 pedals, 3 history clusters, 2 actions.
+       {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS, PEDAL, ACTION_IN_SUGGEST,
+        HISTORY_CLUSTERS, PEDAL, HISTORY_CLUSTERS, PEDAL},
+       {ACTION_IN_SUGGEST, HISTORY_CLUSTERS, PEDAL},
+       {ACTION_IN_SUGGEST, ACTION_IN_SUGGEST}},
+      {"have multiple, want History Clusters, then Actions, then Pedals",
+       // Mix: 4 pedals, 3 history clusters, 2 actions.
+       {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS, PEDAL, ACTION_IN_SUGGEST,
+        HISTORY_CLUSTERS, PEDAL, HISTORY_CLUSTERS, PEDAL},
+       {HISTORY_CLUSTERS, ACTION_IN_SUGGEST, PEDAL},
+       {HISTORY_CLUSTERS, HISTORY_CLUSTERS, HISTORY_CLUSTERS}},
+      {"have multiple, want Pedals, then History Clusters, then Actions",
+       // Mix: 4 pedals, 3 history clusters, 2 actions.
+       {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS, PEDAL, ACTION_IN_SUGGEST,
+        HISTORY_CLUSTERS, PEDAL, HISTORY_CLUSTERS, PEDAL},
+       {PEDAL, HISTORY_CLUSTERS, ACTION_IN_SUGGEST},
+       {PEDAL, PEDAL, PEDAL, PEDAL}},
+      {"have multiple, want nothing",
+       // Mix: 4 pedals, 3 history clusters, 2 actions.
+       {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS, PEDAL, ACTION_IN_SUGGEST,
+        HISTORY_CLUSTERS, PEDAL, HISTORY_CLUSTERS, PEDAL},
+       {},
+       {}}};
+
+  for (const auto& test_case : test_cases) {
+    AutocompleteMatch match(provider.get(), 1, false,
+                            AutocompleteMatchType::SEARCH_SUGGEST_ENTITY);
+
+    // Populate match with requested actions.
+    for (auto& action_id : test_case.actions_attached_to_match) {
+      match.actions.push_back(
+          base::MakeRefCounted<FakeOmniboxAction>(action_id));
+    }
+
+    match.FilterOmniboxActions(test_case.allowed_actions);
+    EXPECT_EQ(match.actions.size(), test_case.resulting_actions.size())
+        << "while testing variant: " << test_case.test_name;
+
+    for (size_t index = 0u; index < match.actions.size(); ++index) {
+      EXPECT_EQ(match.actions[index]->ActionId(),
+                test_case.resulting_actions[index])
+          << "while testing variant: " << test_case.test_name;
+    }
+  }
+}
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 7c35641..15268c3 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -25,6 +25,7 @@
 #include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "components/history_clusters/core/config.h"
+#include "components/omnibox/browser/actions/omnibox_action_concepts.h"
 #include "components/omnibox/browser/actions/omnibox_pedal.h"
 #include "components/omnibox/browser/actions/omnibox_pedal_provider.h"
 #include "components/omnibox/browser/autocomplete_grouper_sections.h"
@@ -525,6 +526,34 @@
         << debug_info;
   }
 #endif
+
+  if constexpr (is_android) {
+    TrimOmniboxActions();
+  }
+}
+
+void AutocompleteResult::TrimOmniboxActions() {
+  // Platform rules:
+  // Android:
+  // - First two positions allow all types of OmniboxActionId
+  // - Third slot permits only PEDALs and HISTORY_CLUSTERS.
+  // - Slots 4 and beyond permit only HISTORY_CLUSTERS.
+  // - In every case, ACTION_IN_SUGGEST is preferred over HISTORY_CLUSTERS
+  // - In every case, HISTORY_CLUSTERS is preferred over PEDALs.
+  std::vector<OmniboxActionId> include_all{OmniboxActionId::ACTION_IN_SUGGEST,
+                                           OmniboxActionId::HISTORY_CLUSTERS,
+                                           OmniboxActionId::PEDAL};
+  std::vector<OmniboxActionId> include_at_most_pedals{
+      OmniboxActionId::HISTORY_CLUSTERS, OmniboxActionId::PEDAL};
+  std::vector<OmniboxActionId> include_at_most_history_clusters{
+      OmniboxActionId::HISTORY_CLUSTERS};
+
+  for (size_t index = 0u; index < matches_.size(); ++index) {
+    matches_[index].FilterOmniboxActions(
+        index < 2   ? include_all
+        : index < 3 ? include_at_most_pedals
+                    : include_at_most_history_clusters);
+  }
 }
 
 void AutocompleteResult::GroupAndDemoteMatchesInGroups() {
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index b3dae4b..59439b3e 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -138,6 +138,9 @@
   // Called after matches are deduped and sorted and before they are culled.
   void GroupAndDemoteMatchesInGroups();
 
+  // Filter and remove OmniboxActions according to Platform-specific rules.
+  void TrimOmniboxActions();
+
   // Sets |action| in matches that have Pedal-triggering text.
   void AttachPedalsToMatches(const AutocompleteInput& input,
                              const AutocompleteProviderClient& client);
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 14ddc32..e01fd5e 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -50,6 +50,18 @@
 
 namespace {
 
+class FakeOmniboxAction : public OmniboxAction {
+ public:
+  explicit FakeOmniboxAction(OmniboxActionId id)
+      : OmniboxAction(LabelStrings(u"", u"", u"", u""), GURL{}, false),
+        id_(id) {}
+  OmniboxActionId ActionId() const override { return id_; }
+
+ private:
+  ~FakeOmniboxAction() override = default;
+  OmniboxActionId id_{};
+};
+
 struct AutocompleteMatchTestData {
   std::string destination_url;
   AutocompleteMatch::Type type;
@@ -2886,4 +2898,128 @@
     AssertResultMatches(result, expected_data.begin(), expected_data.size());
   }
 }
+
+TEST_F(AutocompleteResultTest, Android_TrimOmniboxActions) {
+  scoped_refptr<FakeAutocompleteProvider> provider =
+      new FakeAutocompleteProvider(AutocompleteProvider::Type::TYPE_SEARCH);
+  const OmniboxAction::LabelStrings dummy_labels(u"", u"", u"", u"");
+
+  using OmniboxActionId::ACTION_IN_SUGGEST;
+  using OmniboxActionId::HISTORY_CLUSTERS;
+  using OmniboxActionId::PEDAL;
+  using OmniboxActionId::UNKNOWN;
+  const std::set<OmniboxActionId> all_actions_to_test{ACTION_IN_SUGGEST,
+                                                      HISTORY_CLUSTERS, PEDAL};
+
+  struct FilterOmniboxActionsTestData {
+    std::string test_name;
+    std::vector<std::vector<OmniboxActionId>> input_matches_and_actions;
+    std::vector<std::vector<OmniboxActionId>> result_matches_and_actions;
+  } test_cases[]{
+      {"No actions attached to matches", {{}, {}, {}, {}}, {{}, {}, {}, {}}},
+      {"Pedals shown only in top three slots",
+       {{PEDAL}, {PEDAL}, {PEDAL}, {PEDAL}},
+       {{PEDAL}, {PEDAL}, {PEDAL}, {}}},
+      {"Actions are shown only in top two slots",
+       {{ACTION_IN_SUGGEST},
+        {ACTION_IN_SUGGEST},
+        {ACTION_IN_SUGGEST},
+        {ACTION_IN_SUGGEST}},
+       {{ACTION_IN_SUGGEST}, {ACTION_IN_SUGGEST}, {}, {}}},
+      {"History Clusters are allowed everywhere",
+       {{HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS}},
+       {{HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS}}},
+      {"Actions are promoted over Pedals; positions dictate preference",
+       {{ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL}},
+       {{ACTION_IN_SUGGEST}, {ACTION_IN_SUGGEST}, {PEDAL}, {}}},
+      {"Actions are promoted over History clusters; positions dictate "
+       "preference",
+       {{ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL},
+        {ACTION_IN_SUGGEST, PEDAL}},
+       {{ACTION_IN_SUGGEST}, {ACTION_IN_SUGGEST}, {PEDAL}, {}}},
+      {"Actions are promoted over History clusters; positions dictate "
+       "preference",
+       {{ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {ACTION_IN_SUGGEST, HISTORY_CLUSTERS}},
+       {{ACTION_IN_SUGGEST},
+        {ACTION_IN_SUGGEST},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS}}},
+      {"History clusters are promoted over Pedals; positions dictate "
+       "preference",
+       {{PEDAL, HISTORY_CLUSTERS},
+        {PEDAL, HISTORY_CLUSTERS},
+        {PEDAL, HISTORY_CLUSTERS},
+        {PEDAL, HISTORY_CLUSTERS}},
+       {{HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS}}},
+      {"All variants for every position leaves only one appropriate variant",
+       {{PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS},
+        {PEDAL, ACTION_IN_SUGGEST, HISTORY_CLUSTERS}},
+       {{ACTION_IN_SUGGEST},
+        {ACTION_IN_SUGGEST},
+        {HISTORY_CLUSTERS},
+        {HISTORY_CLUSTERS}}},
+  };
+
+  // Crete matches following the `input_matches_and_actions` input.
+  // The input specifies what type of OMNIBOX_ACTION should be added to every
+  // individual match.
+  // Once done, run the trimming and verify that the output contains exactly the
+  // matches we want to see.
+  auto run_test = [&](const FilterOmniboxActionsTestData& data) {
+    // Create AutocompleteResult from the test data
+    AutocompleteResult result;
+    for (const auto& actions : data.input_matches_and_actions) {
+      AutocompleteMatch match(provider.get(), 1, false,
+                              AutocompleteMatchType::SEARCH_SUGGEST_ENTITY);
+      for (auto& action_id : actions) {
+        match.actions.push_back(
+            base::MakeRefCounted<FakeOmniboxAction>(action_id));
+      }
+      result.AppendMatches({std::move(match)});
+    }
+
+    // Run the trimmer.
+    result.TrimOmniboxActions();
+
+    // Check results.
+    EXPECT_EQ(result.size(), data.result_matches_and_actions.size())
+        << "while testing variant: " << data.test_name;
+
+    for (size_t index = 0u; index < result.size(); ++index) {
+      const auto* match = result.match_at(index);
+      const auto& expected_actions = data.result_matches_and_actions[index];
+      EXPECT_EQ(match->actions.size(), expected_actions.size());
+      for (size_t action_index = 0u; action_index < expected_actions.size();
+           ++action_index) {
+        EXPECT_EQ(expected_actions[action_index],
+                  match->actions[action_index]->ActionId())
+            << "match " << index << "action " << action_index
+            << " while testing variant: " << data.test_name;
+      }
+    }
+  };
+
+  for (const auto& test_case : test_cases) {
+    run_test(test_case);
+  }
+}
 #endif  // BUILDFLAG(IS_ANDROID)
diff --git a/components/omnibox/browser/page_classification_functions.cc b/components/omnibox/browser/page_classification_functions.cc
index e9f9b7c..cb5a3a12 100644
--- a/components/omnibox/browser/page_classification_functions.cc
+++ b/components/omnibox/browser/page_classification_functions.cc
@@ -12,7 +12,6 @@
          (classification == OEP::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS) ||
          (classification == OEP::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS) ||
          (classification == OEP::NTP_REALBOX) ||
-         (classification == OEP::ANDROID_SHORTCUTS_WIDGET) ||
          (classification == OEP::NTP_ZPS_PREFETCH);
 }
 
@@ -23,6 +22,7 @@
           OEP::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT) ||
          (classification ==
           OEP::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT) ||
+         (classification == OEP::ANDROID_SHORTCUTS_WIDGET) ||
          (classification == OEP::SRP_ZPS_PREFETCH);
 }
 
diff --git a/components/os_crypt/async/common/encryptor.cc b/components/os_crypt/async/common/encryptor.cc
index 15599cb..4550797 100644
--- a/components/os_crypt/async/common/encryptor.cc
+++ b/components/os_crypt/async/common/encryptor.cc
@@ -15,6 +15,7 @@
 #include "components/os_crypt/sync/os_crypt.h"
 #include "crypto/aead.h"
 #include "crypto/random.h"
+#include "mojo/public/cpp/bindings/default_construct_tag.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace os_crypt_async {
@@ -39,7 +40,7 @@
   }
 }
 
-Encryptor::Key::Key() = default;
+Encryptor::Key::Key(mojo::DefaultConstruct::Tag) {}
 
 Encryptor::Key::Key(Key&& other) = default;
 Encryptor::Key& Encryptor::Key::operator=(Key&& other) = default;
@@ -51,6 +52,7 @@
 }
 
 Encryptor::Encryptor() = default;
+Encryptor::Encryptor(mojo::DefaultConstruct::Tag) : Encryptor() {}
 
 Encryptor::Encryptor(Encryptor&& other) = default;
 Encryptor& Encryptor::operator=(Encryptor&& other) = default;
diff --git a/components/os_crypt/async/common/encryptor.h b/components/os_crypt/async/common/encryptor.h
index bfc3f22..3fecabe 100644
--- a/components/os_crypt/async/common/encryptor.h
+++ b/components/os_crypt/async/common/encryptor.h
@@ -12,6 +12,7 @@
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
+#include "mojo/public/cpp/bindings/default_construct_tag.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace mojo {
@@ -49,10 +50,8 @@
 
     static const size_t kAES256GCMKeySize = 256u / 8u;
 
-    // Mojo must be able to construct this class for serialization.
-    // TODO(crbug.com/1427202): Remove this once mojo::DefaultConstruct::Tag can
-    // be used instead.
-    Key();
+    // Mojo uses this public constructor for serialization.
+    explicit Key(mojo::DefaultConstruct::Tag);
 
     Key(base::span<const uint8_t> key, const mojom::Algorithm& algo);
 
@@ -81,13 +80,8 @@
 
   using KeyRing = std::map</*tag=*/std::string, Key>;
 
-  // Create an encryptor with no keys or encryption provider. In this case, all
-  // encryption operations will be delegated to OSCrypt.
-  // Must be public for mojo to be able to construct this class for
-  // serialization.
-  // TODO(crbug.com/1427202): Remove this once mojo::DefaultConstruct::Tag can
-  // be used instead.
-  Encryptor();
+  // Mojo uses this public constructor for serialization.
+  explicit Encryptor(mojo::DefaultConstruct::Tag);
 
   ~Encryptor();
 
@@ -123,6 +117,10 @@
 
   FRIEND_TEST_ALL_PREFIXES(EncryptorTraitsTest, TraitsRoundTrip);
 
+  // Create an encryptor with no keys or encryption provider. In this case, all
+  // encryption operations will be delegated to OSCrypt.
+  Encryptor();
+
   // Create an encryptor with a set of `keys`. The `provider_for_encryption`
   // specifies which provider is used for encryption, and must have a
   // corresponding key in `keys`.
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 3e98d89..f1e7294 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -2313,8 +2313,7 @@
   std::unique_ptr<content::AXTreeSnapshotter> snapshotter;
   if (delegate_->ShouldGenerateTaggedPDF()) {
     snapshotter = render_frame()->CreateAXTreeSnapshotter(ui::AXMode::kPDF);
-    snapshotter->Snapshot(/* exclude_offscreen= */ false,
-                          /* max_node_count= */ 0,
+    snapshotter->Snapshot(/* max_node_count= */ 0,
                           /* timeout= */ {}, &metafile.accessibility_tree());
   }
 
@@ -2784,8 +2783,7 @@
   // http://crbug.com/1039817
   if (snapshotter_ && page_number == 0) {
     ui::AXTreeUpdate accessibility_tree;
-    snapshotter_->Snapshot(/* exclude_offscreen= */ false,
-                           /* max_node_count= */ 0,
+    snapshotter_->Snapshot(/* max_node_count= */ 0,
                            /* timeout= */ {}, &accessibility_tree);
     GetPrintManagerHost()->SetAccessibilityTree(
         print_pages_params_->params->document_cookie, accessibility_tree);
diff --git a/components/translate/content/browser/per_frame_content_translate_driver.cc b/components/translate/content/browser/per_frame_content_translate_driver.cc
index 1c6833ad..70103725 100644
--- a/components/translate/content/browser/per_frame_content_translate_driver.cc
+++ b/components/translate/content/browser/per_frame_content_translate_driver.cc
@@ -352,7 +352,6 @@
                          weak_pointer_factory_.GetWeakPtr(),
                          capture_begin_time)),
       ui::AXMode::kWebContents,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 5000,
       /* timeout= */ {});
 
diff --git a/components/translate/core/common/translate_util.cc b/components/translate/core/common/translate_util.cc
index 294405fc..e0095f4 100644
--- a/components/translate/core/common/translate_util.cc
+++ b/components/translate/core/common/translate_util.cc
@@ -50,7 +50,7 @@
 
 BASE_FEATURE(kDesktopPartialTranslate,
              "DesktopPartialTranslate",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const base::FeatureParam<int>
     kDesktopPartialTranslateTextSelectionMaxCharacters{
         &kDesktopPartialTranslate,
diff --git a/content/README.md b/content/README.md
index 70b741e5..5ff6fff 100644
--- a/content/README.md
+++ b/content/README.md
@@ -56,7 +56,9 @@
 vendor-specific logic.
 
 ## Architectural Diagram
-TODO: Draw a modern diagram.
+![Chrome browser depends on content, which as a whole depends on Chromium's
+  low-level libraries and on the constituent parts of
+  //content.](./architecture.png)
 
 See an older diagram at: https://www.chromium.org/developers/content-module.
 
diff --git a/content/architecture.dot b/content/architecture.dot
new file mode 100644
index 0000000..4711d60e
--- /dev/null
+++ b/content/architecture.dot
@@ -0,0 +1,70 @@
+digraph {
+  graph[bgcolor=transparent]
+  node[shape=plaintext]
+
+  Embedder[label=<
+    <table border="1" cellborder="0">
+      <tr>
+        <td port="left1" width="25"></td>
+        <td port="left2" width="25"></td>
+        <td port="center" width="200">Embedder</td>
+        <td port="right" width="50"></td>
+      </tr>
+    </table>
+  >]
+  Content[label=<
+    <table border="1" cellborder="0">
+      <tr>
+        <td port="left1" width="30"></td>
+        <td port="left2" width="30"></td>
+        <td port="center" width="60">Content</td>
+        <td port="right" width="60"></td>
+      </tr>
+    </table>
+  >]
+  Embedder:center->Content:center[label="Content\nPublic API"]
+
+  Blink[label=<
+    <table border="1" cellborder="0">
+      <tr>
+        <td port="left" width="50"></td>
+        <td width="50">Blink</td>
+        <td port="right" width="50"></td>
+      </tr>
+    </table>
+  >]
+
+  {rank=same net, V8}
+  net[label=<
+    <table border="1" cellborder="0">
+      <tr><td>//net</td></tr>
+    </table>
+  >]
+  V8[label=<
+    <table border="1" cellborder="0">
+      <tr><td>V8</td></tr>
+    </table>
+  >]
+  base[label=<
+    <table border="1" cellborder="0">
+      <tr>
+        <td port="left1" width="25"></td>
+        <td port="left2" width="25"></td>
+        <td port="center" width="200">//base</td>
+        <td port="right" width="50"></td>
+      </tr>
+    </table>
+  >]
+
+  Embedder:left1->base:left1
+  Embedder:right->Blink:right[dir=both, label="Blink\nPublic API"]
+  Embedder:left2->net[dir=both]
+
+  Content->base:center
+  Content:left1->net[dir=both]
+  Content:left2->V8[dir=both]
+  Content:right->Blink:left[dir=both]
+  Blink:left->V8[dir=both]
+  Blink->base:right
+  net->base:left2
+}
diff --git a/content/architecture.png b/content/architecture.png
new file mode 100644
index 0000000..53e089d
--- /dev/null
+++ b/content/architecture.png
Binary files differ
diff --git a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
index 99d11e8..016c14f 100644
--- a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
+++ b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
@@ -83,7 +83,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
@@ -154,7 +153,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
@@ -200,7 +198,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
@@ -267,7 +264,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
@@ -322,7 +318,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter_complete)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter_complete.Wait();
@@ -338,7 +333,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter_contents)),
       ui::AXMode::kWebContents,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter_contents.Wait();
@@ -396,7 +390,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::AXMode::kPDF,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
@@ -468,44 +461,6 @@
   EXPECT_EQ(2, *td->GetTableCellColSpan());
 }
 
-IN_PROC_BROWSER_TEST_F(SnapshotAXTreeBrowserTest, ExcludeOffscreen) {
-  GURL url(R"HTML(data:text/html,<body>
-                  <style> p { margin: 50px; } </style>
-                  <script>
-                    for (let i = 0; i < 100; i++) {
-                      let p = document.createElement('p');
-                      p.innerHTML = i;
-                      document.body.append(p);
-                    }
-                  </script>
-                  </body>)HTML");
-  EXPECT_TRUE(NavigateToURL(shell(), url));
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-
-  AXTreeSnapshotWaiter waiter;
-  web_contents->RequestAXTreeSnapshot(
-      base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
-                     base::Unretained(&waiter)),
-      ui::kAXModeComplete,
-      /* exclude_offscreen= */ true,
-      /* max_nodes= */ 0,
-      /* timeout= */ {});
-  waiter.Wait();
-
-  // Dump the whole tree if one of the assertions below fails
-  // to aid in debugging why it failed.
-  //  SCOPED_TRACE(waiter.snapshot().ToString());
-
-  // If we didn't exclude offscreen nodes, thee would be at least 200 nodes on
-  // the page (2 for every paragraph). By excluding offscreen nodes, we should
-  // get between 20 and 40 total, depending on the platform and screen
-  // size.. Allow the test to pass if there are anything fewer than 60
-  // nodes to add a bit of buffer.
-  EXPECT_LT(waiter.snapshot().nodes.size(), 60U);
-}
-
 IN_PROC_BROWSER_TEST_F(SnapshotAXTreeBrowserTest, MaxNodes) {
   GURL url(R"HTML(data:text/html,<body>
                   <style> p { margin: 50px; } </style>
@@ -531,7 +486,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       ui::kAXModeComplete,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 10,
       /* timeout= */ {});
   waiter.Wait();
@@ -572,7 +526,6 @@
         base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                        base::Unretained(&waiter)),
         ui::kAXModeComplete,
-        /* exclude_offscreen= */ false,
         /* max_nodes= */ 0,
         /* timeout= */ {});
     waiter.Wait();
@@ -590,7 +543,6 @@
         base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                        base::Unretained(&waiter)),
         ui::kAXModeComplete,
-        /* exclude_offscreen= */ false,
         /* max_nodes= */ 0,
         /* timeout= */ base::Milliseconds(1));
     waiter.Wait();
@@ -626,7 +578,6 @@
       base::BindOnce(&AXTreeSnapshotWaiter::ReceiveSnapshot,
                      base::Unretained(&waiter)),
       mode,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 0,
       /* timeout= */ {});
   waiter.Wait();
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc
index 59b8d964..d79d1c5d 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -763,7 +763,10 @@
 
   if (base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi) &&
       blink::features::kPrivateAggregationApiEnabledInFledge.Get() &&
-      blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get()) {
+      (blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get() ||
+       base::FeatureList::IsEnabled(
+           blink::features::
+               kPrivateAggregationApiFledgeExtensionsLocalTestingOverride))) {
     fenced_frame_reporter_->OnForEventPrivateAggregationRequestsReceived(
         std::move(private_aggregation_requests_non_reserved_));
   }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 1aa38a3..43c31d210 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7887,7 +7887,10 @@
     const std::string& event_type) {
   if (!base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi) ||
       !blink::features::kPrivateAggregationApiEnabledInFledge.Get() ||
-      !blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get()) {
+      (!blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get() &&
+       !base::FeatureList::IsEnabled(
+           blink::features::
+               kPrivateAggregationApiFledgeExtensionsLocalTestingOverride))) {
     mojo::ReportBadMessage(
         "FLEDGE extensions must be enabled to use reportEvent() for private "
         "aggregation events.");
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index d616c81..a7cbd153 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -930,7 +930,6 @@
   GetWebContents()->RequestAXTreeSnapshot(
       base::BindOnce(CombineTextNodesAndMakeCallback, std::move(callback)),
       ui::AXMode::kWebContents,
-      /* exclude_offscreen= */ false,
       /* max_nodes= */ 5000,
       /* timeout= */ {});
 }
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index f64d535..1cdd7d3 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -6380,18 +6380,28 @@
                          describe_shared_storage_worklet_impl_param);
 
 class SharedStoragePrivateAggregationDisabledBrowserTest
-    : public SharedStorageBrowserTestBase {
+    : public SharedStorageBrowserTestBase,
+      public testing::WithParamInterface<bool> {
  public:
   SharedStoragePrivateAggregationDisabledBrowserTest() {
+    shared_storage_feature_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{blink::features::kSharedStorageAPI,
+          {{"SharedStorageWorkletImplementationType",
+            BlinkStyleWorkletImplementation() ? "blink_style" : "legacy"}}}},
+        /*disabled_features=*/{});
     private_aggregation_feature_.InitAndDisableFeature(
         blink::features::kPrivateAggregationApi);
   }
 
+  bool BlinkStyleWorkletImplementation() override { return GetParam(); }
+
  private:
+  base::test::ScopedFeatureList shared_storage_feature_;
   base::test::ScopedFeatureList private_aggregation_feature_;
 };
 
-IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationDisabledBrowserTest,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationDisabledBrowserTest,
                        PrivateAggregationNotDefined) {
   EXPECT_TRUE(NavigateToURL(shell(),
                             https_server()->GetURL("a.test", kSimplePagePath)));
@@ -6405,26 +6415,41 @@
                          &out_script_url);
 
   ASSERT_EQ(1u, console_observer.messages().size());
-  EXPECT_EQ("ReferenceError: privateAggregation is not defined",
-            base::UTF16ToUTF8(console_observer.messages()[0].message));
+  EXPECT_THAT(base::UTF16ToUTF8(console_observer.messages()[0].message),
+              testing::HasSubstr("privateAggregation is not defined"));
   EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kError,
             console_observer.messages()[0].log_level);
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         SharedStoragePrivateAggregationDisabledBrowserTest,
+                         testing::Bool(),
+                         describe_shared_storage_worklet_impl_param);
+
 class SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest
-    : public SharedStorageBrowserTestBase {
+    : public SharedStorageBrowserTestBase,
+      public testing::WithParamInterface<bool> {
  public:
   SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest() {
+    shared_storage_feature_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{blink::features::kSharedStorageAPI,
+          {{"SharedStorageWorkletImplementationType",
+            BlinkStyleWorkletImplementation() ? "blink_style" : "legacy"}}}},
+        /*disabled_features=*/{});
     private_aggregation_feature_.InitAndEnableFeatureWithParameters(
         blink::features::kPrivateAggregationApi,
         {{"enabled_in_shared_storage", "false"}});
   }
 
+  bool BlinkStyleWorkletImplementation() override { return GetParam(); }
+
  private:
+  base::test::ScopedFeatureList shared_storage_feature_;
   base::test::ScopedFeatureList private_aggregation_feature_;
 };
 
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest,
     PrivateAggregationNotDefined) {
   EXPECT_TRUE(NavigateToURL(shell(),
@@ -6439,14 +6464,21 @@
                          &out_script_url);
 
   ASSERT_EQ(1u, console_observer.messages().size());
-  EXPECT_EQ("ReferenceError: privateAggregation is not defined",
-            base::UTF16ToUTF8(console_observer.messages()[0].message));
+  EXPECT_THAT(base::UTF16ToUTF8(console_observer.messages()[0].message),
+              testing::HasSubstr("privateAggregation is not defined"));
   EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kError,
             console_observer.messages()[0].log_level);
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest,
+    testing::Bool(),
+    describe_shared_storage_worklet_impl_param);
+
 class SharedStoragePrivateAggregationEnabledBrowserTest
-    : public SharedStorageBrowserTestBase {
+    : public SharedStorageBrowserTestBase,
+      public testing::WithParamInterface<bool> {
  public:
   // TODO(alexmt): Consider factoring out along with FLEDGE definition.
   class TestPrivateAggregationManagerImpl
@@ -6461,6 +6493,12 @@
   };
 
   SharedStoragePrivateAggregationEnabledBrowserTest() {
+    shared_storage_feature_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{blink::features::kSharedStorageAPI,
+          {{"SharedStorageWorkletImplementationType",
+            BlinkStyleWorkletImplementation() ? "blink_style" : "legacy"}}}},
+        /*disabled_features=*/{});
     private_aggregation_feature_.InitAndEnableFeature(
         blink::features::kPrivateAggregationApi);
   }
@@ -6500,12 +6538,15 @@
     return *browser_client_;
   }
 
+  bool BlinkStyleWorkletImplementation() override { return GetParam(); }
+
  protected:
   url::Origin a_test_origin_;
 
  private:
   raw_ptr<PrivateAggregationHost, DanglingUntriaged> private_aggregation_host_;
 
+  base::test::ScopedFeatureList shared_storage_feature_;
   base::test::ScopedFeatureList private_aggregation_feature_;
 
   base::MockRepeatingCallback<void(AggregatableReportRequest,
@@ -6516,7 +6557,7 @@
       browser_client_;
 };
 
-IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationEnabledBrowserTest,
                        BasicTest) {
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
@@ -6560,7 +6601,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationEnabledBrowserTest,
                        RejectedTest) {
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
@@ -6585,13 +6626,21 @@
                          &out_script_url);
 
   ASSERT_EQ(1u, console_observer.messages().size());
-  EXPECT_EQ("TypeError: BigInt must be non-negative",
-            base::UTF16ToUTF8(console_observer.messages()[0].message));
+
+  if (BlinkStyleWorkletImplementation()) {
+    EXPECT_THAT(
+        base::UTF16ToUTF8(console_observer.messages()[0].message),
+        testing::HasSubstr(
+            "contribution['bucket'] is negative or does not fit in 128 bits"));
+  } else {
+    EXPECT_EQ("TypeError: BigInt must be non-negative",
+              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,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationEnabledBrowserTest,
                        MultipleRequests) {
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
@@ -6638,7 +6687,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationEnabledBrowserTest,
                        PrivateAggregationPermissionsPolicyNone) {
   GURL url = https_server()->GetURL(
       "a.test",
@@ -6669,14 +6718,14 @@
                          &out_script_url);
 
   EXPECT_EQ(1u, console_observer.messages().size());
-  EXPECT_EQ(
-      "TypeError: The \"private-aggregation\" Permissions Policy denied the "
-      "method on privateAggregation",
-      base::UTF16ToUTF8(console_observer.messages()[0].message));
+  EXPECT_THAT(
+      base::UTF16ToUTF8(console_observer.messages()[0].message),
+      testing::HasSubstr("The \"private-aggregation\" Permissions Policy "
+                         "denied the method on privateAggregation"));
 }
 
 // This is a regression test for crbug.com/1428110.
-IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationEnabledBrowserTest,
                        SimultaneousOperationsReportsArentBatchedTogether) {
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
@@ -6742,6 +6791,11 @@
   EXPECT_EQ(num_one_contribution_reports, 1);
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         SharedStoragePrivateAggregationEnabledBrowserTest,
+                         testing::Bool(),
+                         describe_shared_storage_worklet_impl_param);
+
 class SharedStorageSelectURLLimitBrowserTest
     : public SharedStorageBrowserTestBase,
       public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 19b3ab75..3ed7f87 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -703,7 +703,6 @@
               weak_factory_.GetWeakPtr(), std::move(j_view_structure_root),
               std::move(j_view_structure_builder), std::move(j_callback)),
           ui::AXMode(ui::kAXModeComplete.flags() | ui::AXMode::kHTMLMetadata),
-          /* exclude_offscreen= */ false,
           /* max_nodes= */ 5000,
           /* timeout= */ base::Seconds(2));
 }
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index de9824e7..d645d913 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1783,7 +1783,6 @@
 
 void WebContentsImpl::RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
                                             ui::AXMode ax_mode,
-                                            bool exclude_offscreen,
                                             size_t max_nodes,
                                             base::TimeDelta timeout) {
   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RequestAXTreeSnapshot",
@@ -1794,7 +1793,6 @@
   // delete |combiner|.
   auto params = mojom::SnapshotAccessibilityTreeParams::New();
   params->ax_mode = ax_mode.flags();
-  params->exclude_offscreen = exclude_offscreen;
   params->max_nodes = max_nodes;
   params->timeout = timeout;
 
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 31ab11ad..cebf67d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -382,7 +382,6 @@
   const std::u16string& GetLoadStateHost() override;
   void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
                              ui::AXMode ax_mode,
-                             bool exclude_offscreen,
                              size_t max_nodes,
                              base::TimeDelta timeout) override;
   uint64_t GetUploadSize() override;
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index c7cc095..12f4a20 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -342,12 +342,6 @@
   // See ui/accessibility/ax_mode.h for valid values of |ax_mode|.
   uint32 ax_mode;
 
-  // If true, nodes that are entirely offscreen will have their entire
-  // subtree excluded.  Note that this heuristic is imperfect, and
-  // an absolute-positioned node that's visible, but whose ancestors
-  // are entirely offscreen, may get excluded.
-  bool exclude_offscreen;
-
   // The maximum number of nodes to snapshot before exiting early.
   // Note that this is not a hard limit; once this limit is reached a
   // few more nodes may be added in order to ensure a well-formed
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 4bb32072..8baf744 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -486,7 +486,6 @@
       base::OnceCallback<void(const ui::AXTreeUpdate&)>;
   virtual void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
                                      ui::AXMode ax_mode,
-                                     bool exclude_offscreen,
                                      size_t max_nodes,
                                      base::TimeDelta timeout) = 0;
 
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 9757e12..e3b0e06 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -183,6 +183,10 @@
     {switches::kEnablePrivacySandboxAdsApis,
      std::cref(blink::features::kPrivateAggregationApi),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+    {switches::kEnablePrivacySandboxAdsApis,
+     std::cref(blink::features::
+                   kPrivateAggregationApiFledgeExtensionsLocalTestingOverride),
+     base::FeatureList::OVERRIDE_ENABLE_FEATURE},
   };
 
   std::vector<base::FeatureList::FeatureOverrideInfo> overrides;
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 128369c..28252aaf 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -67,10 +67,6 @@
   // Return in |accessibility_tree| a snapshot of the accessibility tree
   // for the frame with the given accessibility mode.
   //
-  // - |exclude_offscreen| excludes a subtree if a node is entirely offscreen,
-  //   but note that this heuristic is imperfect, and an aboslute-positioned
-  //   node that's visible, but whose ancestors are entirely offscreen, may
-  //   get excluded.
   // - |max_nodes_count| specifies the maximum number of nodes to snapshot
   //   before exiting early. Note that this is not a hard limit; once this limit
   //   is reached a few more nodes may be added in order to ensure a
@@ -79,8 +75,7 @@
   //   (per frame), specified in milliseconds. Like max_node_count, this is not
   //   a hard limit, and once this/ limit is reached a few more nodes may
   //   be added in order to ensure a well-formed tree. Use 0 for no timeout.
-  virtual void Snapshot(bool exclude_offscreen,
-                        size_t max_node_count,
+  virtual void Snapshot(size_t max_node_count,
                         base::TimeDelta timeout,
                         ui::AXTreeUpdate* accessibility_tree) = 0;
 
diff --git a/content/renderer/accessibility/aom_content_ax_tree.cc b/content/renderer/accessibility/aom_content_ax_tree.cc
index b46c81a..e04f300 100644
--- a/content/renderer/accessibility/aom_content_ax_tree.cc
+++ b/content/renderer/accessibility/aom_content_ax_tree.cc
@@ -129,8 +129,7 @@
 bool AomContentAxTree::ComputeAccessibilityTree() {
   ui::AXTreeUpdate tree_update;
   AXTreeSnapshotterImpl snapshotter(render_frame_, ui::kAXModeComplete);
-  snapshotter.Snapshot(/* exclude_offscreen= */ false,
-                       /* max_node_count= */ 0,
+  snapshotter.Snapshot(/* max_node_count= */ 0,
                        /* timeout= */ {}, &tree_update);
   CHECK(tree_.Unserialize(tree_update)) << tree_.error();
   return true;
diff --git a/content/renderer/accessibility/ax_tree_snapshotter_impl.cc b/content/renderer/accessibility/ax_tree_snapshotter_impl.cc
index eddbfe5b..97865603 100644
--- a/content/renderer/accessibility/ax_tree_snapshotter_impl.cc
+++ b/content/renderer/accessibility/ax_tree_snapshotter_impl.cc
@@ -28,17 +28,16 @@
 
 AXTreeSnapshotterImpl::~AXTreeSnapshotterImpl() = default;
 
-void AXTreeSnapshotterImpl::Snapshot(bool exclude_offscreen,
-                                     size_t max_node_count,
+void AXTreeSnapshotterImpl::Snapshot(size_t max_node_count,
                                      base::TimeDelta timeout,
                                      ui::AXTreeUpdate* response) {
   if (!render_frame_->GetWebFrame())
     return;
   context_->UpdateAXForAllDocuments();
 
-  if (context_->SerializeEntireTree(exclude_offscreen, max_node_count, timeout,
-                                    response))
+  if (context_->SerializeEntireTree(max_node_count, timeout, response)) {
     return;
+  }
 
   // It failed again. Clear the response object because it might have errors.
   *response = ui::AXTreeUpdate();
diff --git a/content/renderer/accessibility/ax_tree_snapshotter_impl.h b/content/renderer/accessibility/ax_tree_snapshotter_impl.h
index 46fce7c..a0c2042c 100644
--- a/content/renderer/accessibility/ax_tree_snapshotter_impl.h
+++ b/content/renderer/accessibility/ax_tree_snapshotter_impl.h
@@ -23,8 +23,7 @@
   ~AXTreeSnapshotterImpl() override;
 
   // AXTreeSnapshotter implementation.
-  void Snapshot(bool exclude_offscreen,
-                size_t max_node_count,
+  void Snapshot(size_t max_node_count,
                 base::TimeDelta timeout,
                 ui::AXTreeUpdate* accessibility_tree) override;
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index cccdc37e..0a0aa8f 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2189,8 +2189,7 @@
     SnapshotAccessibilityTreeCallback callback) {
   ui::AXTreeUpdate response;
   AXTreeSnapshotterImpl snapshotter(this, ui::AXMode(params->ax_mode));
-  snapshotter.Snapshot(params->exclude_offscreen, params->max_nodes,
-                       params->timeout, &response);
+  snapshotter.Snapshot(params->max_nodes, params->timeout, &response);
   std::move(callback).Run(response);
 }
 
@@ -5292,7 +5291,10 @@
       frame_->IsOnInitialEmptyDocument() && first_navigation_in_render_frame &&
       // If this is a subframe history navigation that should be sent to the
       // browser, don't commit it synchronously.
-      !is_history_navigation_in_new_child_frame;
+      !is_history_navigation_in_new_child_frame &&
+      // Fullscreen navigation requests must go to the browser (for permission
+      // checks and other security measures).
+      !info->is_fullscreen_requested;
 
   if (should_do_synchronous_about_blank_navigation) {
     for (auto& observer : observers_)
diff --git a/content/services/auction_worklet/private_aggregation_bindings.cc b/content/services/auction_worklet/private_aggregation_bindings.cc
index 1ccd04b..699ff4c 100644
--- a/content/services/auction_worklet/private_aggregation_bindings.cc
+++ b/content/services/auction_worklet/private_aggregation_bindings.cc
@@ -326,7 +326,10 @@
             send_histogram_report_function)
       .Check();
 
-  if (blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get()) {
+  if (blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get() ||
+      base::FeatureList::IsEnabled(
+          blink::features::
+              kPrivateAggregationApiFledgeExtensionsLocalTestingOverride)) {
     v8::Local<v8::Function> report_contribution_for_event_function =
         v8::Function::New(
             context, &PrivateAggregationBindings::ReportContributionForEvent,
diff --git a/content/test/data/gpu/webcodecs/encode-decode.html b/content/test/data/gpu/webcodecs/encode-decode.html
index 3cf813d..bf6d897 100644
--- a/content/test/data/gpu/webcodecs/encode-decode.html
+++ b/content/test/data/gpu/webcodecs/encode-decode.html
@@ -124,6 +124,11 @@
       'source_type': 'offscreen',
       'codec': 'avc1.42001E',
       'acceleration':'prefer-software'
+    },
+    {
+      'source_type': 'offscreen',
+      'codec': 'avc1.42001E',
+      'acceleration':'prefer-hardware'
     }]);
   </script>
 </head>
diff --git a/content/test/gpu/gpu_tests/test_expectations/OWNERS b/content/test/gpu/gpu_tests/test_expectations/OWNERS
index 39eb8eb..4aca645 100644
--- a/content/test/gpu/gpu_tests/test_expectations/OWNERS
+++ b/content/test/gpu/gpu_tests/test_expectations/OWNERS
@@ -1,6 +1,8 @@
 # The entire Chrome Graphics team is allowed to administer these test expectations.
 file://gpu/GRAPHICS_TEAM_OWNERS
 
+per-file webcodecs_expectations.txt=file://third_party/blink/renderer/modules/webcodecs/OWNERS
+
 # Service accounts for automated expectation scripts.
 chrome-automated-expectation@chops-service-accounts.iam.gserviceaccount.com
 chromium-automated-expectation@chops-service-accounts.iam.gserviceaccount.com
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 5e54bb8..abeebc702 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -88,8 +88,6 @@
 ###################
 # Non-"Skip" expectations go here to suppress regular flakes/failures.
 
-crbug.com/1371749 [ mac-x86_64 ] WebCodecs_EncodeDecode_offscreen_avc1.42001E_prefer-hardware [ Failure ]
-
 crbug.com/1409453 [ win amd ] WebCodecs_copyTo_hw_decoder [ Failure ]
 crbug.com/1409453 [ win amd ] WebCodecs_DrawImage_hw_decoder [ Failure ]
 crbug.com/1409453 [ win amd ] WebCodecs_TexImage2d_hw_decoder [ Failure ]
diff --git a/device/bluetooth/floss/floss_dbus_client.cc b/device/bluetooth/floss/floss_dbus_client.cc
index 8f32da7..a131811 100644
--- a/device/bluetooth/floss/floss_dbus_client.cc
+++ b/device/bluetooth/floss/floss_dbus_client.cc
@@ -123,6 +123,7 @@
 const char kListenUsingInsecureRfcommWithServiceRecord[] =
     "ListenUsingInsecureRfcommWithServiceRecord";
 const char kListenUsingL2capChannel[] = "ListenUsingL2capChannel";
+const char kListenUsingRfcomm[] = "ListenUsingRfcomm";
 const char kListenUsingRfcommWithServiceRecord[] =
     "ListenUsingRfcommWithServiceRecord";
 const char kCreateInsecureL2capChannel[] = "CreateInsecureL2capChannel";
diff --git a/device/bluetooth/floss/floss_dbus_client.h b/device/bluetooth/floss/floss_dbus_client.h
index a998b3b..0446e534 100644
--- a/device/bluetooth/floss/floss_dbus_client.h
+++ b/device/bluetooth/floss/floss_dbus_client.h
@@ -127,6 +127,7 @@
 extern DEVICE_BLUETOOTH_EXPORT const char
     kListenUsingInsecureRfcommWithServiceRecord[];
 extern DEVICE_BLUETOOTH_EXPORT const char kListenUsingL2capChannel[];
+extern DEVICE_BLUETOOTH_EXPORT const char kListenUsingRfcomm[];
 extern DEVICE_BLUETOOTH_EXPORT const char kListenUsingRfcommWithServiceRecord[];
 extern DEVICE_BLUETOOTH_EXPORT const char kCreateInsecureL2capChannel[];
 extern DEVICE_BLUETOOTH_EXPORT const char
diff --git a/device/bluetooth/floss/floss_socket_manager.cc b/device/bluetooth/floss/floss_socket_manager.cc
index 7e48a34..155ce12 100644
--- a/device/bluetooth/floss/floss_socket_manager.cc
+++ b/device/bluetooth/floss/floss_socket_manager.cc
@@ -343,6 +343,27 @@
       method, callback_id_);
 }
 
+void FlossSocketManager::ListenUsingRfcommAlt(
+    const absl::optional<std::string> name,
+    const absl::optional<device::BluetoothUUID> application_uuid,
+    const absl::optional<int> channel,
+    const absl::optional<int> flags,
+    ResponseCallback<BtifStatus> callback,
+    ConnectionStateChanged ready_cb,
+    ConnectionAccepted new_connection_cb) {
+  if (callback_id_ == kInvalidCallbackId) {
+    std::move(callback).Run(
+        base::unexpected(Error(kErrorInvalidCallback, /*message=*/"")));
+    return;
+  }
+  CallSocketMethod(
+      base::BindOnce(&FlossSocketManager::CompleteListen,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(ready_cb), std::move(new_connection_cb)),
+      socket_manager::kListenUsingRfcomm, callback_id_, channel,
+      application_uuid, name, flags);
+}
+
 void FlossSocketManager::ListenUsingRfcomm(
     const std::string& name,
     const device::BluetoothUUID& uuid,
diff --git a/device/bluetooth/floss/floss_socket_manager.h b/device/bluetooth/floss/floss_socket_manager.h
index 2c34b94..227e3eb 100644
--- a/device/bluetooth/floss/floss_socket_manager.h
+++ b/device/bluetooth/floss/floss_socket_manager.h
@@ -140,6 +140,19 @@
                                 ConnectionStateChanged ready_cb,
                                 ConnectionAccepted new_connection_cb);
 
+  // Listen for connections using an RFCOMM channel. This API exposes all of the
+  // options supported by Floss and should only be used if there are no safer
+  // variants capable of supporting a use-case, such as when manually
+  // constructing SDP records for a listening socket.
+  virtual void ListenUsingRfcommAlt(
+      const absl::optional<std::string> name,
+      const absl::optional<device::BluetoothUUID> application_uuid,
+      const absl::optional<int> channel,
+      const absl::optional<int> flags,
+      ResponseCallback<BtifStatus> callback,
+      ConnectionStateChanged ready_cb,
+      ConnectionAccepted new_connection_cb);
+
   // Listen for connections using an RFCOMM channel. Creates SDP record with
   // given name and UUID.
   virtual void ListenUsingRfcomm(const std::string& name,
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 720fb47..a9cc15d 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -36,10 +36,6 @@
              "WebAuthenticationPasskeysUI",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kWebAuthnNoEmptyDisplayNameCBOR,
-             "WebAuthenticationNoEmptyDisplayNameCBOR",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kWebAuthnNonDiscoverableMakeCredentialQRFlag,
              "WebAuthenticationNonDiscoverableMakeCredentialQRFlag",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/device/fido/features.h b/device/fido/features.h
index f64e8da6..369432db 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -36,10 +36,6 @@
 // Enable some experimental UI changes
 COMPONENT_EXPORT(DEVICE_FIDO) BASE_DECLARE_FEATURE(kWebAuthPasskeysUI);
 
-// Don't send empty displayName values to security keys when creating
-// credentials.
-BASE_DECLARE_FEATURE(kWebAuthnNoEmptyDisplayNameCBOR);
-
 // Include an indication for non-discoverable makeCredential calls in caBLE QR
 // codes.
 BASE_DECLARE_FEATURE(kWebAuthnNonDiscoverableMakeCredentialQRFlag);
diff --git a/device/fido/public_key_credential_user_entity.cc b/device/fido/public_key_credential_user_entity.cc
index 90dbb89..0eca1d8 100644
--- a/device/fido/public_key_credential_user_entity.cc
+++ b/device/fido/public_key_credential_user_entity.cc
@@ -88,9 +88,7 @@
     user_map.emplace(kEntityNameMapKey, *user.name);
   // Empty display names result in CTAP1_ERR_INVALID_LENGTH on some security
   // keys.
-  if (user.display_name &&
-      (!base::FeatureList::IsEnabled(kWebAuthnNoEmptyDisplayNameCBOR) ||
-       !user.display_name->empty())) {
+  if (user.display_name && !user.display_name->empty()) {
     user_map.emplace(kDisplayNameMapKey, *user.display_name);
   }
   return cbor::Value(std::move(user_map));
diff --git a/device/gamepad/dualshock4_controller.cc b/device/gamepad/dualshock4_controller.cc
index 149deb3..d13fda24 100644
--- a/device/gamepad/dualshock4_controller.cc
+++ b/device/gamepad/dualshock4_controller.cc
@@ -281,9 +281,6 @@
   writer_.reset();
 }
 
-#include <bitset>
-#include <tuple>
-
 bool Dualshock4Controller::ProcessInputReport(uint8_t report_id,
                                               base::span<const uint8_t> report,
                                               Gamepad* pad,
diff --git a/device/udev_linux/BUILD.gn b/device/udev_linux/BUILD.gn
index 623733c..51dbd07 100644
--- a/device/udev_linux/BUILD.gn
+++ b/device/udev_linux/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/features.gni")
 
 if (use_udev) {
-  source_set("udev_linux") {
+  component("udev_linux") {
     sources = [
       "scoped_udev.h",
       "udev.cc",
@@ -20,6 +20,8 @@
       "udev_watcher.h",
     ]
 
+    defines = [ "IS_DEVICE_UDEV_LINUX_IMPL" ]
+
     deps = [
       "//base",
       "//build/linux/libudev",
diff --git a/device/udev_linux/udev.h b/device/udev_linux/udev.h
index fe37ff0..886b2d9 100644
--- a/device/udev_linux/udev.h
+++ b/device/udev_linux/udev.h
@@ -10,6 +10,7 @@
 #include <sys/stat.h>
 
 #include <string>
+#include "base/component_export.h"
 
 #if !defined(USE_UDEV)
 #error "USE_UDEV not defined"
@@ -29,70 +30,107 @@
 
 namespace device {
 
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_action(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_devnode(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_devtype(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_device* udev_device_get_parent(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_device* udev_device_get_parent_with_subsystem_devtype(
     udev_device* udev_device,
     const char* subsystem,
     const char* devtype);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_list_entry* udev_device_get_properties_list_entry(
     struct udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_property_value(udev_device* udev_device,
                                            const char* key);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_subsystem(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_sysattr_value(udev_device* udev_device,
                                           const char* sysattr);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_sysname(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_device_get_syspath(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_device* udev_device_new_from_devnum(udev* udev, char type, dev_t devnum);
-udev_device* udev_device_new_from_subsystem_sysname(
-    udev* udev,
-    const char* subsystem,
-    const char* sysname);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
+udev_device* udev_device_new_from_subsystem_sysname(udev* udev,
+                                                    const char* subsystem,
+                                                    const char* sysname);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_device* udev_device_new_from_syspath(udev* udev, const char* syspath);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 void udev_device_unref(udev_device* udev_device);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
                                        const char* subsystem);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_list_entry* udev_enumerate_get_list_entry(udev_enumerate* udev_enumerate);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_enumerate* udev_enumerate_new(udev* udev);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 void udev_enumerate_unref(udev_enumerate* udev_enumerate);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_list_entry* udev_list_entry_get_next(udev_list_entry* list_entry);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 const char* udev_list_entry_get_name(udev_list_entry* list_entry);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 int udev_monitor_enable_receiving(udev_monitor* udev_monitor);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 int udev_monitor_filter_add_match_subsystem_devtype(udev_monitor* udev_monitor,
                                                     const char* subsystem,
                                                     const char* devtype);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 int udev_monitor_get_fd(udev_monitor* udev_monitor);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_monitor* udev_monitor_new_from_netlink(udev* udev, const char* name);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 udev_device* udev_monitor_receive_device(udev_monitor* udev_monitor);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 void udev_monitor_unref(udev_monitor* udev_monitor);
-udev* udev_new();
-void udev_set_log_fn(
-    struct udev* udev,
-    void (*log_fn)(struct udev* udev, int priority, const char* file, int line,
-                   const char* fn, const char* format, va_list args));
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX) udev* udev_new();
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
+void udev_set_log_fn(struct udev* udev,
+                     void (*log_fn)(struct udev* udev,
+                                    int priority,
+                                    const char* file,
+                                    int line,
+                                    const char* fn,
+                                    const char* format,
+                                    va_list args));
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 void udev_set_log_priority(struct udev* udev, int priority);
-void udev_unref(udev* udev);
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX) void udev_unref(udev* udev);
 
 // Calls udev_device_get_property_value() and replaces missing values with
 // the empty string.
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 std::string UdevDeviceGetPropertyValue(udev_device* udev_device,
                                        const char* key);
 
 // Calls udev_device_get_sysattr_value() and replaces missing values with
 // the empty string.
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 std::string UdevDeviceGetSysattrValue(udev_device* udev_device,
                                       const char* key);
 
 // Walks up the chain of parent devices calling udev_device_get_sysattr_value()
 // until a value is found. If no value is found, an empty string is returned.
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 std::string UdevDeviceRecursiveGetSysattrValue(udev_device* udev_device,
                                                const char* key);
 
 // Decodes udev-encoded string. Useful for decoding "*_ENC" udev properties.
+COMPONENT_EXPORT(DEVICE_UDEV_LINUX)
 std::string UdevDecodeString(const std::string& encoded);
 
 }  // namespace device
diff --git a/device/udev_linux/udev0_loader.h b/device/udev_linux/udev0_loader.h
index dd6642ed..b071964 100644
--- a/device/udev_linux/udev0_loader.h
+++ b/device/udev_linux/udev0_loader.h
@@ -13,7 +13,7 @@
 
 namespace device {
 
-class Udev0Loader : public UdevLoader {
+class COMPONENT_EXPORT(DEVICE_UDEV_LINUX) Udev0Loader : public UdevLoader {
  public:
   Udev0Loader();
 
diff --git a/device/udev_linux/udev1_loader.h b/device/udev_linux/udev1_loader.h
index 7e24a17b..8fb8074d 100644
--- a/device/udev_linux/udev1_loader.h
+++ b/device/udev_linux/udev1_loader.h
@@ -13,7 +13,7 @@
 
 namespace device {
 
-class Udev1Loader : public UdevLoader {
+class COMPONENT_EXPORT(DEVICE_UDEV_LINUX) Udev1Loader : public UdevLoader {
  public:
   Udev1Loader();
 
diff --git a/device/udev_linux/udev_loader.h b/device/udev_linux/udev_loader.h
index 21bec05..44384f8 100644
--- a/device/udev_linux/udev_loader.h
+++ b/device/udev_linux/udev_loader.h
@@ -9,6 +9,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include "base/component_export.h"
+
 #if !defined(USE_UDEV)
 #error "USE_UDEV not defined"
 #endif
@@ -27,7 +29,7 @@
 //
 // All the methods have the same signatures as libudev's functions. e.g.
 // udev_monitor_get_fd(mon) simply becomes device::udev_monitor_get_fd(mon).
-class UdevLoader {
+class COMPONENT_EXPORT(DEVICE_UDEV_LINUX) UdevLoader {
  public:
   static UdevLoader* Get();
 
diff --git a/device/udev_linux/udev_watcher.h b/device/udev_linux/udev_watcher.h
index f0877335..524e661 100644
--- a/device/udev_linux/udev_watcher.h
+++ b/device/udev_linux/udev_watcher.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/files/file_descriptor_watcher_posix.h"
 #include "base/memory/raw_ptr.h"
 #include "base/sequence_checker.h"
@@ -20,7 +21,7 @@
 
 // This class wraps an instance of udev_monitor, watching for devices that are
 // added and removed from the system. This class has sequence affinity.
-class UdevWatcher {
+class COMPONENT_EXPORT(DEVICE_UDEV_LINUX) UdevWatcher {
  public:
   class Observer {
    public:
diff --git a/docs/accessibility/browser/android.md b/docs/accessibility/browser/android.md
index 6a016becc..046bfac 100644
--- a/docs/accessibility/browser/android.md
+++ b/docs/accessibility/browser/android.md
@@ -526,8 +526,6 @@
     -f "*WebContentsAccessibilityTreeTest*testExample"
 ```
 
-Piece of cake!
-
 ## Common Android accessibility "gotchas"
 
 - "name" vs. "text"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index eb4d7c3..c8200ee 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -73218,7 +73218,7 @@
       dimensions: "builder:linux-chromeos-rel"
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
@@ -74731,7 +74731,7 @@
       dimensions: "builder:linux-lacros-rel"
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
diff --git a/infra/config/lib/linux-default.json b/infra/config/lib/linux-default.json
index d35caee..774d0b2 100644
--- a/infra/config/lib/linux-default.json
+++ b/infra/config/lib/linux-default.json
@@ -4,6 +4,8 @@
     "linux-ssd-rel-dev": "Ubuntu-22.04"
   },
   "try": {
+    "linux-chromeos-rel": "Ubuntu-22.04",
+    "linux-lacros-rel": "Ubuntu-22.04",
     "linux-rel": "Ubuntu-22.04",
     "linux_chromium_tsan_rel_ng": "Ubuntu-22.04"
   }
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 0fd594d3..2f34020 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2685,6 +2685,39 @@
       <message name="IDS_IOS_SETTINGS_ADD_PASSWORD_FOOTER_NON_SYNCING" desc="Disclaimer telling non-syncing users what will happen to their passwords">
         Your password will be saved to your device.
       </message>
+      <message name="IDS_IOS_SET_UP_LIST_ALL_SET_DESCRIPTION" desc="Description for the Set Up List item that tells the user they have completed all tasks.">
+        You'll now get the most out of Chrome on your device.
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_ALL_SET_TITLE" desc="Title for the Set Up List item that tells the user they have completed all tasks.">
+        You're All Set
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION" desc="Description for the Set Up List item that encourages the user to set up Chrome's autofill settings.">
+        Access your Chrome passwords and more across other apps on this device.
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_AUTOFILL_TITLE" desc="Title for the Set Up List item that encourages the user to set up Chrome's autofill settings.">
+        Use Chrome for Autofill
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION" desc="Description for the Set Up List item that encourages the user to set Chrome as their default browser.">
+        Use Chrome anytime you tap links in messages, documents, and other apps.
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE" desc="Title for the Set Up List item that encourages the user to set Chrome as their default browser.">
+        Use Chrome by Default
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_SETTINGS_TURN_OFF" desc="Title for the item to turn off the Set Up List, in the Set Up List Settings action sheet.">
+        Turn off
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_SETTINGS_CANCEL" desc="Title for the item to cancel opening the Set Up List Settings action sheet.">
+        Cancel
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION" desc="Description for the Set Up List item that encourages the user to sign in and set up syncing.">
+        Sign in with your Google Account to sync your passwords, history, and more.
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_TITLE" desc="Title for the Set Up List item that encourages the user to sign in and set up syncing.">
+        Sign in and Sync
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_TITLE" desc="Title for the Set Up List - a list of tasks the user might want to complete when first using Chrome.">
+        Get the most out of Chrome
+      </message>
       <message name="IDS_IOS_LEAK_CHECK_SWITCH" desc="Title for the switch toggling whether Chrome should check that entered credentials have been part of a leak.">
         Warn You if Passwords Are Exposed in a Data Breach
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..e2d3665
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+a96bb1c3f680a2d36422e4326eb559fe072eb8e0
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_TITLE.png.sha1
new file mode 100644
index 0000000..e2d3665
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_ALL_SET_TITLE.png.sha1
@@ -0,0 +1 @@
+a96bb1c3f680a2d36422e4326eb559fe072eb8e0
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_TITLE.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_AUTOFILL_TITLE.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_CANCEL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_CANCEL.png.sha1
new file mode 100644
index 0000000..6c03d41
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_CANCEL.png.sha1
@@ -0,0 +1 @@
+72dfe222a800cd5d09c83ecc581a979a3972fed8
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_TURN_OFF.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_TURN_OFF.png.sha1
new file mode 100644
index 0000000..6c03d41
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SETTINGS_TURN_OFF.png.sha1
@@ -0,0 +1 @@
+72dfe222a800cd5d09c83ecc581a979a3972fed8
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_TITLE.png.sha1
new file mode 100644
index 0000000..7dfeac3
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_TITLE.png.sha1
@@ -0,0 +1 @@
+8b05fa88e16c42565985fb9cecf4108fa513a8f4
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_TITLE.png.sha1
new file mode 100644
index 0000000..bcf45da5
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_TITLE.png.sha1
@@ -0,0 +1 @@
+d132f91dc19a94e00881eb30db7a54dd8e040ad9
\ No newline at end of file
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 426acde..8b85ac14 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -311,10 +311,11 @@
   if (reject_new_deserializers_)
     return false;
 
-  PendingSyncMsg pending(
-      SyncMessage::GetMessageId(*sync_msg), sync_msg->TakeReplyDeserializer(),
-      new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL,
-                              base::WaitableEvent::InitialState::NOT_SIGNALED));
+  PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg),
+                         sync_msg->TakeReplyDeserializer(),
+                         std::make_unique<base::WaitableEvent>(
+                             base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED));
   deserializers_.push_back(std::move(pending));
   return true;
 }
@@ -323,9 +324,7 @@
   bool result;
   {
     base::AutoLock auto_lock(deserializers_lock_);
-    PendingSyncMsg& msg = deserializers_.back();
-    msg.done_event.ClearAndDelete();
-    result = msg.send_result;
+    result = deserializers_.back().send_result;
     deserializers_.pop_back();
   }
 
@@ -343,7 +342,7 @@
 
 base::WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
   base::AutoLock auto_lock(deserializers_lock_);
-  return deserializers_.back().done_event;
+  return deserializers_.back().done_event.get();
 }
 
 base::WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
@@ -370,7 +369,7 @@
     DVLOG(1) << "Received error reply";
   }
 
-  base::WaitableEvent* done_event = deserializers_.back().done_event;
+  base::WaitableEvent* done_event = deserializers_.back().done_event.get();
   TRACE_EVENT_WITH_FLOW0("toplevel.flow",
                          "SyncChannel::SyncContext::TryToUnblockListener",
                          done_event, TRACE_EVENT_FLAG_FLOW_OUT);
@@ -438,7 +437,7 @@
   for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
     TRACE_EVENT_WITH_FLOW0("toplevel.flow",
                            "SyncChannel::SyncContext::CancelPendingSends",
-                           iter->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
+                           iter->done_event.get(), TRACE_EVENT_FLAG_FLOW_OUT);
     iter->done_event->Signal();
   }
 }
diff --git a/ipc/ipc_sync_message.cc b/ipc/ipc_sync_message.cc
index 6df221c..196fd01 100644
--- a/ipc/ipc_sync_message.cc
+++ b/ipc/ipc_sync_message.cc
@@ -9,6 +9,7 @@
 #include "base/atomic_sequence_num.h"
 #include "base/check.h"
 #include "base/notreached.h"
+#include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
 
 namespace {
@@ -116,8 +117,8 @@
 
 PendingSyncMsg::PendingSyncMsg(int id,
                                std::unique_ptr<MessageReplyDeserializer> d,
-                               base::WaitableEvent* e)
-    : id(id), deserializer(std::move(d)), done_event(e) {}
+                               std::unique_ptr<base::WaitableEvent> e)
+    : id(id), deserializer(std::move(d)), done_event(std::move(e)) {}
 
 PendingSyncMsg::PendingSyncMsg(PendingSyncMsg&& that) = default;
 
diff --git a/ipc/ipc_sync_message.h b/ipc/ipc_sync_message.h
index 0dfdf117..af38f4c 100644
--- a/ipc/ipc_sync_message.h
+++ b/ipc/ipc_sync_message.h
@@ -9,7 +9,6 @@
 
 #include <memory>
 
-#include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -86,14 +85,14 @@
 struct IPC_MESSAGE_SUPPORT_EXPORT PendingSyncMsg {
   PendingSyncMsg(int id,
                  std::unique_ptr<MessageReplyDeserializer> d,
-                 base::WaitableEvent* e);
+                 std::unique_ptr<base::WaitableEvent> e);
   PendingSyncMsg(PendingSyncMsg&& that);
   ~PendingSyncMsg();
 
   int id;
   bool send_result = false;
   std::unique_ptr<MessageReplyDeserializer> deserializer;
-  raw_ptr<base::WaitableEvent> done_event;
+  std::unique_ptr<base::WaitableEvent> done_event;
 };
 
 }  // namespace IPC
diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc
index a05fd56..15f66c8 100644
--- a/ipc/ipc_sync_message_filter.cc
+++ b/ipc/ipc_sync_message_filter.cc
@@ -44,12 +44,14 @@
     return true;
   }
 
-  base::WaitableEvent done_event(
+  auto owned_event = std::make_unique<base::WaitableEvent>(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent* done_event = owned_event.get();
   PendingSyncMsg pending_message(
       SyncMessage::GetMessageId(*message),
-      static_cast<SyncMessage*>(message)->TakeReplyDeserializer(), &done_event);
+      static_cast<SyncMessage*>(message)->TakeReplyDeserializer(),
+      std::move(owned_event));
 
   {
     base::AutoLock auto_lock(lock_);
@@ -81,14 +83,14 @@
         registry->RegisterEvent(shutdown_event_,
                                 base::BindRepeating(&OnEventReady, &shutdown));
     mojo::SyncHandleRegistry::EventCallbackSubscription done_subscription =
-        registry->RegisterEvent(&done_event,
+        registry->RegisterEvent(done_event,
                                 base::BindRepeating(&OnEventReady, &done));
 
     const bool* stop_flags[] = {&done, &shutdown};
     registry->Wait(stop_flags, 2);
     if (done) {
       TRACE_EVENT_WITH_FLOW0("toplevel.flow", "SyncMessageFilter::Send",
-                             &done_event, TRACE_EVENT_FLAG_FLOW_IN);
+                             done_event, TRACE_EVENT_FLAG_FLOW_IN);
     }
   }
 
@@ -134,9 +136,9 @@
         (*iter)->send_result =
             (*iter)->deserializer->SerializeOutputParameters(message);
       }
-      TRACE_EVENT_WITH_FLOW0("toplevel.flow",
-                             "SyncMessageFilter::OnMessageReceived",
-                             (*iter)->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
+      TRACE_EVENT_WITH_FLOW0(
+          "toplevel.flow", "SyncMessageFilter::OnMessageReceived",
+          (*iter)->done_event.get(), TRACE_EVENT_FLAG_FLOW_OUT);
       (*iter)->done_event->Signal();
       return true;
     }
@@ -172,9 +174,9 @@
   lock_.AssertAcquired();
   for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin();
        iter != pending_sync_messages_.end(); ++iter) {
-    TRACE_EVENT_WITH_FLOW0("toplevel.flow",
-                           "SyncMessageFilter::SignalAllEvents",
-                           (*iter)->done_event, TRACE_EVENT_FLAG_FLOW_OUT);
+    TRACE_EVENT_WITH_FLOW0(
+        "toplevel.flow", "SyncMessageFilter::SignalAllEvents",
+        (*iter)->done_event.get(), TRACE_EVENT_FLAG_FLOW_OUT);
     (*iter)->done_event->Signal();
   }
 }
diff --git a/media/base/android/java/src/org/chromium/media/CodecProfileLevelList.java b/media/base/android/java/src/org/chromium/media/CodecProfileLevelList.java
index 83e28dc..ea705b4d 100644
--- a/media/base/android/java/src/org/chromium/media/CodecProfileLevelList.java
+++ b/media/base/android/java/src/org/chromium/media/CodecProfileLevelList.java
@@ -174,7 +174,7 @@
         }
     }
 
-    private static int mediaCodecLevelToChromiumMediaLevel(int codec, int level) {
+    public static int mediaCodecLevelToChromiumMediaLevel(int codec, int level) {
         switch (codec) {
             case VideoCodec.H264:
                 switch (level) {
@@ -259,6 +259,59 @@
                     default:
                         throw new UnsupportedCodecProfileException();
                 }
+            case VideoCodec.AV1:
+                switch (level) {
+                    case CodecProfileLevel.AV1Level2:
+                        return 20;
+                    case CodecProfileLevel.AV1Level21:
+                        return 21;
+                    case CodecProfileLevel.AV1Level22:
+                        return 22;
+                    case CodecProfileLevel.AV1Level23:
+                        return 23;
+                    case CodecProfileLevel.AV1Level3:
+                        return 30;
+                    case CodecProfileLevel.AV1Level31:
+                        return 31;
+                    case CodecProfileLevel.AV1Level32:
+                        return 32;
+                    case CodecProfileLevel.AV1Level33:
+                        return 33;
+                    case CodecProfileLevel.AV1Level4:
+                        return 40;
+                    case CodecProfileLevel.AV1Level41:
+                        return 41;
+                    case CodecProfileLevel.AV1Level42:
+                        return 42;
+                    case CodecProfileLevel.AV1Level43:
+                        return 43;
+                    case CodecProfileLevel.AV1Level5:
+                        return 50;
+                    case CodecProfileLevel.AV1Level51:
+                        return 51;
+                    case CodecProfileLevel.AV1Level52:
+                        return 52;
+                    case CodecProfileLevel.AV1Level53:
+                        return 53;
+                    case CodecProfileLevel.AV1Level6:
+                        return 60;
+                    case CodecProfileLevel.AV1Level61:
+                        return 61;
+                    case CodecProfileLevel.AV1Level62:
+                        return 62;
+                    case CodecProfileLevel.AV1Level63:
+                        return 63;
+                    case CodecProfileLevel.AV1Level7:
+                        return 70;
+                    case CodecProfileLevel.AV1Level71:
+                        return 71;
+                    case CodecProfileLevel.AV1Level72:
+                        return 72;
+                    case CodecProfileLevel.AV1Level73:
+                        return 73;
+                    default:
+                        throw new UnsupportedCodecProfileException();
+                }
             case VideoCodec.HEVC:
                 switch (level) {
                     case CodecProfileLevel.HEVCHighTierLevel1:
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
index 6e2ae49..2e250c9d 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
@@ -25,11 +25,16 @@
     static MediaCodecBridge createVideoDecoder(String mime, @CodecType int codecType,
             MediaCrypto mediaCrypto, int width, int height, Surface surface, byte[] csd0,
             byte[] csd1, HdrMetadata hdrMetadata, boolean allowAdaptivePlayback,
-            boolean useAsyncApi) {
+            boolean useAsyncApi, String decoderName) {
         CodecCreationInfo info = new CodecCreationInfo();
         try {
-            Log.i(TAG, "create MediaCodec video decoder, mime %s", mime);
-            info = MediaCodecUtil.createDecoder(mime, codecType, mediaCrypto);
+            Log.i(TAG, "create MediaCodec video decoder, mime %s, decoder name %s", mime,
+                    decoderName);
+            if (!decoderName.isEmpty()) {
+                info = MediaCodecUtil.createDecoderByName(mime, decoderName);
+            } else {
+                info = MediaCodecUtil.createDecoder(mime, codecType, mediaCrypto);
+            }
 
             if (info.mediaCodec == null) return null;
 
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index f3baabd4..43d88c721 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -377,6 +377,26 @@
     }
 
     /**
+     * Create MediaCodec decoder with the given name.
+     * @param mime MIME type of the media.
+     * @param decoderName name of the decoder.
+     * @return CodecCreationInfo object
+     */
+    static CodecCreationInfo createDecoderByName(String mime, String decoderName) {
+        CodecCreationInfo result = new CodecCreationInfo();
+        try {
+            result.mediaCodec = MediaCodec.createByCodecName(decoderName);
+            result.supportsAdaptivePlayback =
+                    codecSupportsAdaptivePlayback(result.mediaCodec, mime);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to create MediaCodec by decoder name %s", decoderName, e);
+            result.mediaCodec = null;
+        }
+
+        return result;
+    }
+
+    /**
      * This is a way to handle misbehaving devices.
      * Some devices cannot decode certain codecs, while other codecs work fine.
      *
diff --git a/media/base/android/java/src/org/chromium/media/VideoAcceleratorUtil.java b/media/base/android/java/src/org/chromium/media/VideoAcceleratorUtil.java
index 930c5b7..d2ae381 100644
--- a/media/base/android/java/src/org/chromium/media/VideoAcceleratorUtil.java
+++ b/media/base/android/java/src/org/chromium/media/VideoAcceleratorUtil.java
@@ -17,8 +17,10 @@
 import org.chromium.base.annotations.JNINamespace;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * A collection of SDK based helper functions for retrieving supported profiles
@@ -47,6 +49,7 @@
 
     private static class SupportedProfileAdapter {
         public int profile;
+        public int level;
         public int maxWidth;
         public int maxHeight;
         public int minWidth;
@@ -57,6 +60,8 @@
         public boolean supportsVbr;
         public String name;
         public boolean isSoftwareCodec;
+        public boolean supportsSecurePlayback;
+        public boolean requiresSecurePlayback;
 
         @CalledByNative("SupportedProfileAdapter")
         public int getProfile() {
@@ -64,6 +69,11 @@
         }
 
         @CalledByNative("SupportedProfileAdapter")
+        public int getLevel() {
+            return this.level;
+        }
+
+        @CalledByNative("SupportedProfileAdapter")
         public int getMaxWidth() {
             return this.maxWidth;
         }
@@ -112,6 +122,16 @@
         public boolean isSoftwareCodec() {
             return this.isSoftwareCodec;
         }
+
+        @CalledByNative("SupportedProfileAdapter")
+        public boolean supportsSecurePlayback() {
+            return this.supportsSecurePlayback;
+        }
+
+        @CalledByNative("SupportedProfileAdapter")
+        public boolean requiresSecurePlayback() {
+            return this.requiresSecurePlayback;
+        }
     }
 
     // Currently our encoder only supports NV12.
@@ -282,7 +302,7 @@
     private static SupportedProfileAdapter[] getSupportedDecoderProfiles() {
         MediaCodecInfo[] codecList;
         try {
-            codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS).getCodecInfos();
+            codecList = new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos();
         } catch (Throwable e) {
             // Swallow the exception due to bad Android implementation and pretend
             // MediaCodecList is not supported.
@@ -322,18 +342,26 @@
                         && videoCapabilities.isSizeSupported(
                                 supportedHeights.getUpper(), supportedWidths.getUpper());
 
-                // Since the supported profiles interface doesn't support levels, we just attach
-                // the same min/max to every profile.
-                HashSet<Integer> supportedProfiles = new HashSet<Integer>();
+                // The map from supported profile to the highest supported level. We just attach
+                // the same min/max to every profile, level pair.
+                HashMap<Integer, Integer> supportedProfileLevels = new HashMap<>();
                 int codec = CodecProfileLevelList.getCodecFromMime(type);
                 for (CodecProfileLevel cpl : capabilities.profileLevels) {
                     try {
-                        supportedProfiles.add(
-                                CodecProfileLevelList.mediaCodecProfileToChromiumMediaProfile(
-                                        codec, cpl.profile));
+                        int profile = CodecProfileLevelList.mediaCodecProfileToChromiumMediaProfile(
+                                codec, cpl.profile);
+                        int level = CodecProfileLevelList.mediaCodecLevelToChromiumMediaLevel(
+                                codec, cpl.level);
+                        int supportedLevel = supportedProfileLevels.getOrDefault(profile, -1);
+                        if (level > supportedLevel) {
+                            supportedProfileLevels.put(profile, level);
+                        }
                     } catch (RuntimeException e) {
-                        // This means mediaCodecProfileToChromiumMediaProfile() needs updating.
-                        Log.w(TAG, "Unknown profile: " + cpl.profile + " for codec " + type);
+                        // This means mediaCodecProfileToChromiumMediaProfile() or
+                        // mediaCodecLevelToChromiumMediaLevel() needs updating.
+                        Log.w(TAG,
+                                "Unknown profile: " + cpl.profile + " or level: " + cpl.level
+                                        + " for codec " + type);
                         continue;
                     }
                 }
@@ -341,26 +369,37 @@
                 // Not all platforms seem to have a populated `profileLevels`, e.g., the
                 // x86 emulator. In these cases, populate whats required by Android:
                 // https://developer.android.com/guide/topics/media/media-formats
-                if (supportedProfiles.isEmpty()) {
+                //
+                // The decoder selection will choose the decoder if the supported level is
+                // larger than or equal to the requested level. So here we use a large number to
+                // let the decoder selection succeed, if Android doesn't list required levels.
+                int maxLevel = Integer.MAX_VALUE;
+                if (supportedProfileLevels.isEmpty()) {
                     Log.d(TAG,
                             "CodecCapabilities.profileLevels is missing for codec " + type
                                     + ". Assuming default support.");
                     switch (codec) {
                         case VideoCodec.VP8:
-                            supportedProfiles.add(VideoCodecProfile.VP8PROFILE_ANY);
+                            supportedProfileLevels.put(VideoCodecProfile.VP8PROFILE_ANY, maxLevel);
                             break;
                         case VideoCodec.VP9:
-                            supportedProfiles.add(VideoCodecProfile.VP9PROFILE_PROFILE0);
+                            supportedProfileLevels.put(
+                                    VideoCodecProfile.VP9PROFILE_PROFILE0, maxLevel);
                             break;
                         case VideoCodec.HEVC:
-                            supportedProfiles.add(VideoCodecProfile.HEVCPROFILE_MAIN);
+                            // Main Profile Level 3 (90) for mobile devices and Main Profile
+                            // Level 4.1 (123) for Android TV.
+                            supportedProfileLevels.put(VideoCodecProfile.HEVCPROFILE_MAIN, 90);
                             break;
                         case VideoCodec.H264:
-                            supportedProfiles.add(VideoCodecProfile.H264PROFILE_BASELINE);
-                            supportedProfiles.add(VideoCodecProfile.H264PROFILE_MAIN);
+                            supportedProfileLevels.put(
+                                    VideoCodecProfile.H264PROFILE_BASELINE, maxLevel);
+                            supportedProfileLevels.put(
+                                    VideoCodecProfile.H264PROFILE_MAIN, maxLevel);
                             break;
                         case VideoCodec.AV1:
-                            supportedProfiles.add(VideoCodecProfile.AV1PROFILE_PROFILE_MAIN);
+                            supportedProfileLevels.put(
+                                    VideoCodecProfile.AV1PROFILE_PROFILE_MAIN, maxLevel);
                             break;
                     }
                 }
@@ -368,36 +407,49 @@
                 // Prior to Oreo, high profile support wasn't advertised properly.
                 if (codec == VideoCodec.H264 && Build.VERSION.SDK_INT < Build.VERSION_CODES.O
                         && hasHighProfileSupport(info.getName())) {
-                    supportedProfiles.add(VideoCodecProfile.H264PROFILE_HIGH);
+                    supportedProfileLevels.put(VideoCodecProfile.H264PROFILE_HIGH, maxLevel);
                 }
 
                 boolean isSoftwareCodec = MediaCodecUtil.isSoftwareCodec(info);
-                for (int mediaProfile : supportedProfiles) {
+                boolean supportsSecurePlayback = capabilities.isFeatureSupported(
+                        MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+                boolean requiresSecurePlayback = capabilities.isFeatureRequired(
+                        MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+                for (Map.Entry<Integer, Integer> profileLevel : supportedProfileLevels.entrySet()) {
                     SupportedProfileAdapter profile = new SupportedProfileAdapter();
-                    profile.profile = mediaProfile;
+                    profile.profile = profileLevel.getKey();
+                    profile.level = profileLevel.getValue();
                     profile.minWidth = supportedWidths.getLower();
                     profile.minHeight = supportedHeights.getLower();
                     profile.maxWidth = supportedWidths.getUpper();
                     profile.maxHeight = supportedHeights.getUpper();
+                    profile.name = info.getName();
                     profile.isSoftwareCodec = isSoftwareCodec;
+                    profile.supportsSecurePlayback = supportsSecurePlayback;
+                    profile.requiresSecurePlayback = requiresSecurePlayback;
                     profiles.add(profile);
 
                     Log.d(TAG,
-                            "Support: name=" + info.getName() + ", profile=" + mediaProfile
-                                    + ", min=" + profile.minWidth + "x" + profile.minHeight
-                                    + ", max=" + profile.maxWidth + "x" + profile.maxHeight
-                                    + ", is_sw=" + profile.isSoftwareCodec);
+                            "Support: name=" + info.getName() + ", profile=" + profile.profile
+                                    + ", level=" + profile.level + ", min=" + profile.minWidth + "x"
+                                    + profile.minHeight + ", max=" + profile.maxWidth + "x"
+                                    + profile.maxHeight + ", is_sw=" + profile.isSoftwareCodec
+                                    + ", secure=" + profile.supportsSecurePlayback);
 
                     // Invert min/max height/width for a portrait mode entry if needed.
                     if (needsPortraitEntry) {
                         profile = new SupportedProfileAdapter();
 
-                        profile.profile = mediaProfile;
+                        profile.profile = profileLevel.getKey();
+                        profile.level = profileLevel.getValue();
                         profile.minWidth = supportedHeights.getLower();
                         profile.minHeight = supportedWidths.getLower();
                         profile.maxWidth = supportedHeights.getUpper();
                         profile.maxHeight = supportedWidths.getUpper();
+                        profile.name = info.getName();
                         profile.isSoftwareCodec = isSoftwareCodec;
+                        profile.supportsSecurePlayback = supportsSecurePlayback;
+                        profile.requiresSecurePlayback = requiresSecurePlayback;
                         profiles.add(profile);
                     }
                 }
diff --git a/media/base/android/media_codec_bridge_impl.cc b/media/base/android/media_codec_bridge_impl.cc
index 3ca413c..b75f9f0 100644
--- a/media/base/android/media_codec_bridge_impl.cc
+++ b/media/base/android/media_codec_bridge_impl.cc
@@ -248,6 +248,7 @@
         config.container_color_space, config.hdr_metadata.value());
   }
   auto j_hdr_metadata = jni_hdr_metadata ? jni_hdr_metadata->obj() : nullptr;
+  auto j_decoder_name = ConvertUTF8ToJavaString(env, config.name);
 
   ScopedJavaGlobalRef<jobject> j_bridge(
       Java_MediaCodecBridgeBuilder_createVideoDecoder(
@@ -255,7 +256,7 @@
           config.initial_expected_coded_size.width(),
           config.initial_expected_coded_size.height(), config.surface, j_csd0,
           j_csd1, j_hdr_metadata, true /* allow_adaptive_playback */,
-          !!config.on_buffers_available_cb));
+          !!config.on_buffers_available_cb, j_decoder_name));
   if (j_bridge.is_null())
     return nullptr;
 
diff --git a/media/base/android/media_codec_bridge_impl.h b/media/base/android/media_codec_bridge_impl.h
index 3caa477..d285f84 100644
--- a/media/base/android/media_codec_bridge_impl.h
+++ b/media/base/android/media_codec_bridge_impl.h
@@ -64,6 +64,9 @@
   // called on an arbitrary thread, so use base::BindPostTaskToCurrentDefault if
   // needed.
   base::RepeatingClosure on_buffers_available_cb;
+
+  // The name of decoder/encoder. It's used to create the MediaCodec instance.
+  std::string name;
 };
 
 // A bridge to a Java MediaCodec.
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 0515931..330f9e1 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -112,6 +112,78 @@
   return supported_configs;
 }
 
+// Return the name of the decoder that will be used to create MediaCodec.
+std::string SelectMediaCodec(const VideoDecoderConfig& config,
+                             bool requires_secure_codec) {
+  std::string software_decoder;
+  for (const auto& info : GetDecoderInfoCache()) {
+    VideoCodec codec = VideoCodecProfileToVideoCodec(info.profile);
+    if (config.codec() != codec) {
+      continue;
+    }
+
+    if (config.profile() != VIDEO_CODEC_PROFILE_UNKNOWN &&
+        config.profile() != info.profile) {
+      continue;
+    }
+
+    if (config.level() != kNoVideoCodecLevel && config.level() > info.level) {
+      continue;
+    }
+
+    if (config.coded_size().width() < info.coded_size_min.width() ||
+        config.coded_size().height() < info.coded_size_min.height() ||
+        config.coded_size().width() > info.coded_size_max.width() ||
+        config.coded_size().height() > info.coded_size_max.width()) {
+      continue;
+    }
+
+    if (!requires_secure_codec &&
+        info.secure_codec_capability == SecureCodecCapability::kEncrypted) {
+      continue;
+    }
+
+    if (requires_secure_codec &&
+        info.secure_codec_capability == SecureCodecCapability::kClear) {
+      continue;
+    }
+
+    // Prioritize hardware decoder. Software decoder will be selected as a
+    // fallback option.
+    if (info.is_software_codec) {
+      if (software_decoder.empty()) {
+        software_decoder = info.name;
+      }
+      continue;
+    }
+
+    return info.name;
+  }
+
+  // Allow software decoder if either:
+  // 1. the stream is encrypted.
+  // 2. No software decoder is bundled into Chromium.
+  if (!(config.is_encrypted()
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+        || config.codec() == VideoCodec::kH264
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+        || (config.codec() == VideoCodec::kHEVC &&
+            base::FeatureList::IsEnabled(kPlatformHEVCDecoderSupport))
+#endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
+        || config.codec() == VideoCodec::kDolbyVision
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+        )) {
+    software_decoder = "";
+  }
+
+  DVLOG_IF(2, software_decoder.empty())
+      << "Can't find proper video decoder from decoder info cache, "
+         "fallback to the default decoder selection path.";
+  return software_decoder;
+}
+
 }  // namespace
 
 // When re-initializing the codec changes the resolution to be more than
@@ -605,6 +677,7 @@
   config->initial_expected_coded_size = decoder_config_.coded_size();
   config->container_color_space = decoder_config_.color_space_info();
   config->hdr_metadata = decoder_config_.hdr_metadata();
+  config->name = SelectMediaCodec(decoder_config_, requires_secure_codec_);
 
   // Use the asynchronous API if we can.
   if (device_info_->IsAsyncApiSupported()) {
diff --git a/media/gpu/android/video_accelerator_util.cc b/media/gpu/android/video_accelerator_util.cc
index d21aeae..7f9472d 100644
--- a/media/gpu/android/video_accelerator_util.cc
+++ b/media/gpu/android/video_accelerator_util.cc
@@ -91,15 +91,22 @@
       constexpr auto kDefaultSize = gfx::Size(4096, 4096);
       constexpr auto kSoftwareCodec = true;
       constexpr auto kHardwareCodec = false;
+      constexpr char kUnknownDecoderName[] = "";
       return std::vector<MediaCodecDecoderInfo>({
-          {H264PROFILE_BASELINE, gfx::Size(), kDefaultSize, kHardwareCodec},
-          {H264PROFILE_MAIN, gfx::Size(), kDefaultSize, kHardwareCodec},
-          {H264PROFILE_HIGH, gfx::Size(), kDefaultSize, kHardwareCodec},
-          {HEVCPROFILE_MAIN, gfx::Size(), kDefaultSize, kHardwareCodec},
+          {H264PROFILE_BASELINE, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kHardwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
+          {H264PROFILE_MAIN, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kHardwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
+          {H264PROFILE_HIGH, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kHardwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
+          {HEVCPROFILE_MAIN, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kHardwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
 
           // Report codecs as software where we have a bundled decoder.
-          {VP8PROFILE_ANY, gfx::Size(), kDefaultSize, kSoftwareCodec},
-          {VP9PROFILE_PROFILE0, gfx::Size(), kDefaultSize, kSoftwareCodec},
+          {VP8PROFILE_ANY, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kSoftwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
+          {VP9PROFILE_PROFILE0, kNoVideoCodecLevel, gfx::Size(), kDefaultSize,
+           kSoftwareCodec, SecureCodecCapability::kClear, kUnknownDecoderName},
       });
     }
 
@@ -108,6 +115,7 @@
       MediaCodecDecoderInfo info;
       info.profile = static_cast<VideoCodecProfile>(
           Java_SupportedProfileAdapter_getProfile(env, java_profile));
+      info.level = Java_SupportedProfileAdapter_getLevel(env, java_profile);
       info.coded_size_min = gfx::Size(
           Java_SupportedProfileAdapter_getMinWidth(env, java_profile),
           Java_SupportedProfileAdapter_getMinHeight(env, java_profile));
@@ -116,6 +124,22 @@
           Java_SupportedProfileAdapter_getMaxHeight(env, java_profile));
       info.is_software_codec =
           Java_SupportedProfileAdapter_isSoftwareCodec(env, java_profile);
+      bool supports_secure_playback =
+          Java_SupportedProfileAdapter_supportsSecurePlayback(env,
+                                                              java_profile);
+      bool requires_secure_playback =
+          Java_SupportedProfileAdapter_requiresSecurePlayback(env,
+                                                              java_profile);
+      // If the decoder requires secure playback, it must support secure
+      // playback.
+      DCHECK(!requires_secure_playback || supports_secure_playback);
+      info.secure_codec_capability =
+          requires_secure_playback
+              ? SecureCodecCapability::kEncrypted
+              : (supports_secure_playback ? SecureCodecCapability::kAny
+                                          : SecureCodecCapability::kClear);
+      info.name = base::android::ConvertJavaStringToUTF8(
+          Java_SupportedProfileAdapter_getName(env, java_profile));
       cpp_infos.push_back(info);
     }
     std::sort(
diff --git a/media/gpu/android/video_accelerator_util.h b/media/gpu/android/video_accelerator_util.h
index 11a747c9..f6ced57 100644
--- a/media/gpu/android/video_accelerator_util.h
+++ b/media/gpu/android/video_accelerator_util.h
@@ -24,11 +24,21 @@
 MEDIA_GPU_EXPORT const std::vector<MediaCodecEncoderInfo>&
 GetEncoderInfoCache();
 
+// Secure playback supporting for the decoder.
+enum class SecureCodecCapability {
+  kClear,      // Decoder only supports clear playback.
+  kAny,        // Decoder supports both clear and secure playback.
+  kEncrypted,  // Decoder only supports secure playback.
+};
+
 struct MediaCodecDecoderInfo {
   VideoCodecProfile profile;
+  VideoCodecLevel level;
   gfx::Size coded_size_min;
   gfx::Size coded_size_max;
   bool is_software_codec;
+  SecureCodecCapability secure_codec_capability;
+  std::string name;
 };
 
 // Returns information on decoder from the Java MediaCodecList.getCodecInfos()
diff --git a/media/gpu/args.gni b/media/gpu/args.gni
index 813657a..0e2f1b4 100644
--- a/media/gpu/args.gni
+++ b/media/gpu/args.gni
@@ -34,10 +34,9 @@
 
   # Indicates if ChromeOS protected media support exists. This is used
   # to enable the CDM daemon in Chrome OS as well as support for
-  # encrypted content with HW video decoders.
-  # TODO(jkardatzke): Enable this for Lacros always, it is determined at runtime
-  # in that configuration.
-  use_chromeos_protected_media = false
+  # encrypted content with HW video decoders. This is always enabled for Lacros
+  # because it detects support at runtime.
+  use_chromeos_protected_media = is_chromeos_lacros
 
   # Indicates if the ChromeOS protected media functionality should also be
   # utilized by HW video decoding for ARC.
diff --git a/media/gpu/mac/vt_video_encode_accelerator_mac.cc b/media/gpu/mac/vt_video_encode_accelerator_mac.cc
index 5d098d9..008aabe 100644
--- a/media/gpu/mac/vt_video_encode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_encode_accelerator_mac.cc
@@ -196,11 +196,10 @@
 }  // namespace
 
 struct VTVideoEncodeAccelerator::InProgressFrameEncode {
-  InProgressFrameEncode(base::TimeDelta rtp_timestamp,
+  InProgressFrameEncode(scoped_refptr<VideoFrame> frame,
                         const gfx::ColorSpace& frame_cs)
-      : timestamp(rtp_timestamp), encoded_color_space(frame_cs) {}
-
-  const base::TimeDelta timestamp;
+      : frame(frame), encoded_color_space(frame_cs) {}
+  const scoped_refptr<VideoFrame> frame;
   const gfx::ColorSpace encoded_color_space;
 };
 
@@ -212,7 +211,7 @@
                const InProgressFrameEncode& frame_info)
       : info(info_flags),
         sample_buffer(sbuf, base::scoped_policy::RETAIN),
-        capture_timestamp(frame_info.timestamp),
+        capture_timestamp(frame_info.frame->timestamp()),
         encoded_color_space(frame_info.encoded_color_space) {}
 
   EncodeOutput(const EncodeOutput&) = delete;
@@ -478,10 +477,13 @@
           kVTEncodeFrameOptionKey_ForceKeyFrame,
           force_keyframe ? kCFBooleanTrue : kCFBooleanFalse);
 
+  auto timestamp_cm =
+      CMTimeMake(frame->timestamp().InMicroseconds(), USEC_PER_SEC);
+
   // Wrap information we'll need after the frame is encoded in a heap object.
   // We'll get the pointer back from the VideoToolbox completion callback.
   auto request = std::make_unique<InProgressFrameEncode>(
-      frame->timestamp(), encoder_color_space_.value_or(gfx::ColorSpace()));
+      std::move(frame), encoder_color_space_.value_or(gfx::ColorSpace()));
 
   if (bitrate_.mode() == Bitrate::Mode::kConstant) {
     // In CBR mode, we adjust bitrate before every encode based on past history
@@ -489,9 +491,6 @@
     SetAdjustedConstantBitrate(bitrate_adjuster_.GetAdjustedBitrateBps());
   }
 
-  auto timestamp_cm =
-      CMTimeMake(frame->timestamp().InMicroseconds(), USEC_PER_SEC);
-
   // We can pass the ownership of |request| to the encode callback if
   // successful. Otherwise let it fall out of scope.
   OSStatus status = VTCompressionSessionEncodeFrame(
diff --git a/media/media_options.gni b/media/media_options.gni
index 333b523..5621ef4e 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -121,13 +121,12 @@
   enable_av1_decoder = enable_dav1d_decoder
 
   # Enable HEVC/H265 demuxing. Actual decoding must be provided by the
-  # platform.
-  # TODO(b/194429120): Enable this for Lacros builds.
+  # platform. Always enable this for Lacros, it determines support at runtime.
   # TODO(crbug.com/1336055): Revisit the default value for this setting as it
   # applies to video-capable devices.
   enable_platform_hevc =
-      proprietary_codecs &&
-      (enable_hevc_parser_and_hw_decoder || is_cast_media_device)
+      proprietary_codecs && (enable_hevc_parser_and_hw_decoder ||
+                             is_cast_media_device || is_chromeos_lacros)
 
   enable_mse_mpeg2ts_stream_parser =
       proprietary_codecs &&
diff --git a/net/base/parse_number.h b/net/base/parse_number.h
index e7e74e2a..f4827f7a 100644
--- a/net/base/parse_number.h
+++ b/net/base/parse_number.h
@@ -104,7 +104,7 @@
 // The ParseUint*() functions parse a string representing a number.
 //
 // These are equivalent to calling ParseInt*(), except with unsigned output
-// types. ParseIntFormat may onlu be one of {NON_NEGATIVE, STRICT_NON_NEGATIVE}.
+// types. ParseIntFormat may only be one of {NON_NEGATIVE, STRICT_NON_NEGATIVE}.
 [[nodiscard]] NET_EXPORT bool ParseUint32(
     base::StringPiece input,
     ParseIntFormat format,
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index 7f08149a..076d3565 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -143,8 +143,9 @@
     return;
 
   for (const der::Input& oid : cert->policy_oids()) {
-    if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
+    if (ev_metadata->IsEVPolicyOID(oid)) {
       oids->insert(oid);
+    }
   }
 }
 
@@ -359,8 +360,9 @@
                              sizeof(root_fingerprint.data));
 
     for (const der::Input& oid : path->user_constrained_policy_set) {
-      if (ev_metadata_->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
+      if (ev_metadata_->HasEVPolicyOID(root_fingerprint, oid)) {
         return true;
+      }
     }
 
     return false;
diff --git a/net/cert/ev_root_ca_metadata.cc b/net/cert/ev_root_ca_metadata.cc
index 343c6483..4d8cb0a 100644
--- a/net/cert/ev_root_ca_metadata.cc
+++ b/net/cert/ev_root_ca_metadata.cc
@@ -6,10 +6,6 @@
 
 #include "build/build_config.h"
 
-#if BUILDFLAG(IS_WIN)
-#include <stdlib.h>
-#endif
-
 #include "base/containers/contains.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -53,93 +49,7 @@
   return g_ev_root_ca_metadata.Pointer();
 }
 
-#if BUILDFLAG(IS_WIN)
-
-namespace {
-
-bool ConvertBytesToDottedString(const der::Input& policy_oid,
-                                std::string* dotted) {
-  CBS cbs;
-  CBS_init(&cbs, policy_oid.UnsafeData(), policy_oid.Length());
-  bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
-  if (!text)
-    return false;
-  dotted->assign(text.get());
-  return true;
-}
-
-}  // namespace
-
-bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
-  for (const auto& ev_root : kEvRootCaMetadata) {
-    if (base::Contains(ev_root.policy_oids, policy_oid)) {
-      return true;
-    }
-  }
-
-  for (const auto& ca : extra_cas_) {
-    if (ca.second == policy_oid)
-      return true;
-  }
-
-  return false;
-}
-
-bool EVRootCAMetadata::IsEVPolicyOIDGivenBytes(
-    const der::Input& policy_oid) const {
-  std::string dotted;
-  return ConvertBytesToDottedString(policy_oid, &dotted) &&
-         IsEVPolicyOID(dotted.c_str());
-}
-
-bool EVRootCAMetadata::HasEVPolicyOID(const SHA256HashValue& fingerprint,
-                                      PolicyOID policy_oid) const {
-  for (const auto& ev_root : kEvRootCaMetadata) {
-    if (fingerprint != ev_root.fingerprint)
-      continue;
-    return base::Contains(ev_root.policy_oids, policy_oid);
-  }
-
-  auto it = extra_cas_.find(fingerprint);
-  return it != extra_cas_.end() && it->second == policy_oid;
-}
-
-bool EVRootCAMetadata::HasEVPolicyOIDGivenBytes(
-    const SHA256HashValue& fingerprint,
-    const der::Input& policy_oid) const {
-  std::string dotted;
-  return ConvertBytesToDottedString(policy_oid, &dotted) &&
-         HasEVPolicyOID(fingerprint, dotted.c_str());
-}
-
-// static
-bool EVRootCAMetadata::IsCaBrowserForumEvOid(PolicyOID policy_oid) {
-  return strcmp(policy_oid, "2.23.140.1.1") == 0;
-}
-
-bool EVRootCAMetadata::AddEVCA(const SHA256HashValue& fingerprint,
-                               const char* policy) {
-  for (const auto& ev_root : kEvRootCaMetadata) {
-    if (fingerprint == ev_root.fingerprint)
-      return false;
-  }
-  if (extra_cas_.find(fingerprint) != extra_cas_.end())
-    return false;
-
-  extra_cas_[fingerprint] = policy;
-  return true;
-}
-
-bool EVRootCAMetadata::RemoveEVCA(const SHA256HashValue& fingerprint) {
-  auto it = extra_cas_.find(fingerprint);
-  if (it == extra_cas_.end())
-    return false;
-
-  extra_cas_.erase(it);
-  return true;
-}
-
-#elif defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
+#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
 
 namespace {
 
@@ -158,19 +68,12 @@
 
 }  // namespace
 
-bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
-  return policy_oids_.find(policy_oid.AsString()) != policy_oids_.end();
-}
-
-bool EVRootCAMetadata::IsEVPolicyOIDGivenBytes(
-    const der::Input& policy_oid) const {
-  // This implementation uses DER bytes already, so the two functions are the
-  // same.
-  return IsEVPolicyOID(policy_oid);
+bool EVRootCAMetadata::IsEVPolicyOID(der::Input policy_oid) const {
+  return policy_oids_.find(policy_oid.AsStringPiece()) != policy_oids_.end();
 }
 
 bool EVRootCAMetadata::HasEVPolicyOID(const SHA256HashValue& fingerprint,
-                                      PolicyOID policy_oid) const {
+                                      der::Input policy_oid) const {
   PolicyOIDMap::const_iterator iter = ev_policy_.find(fingerprint);
   if (iter == ev_policy_.end())
     return false;
@@ -181,20 +84,6 @@
   return false;
 }
 
-bool EVRootCAMetadata::HasEVPolicyOIDGivenBytes(
-    const SHA256HashValue& fingerprint,
-    const der::Input& policy_oid) const {
-  // The implementation uses DER bytes already, so the two functions are the
-  // same.
-  return HasEVPolicyOID(fingerprint, policy_oid);
-}
-
-// static
-bool EVRootCAMetadata::IsCaBrowserForumEvOid(PolicyOID policy_oid) {
-  const uint8_t kCabEvOid[] = {0x67, 0x81, 0x0c, 0x01, 0x01};
-  return der::Input(kCabEvOid) == policy_oid;
-}
-
 bool EVRootCAMetadata::AddEVCA(const SHA256HashValue& fingerprint,
                                const char* policy) {
   if (ev_policy_.find(fingerprint) != ev_policy_.end())
@@ -225,26 +114,13 @@
 // metadata.
 //
 
-bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
-  LOG(WARNING) << "Not implemented";
-  return false;
-}
-
-bool EVRootCAMetadata::IsEVPolicyOIDGivenBytes(
-    const der::Input& policy_oid) const {
+bool EVRootCAMetadata::IsEVPolicyOID(der::Input policy_oid) const {
   LOG(WARNING) << "Not implemented";
   return false;
 }
 
 bool EVRootCAMetadata::HasEVPolicyOID(const SHA256HashValue& fingerprint,
-                                      PolicyOID policy_oid) const {
-  LOG(WARNING) << "Not implemented";
-  return false;
-}
-
-bool EVRootCAMetadata::HasEVPolicyOIDGivenBytes(
-    const SHA256HashValue& fingerprint,
-    const der::Input& policy_oid) const {
+                                      der::Input policy_oid) const {
   LOG(WARNING) << "Not implemented";
   return false;
 }
@@ -264,15 +140,15 @@
 
 EVRootCAMetadata::EVRootCAMetadata() {
 // Constructs the object from the raw metadata in kEvRootCaMetadata.
-#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA) && !BUILDFLAG(IS_WIN)
+#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
   for (const auto& ev_root : kEvRootCaMetadata) {
     for (const auto& policy : ev_root.policy_oids) {
       if (policy.empty())
         break;
 
-      std::string policy_der = OIDStringToDER(policy.data());
+      std::string policy_der = OIDStringToDER(policy);
       if (policy_der.empty()) {
-        LOG(ERROR) << "Failed to register OID: " << policy;
+        LOG(ERROR) << "Failed to decode OID: " << policy;
         continue;
       }
 
diff --git a/net/cert/ev_root_ca_metadata.h b/net/cert/ev_root_ca_metadata.h
index 28a7d83b..29c31c4 100644
--- a/net/cert/ev_root_ca_metadata.h
+++ b/net/cert/ev_root_ca_metadata.h
@@ -38,39 +38,18 @@
 // extended-validation (EV) certificates.
 class NET_EXPORT_PRIVATE EVRootCAMetadata {
  public:
-#if BUILDFLAG(IS_WIN)
-  typedef const char* PolicyOID;
-#else
-  // DER-encoded OID value (no tag or length).
-  typedef der::Input PolicyOID;
-#endif
-
   static EVRootCAMetadata* GetInstance();
 
   EVRootCAMetadata(const EVRootCAMetadata&) = delete;
   EVRootCAMetadata& operator=(const EVRootCAMetadata&) = delete;
 
   // Returns true if policy_oid is an EV policy OID of some root CA.
-  bool IsEVPolicyOID(PolicyOID policy_oid) const;
-
-  // Same as above but using the the DER-encoded OID (no tag or length).
-  bool IsEVPolicyOIDGivenBytes(const der::Input& policy_oid) const;
+  bool IsEVPolicyOID(der::Input policy_oid) const;
 
   // Returns true if the root CA with the given certificate fingerprint has
   // the EV policy OID policy_oid.
   bool HasEVPolicyOID(const SHA256HashValue& fingerprint,
-                      PolicyOID policy_oid) const;
-
-  // Same as above but using the the DER-encoded OID (no tag or length).
-  bool HasEVPolicyOIDGivenBytes(const SHA256HashValue& fingerprint,
-                                const der::Input& policy_oid) const;
-
-#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
-  // Returns true if |policy_oid| is for 2.23.140.1.1 (CA/Browser Forum's
-  // Extended Validation Policy). This is used as a hack by the
-  // platform-specific CertVerifyProcs when doing EV verification.
-  static bool IsCaBrowserForumEvOid(PolicyOID policy_oid);
-#endif
+                      der::Input policy_oid) const;
 
   // AddEVCA adds an EV CA to the list of known EV CAs with the given policy.
   // |policy| is expressed as a string of dotted numbers. It returns true on
@@ -87,16 +66,11 @@
   EVRootCAMetadata();
   ~EVRootCAMetadata();
 
-#if BUILDFLAG(IS_WIN)
-  using ExtraEVCAMap = std::map<SHA256HashValue, std::string>;
-
-  // extra_cas_ contains any EV CA metadata that was added at runtime.
-  ExtraEVCAMap extra_cas_;
-#elif defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
+#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
   using PolicyOIDMap = std::map<SHA256HashValue, std::vector<std::string>>;
 
   PolicyOIDMap ev_policy_;
-  std::set<std::string> policy_oids_;
+  std::set<std::string, std::less<>> policy_oids_;
 #endif
 };
 
diff --git a/net/cert/ev_root_ca_metadata_unittest.cc b/net/cert/ev_root_ca_metadata_unittest.cc
index e73b50c0..db5bf82 100644
--- a/net/cert/ev_root_ca_metadata_unittest.cc
+++ b/net/cert/ev_root_ca_metadata_unittest.cc
@@ -14,20 +14,14 @@
 
 namespace {
 
-#if BUILDFLAG(IS_WIN)
-const char kFakePolicyStr[] = "2.16.840.1.42";
-const char kCabEvPolicyStr[] = "2.23.140.1.1";
-const char kStarfieldPolicyStr[] = "2.16.840.1.114414.1.7.23.3";
-#elif defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
-const char kFakePolicyStr[] = "2.16.840.1.42";
-#endif
-
 #if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
+const char kFakePolicyStr[] = "2.16.840.1.42";
+
 // DER OID values (no tag or length).
-const uint8_t kFakePolicyBytes[] = {0x60, 0x86, 0x48, 0x01, 0x2a};
-const uint8_t kCabEvPolicyBytes[] = {0x67, 0x81, 0x0c, 0x01, 0x01};
-const uint8_t kStarfieldPolicyBytes[] = {0x60, 0x86, 0x48, 0x01, 0x86, 0xFD,
-                                         0x6E, 0x01, 0x07, 0x17, 0x03};
+const uint8_t kFakePolicy[] = {0x60, 0x86, 0x48, 0x01, 0x2a};
+const uint8_t kCabEvPolicy[] = {0x67, 0x81, 0x0c, 0x01, 0x01};
+const uint8_t kStarfieldPolicy[] = {0x60, 0x86, 0x48, 0x01, 0x86, 0xFD,
+                                    0x6E, 0x01, 0x07, 0x17, 0x03};
 
 const SHA256HashValue kFakeFingerprint = {
     {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
@@ -38,118 +32,46 @@
      0xa9, 0x95, 0x8e, 0x55, 0x90, 0xe4, 0x0f, 0xcc, 0x7f, 0xaa, 0x4f,
      0xb7, 0xc2, 0xc8, 0x67, 0x75, 0x21, 0xfb, 0x5f, 0xb6, 0x58}};
 
-class EVOidData {
- public:
-  EVOidData();
-  bool Init();
-
-  EVRootCAMetadata::PolicyOID fake_policy;
-  der::Input fake_policy_bytes;
-
-  EVRootCAMetadata::PolicyOID cab_ev_policy;
-  der::Input cab_ev_policy_bytes;
-
-  EVRootCAMetadata::PolicyOID starfield_policy;
-  der::Input starfield_policy_bytes;
-};
-
-#endif  // defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
-
-#if BUILDFLAG(IS_WIN)
-
-EVOidData::EVOidData()
-    : fake_policy(kFakePolicyStr),
-      fake_policy_bytes(kFakePolicyBytes),
-      cab_ev_policy(kCabEvPolicyStr),
-      cab_ev_policy_bytes(kCabEvPolicyBytes),
-      starfield_policy(kStarfieldPolicyStr),
-      starfield_policy_bytes(kStarfieldPolicyBytes) {}
-
-bool EVOidData::Init() {
-  return true;
-}
-
-#elif defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
-
-EVOidData::EVOidData()
-    : fake_policy(kFakePolicyBytes),
-      fake_policy_bytes(kFakePolicyBytes),
-      cab_ev_policy(kCabEvPolicyBytes),
-      cab_ev_policy_bytes(kCabEvPolicyBytes),
-      starfield_policy(kStarfieldPolicyBytes),
-      starfield_policy_bytes(kStarfieldPolicyBytes) {}
-
-bool EVOidData::Init() {
-  return true;
-}
-
-#endif
-
-#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
-
-class EVRootCAMetadataTest : public testing::Test {
- protected:
-  void SetUp() override { ASSERT_TRUE(ev_oid_data.Init()); }
-
-  EVOidData ev_oid_data;
-};
-
-TEST_F(EVRootCAMetadataTest, Basic) {
+TEST(EVRootCAMetadataTest, Basic) {
   EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance());
 
   // Contains an expected policy.
-  EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.starfield_policy));
-  EXPECT_TRUE(
-      ev_metadata->IsEVPolicyOIDGivenBytes(ev_oid_data.starfield_policy_bytes));
+  EXPECT_TRUE(ev_metadata->IsEVPolicyOID(der::Input(kStarfieldPolicy)));
 
   // Does not contain an unregistered policy.
-  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
-  EXPECT_FALSE(
-      ev_metadata->IsEVPolicyOIDGivenBytes(ev_oid_data.fake_policy_bytes));
+  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(der::Input(kFakePolicy)));
 
   // The policy is correct for the right root.
   EXPECT_TRUE(ev_metadata->HasEVPolicyOID(kStarfieldFingerprint,
-                                          ev_oid_data.starfield_policy));
-  EXPECT_TRUE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kStarfieldFingerprint, ev_oid_data.starfield_policy_bytes));
+                                          der::Input(kStarfieldPolicy)));
 
   // The policy does not match if the root does not match.
   EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint,
-                                           ev_oid_data.starfield_policy));
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kFakeFingerprint, ev_oid_data.starfield_policy_bytes));
+                                           der::Input(kStarfieldPolicy)));
 
   // The expected root only has the expected policies; it should fail to match
   // the root against both unknown policies as well as policies associated
   // with other roots.
   EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kStarfieldFingerprint,
-                                           ev_oid_data.fake_policy));
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kStarfieldFingerprint, ev_oid_data.fake_policy_bytes));
+                                           der::Input(kFakePolicy)));
 
   EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kStarfieldFingerprint,
-                                           ev_oid_data.cab_ev_policy));
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kStarfieldFingerprint, ev_oid_data.cab_ev_policy_bytes));
+                                           der::Input(kCabEvPolicy)));
 
-  // Test a completely bogus OID given bytes.
+  // Test a completely bogus OID.
   const uint8_t bad_oid[] = {0};
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(kStarfieldFingerprint,
-                                                     der::Input(bad_oid)));
+  EXPECT_FALSE(
+      ev_metadata->HasEVPolicyOID(kStarfieldFingerprint, der::Input(bad_oid)));
 }
 
-TEST_F(EVRootCAMetadataTest, AddRemove) {
+TEST(EVRootCAMetadataTest, AddRemove) {
   EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance());
 
   // An unregistered/junk policy should not work.
-  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
-  EXPECT_FALSE(
-      ev_metadata->IsEVPolicyOIDGivenBytes(ev_oid_data.fake_policy_bytes));
+  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(der::Input(kFakePolicy)));
 
   EXPECT_FALSE(
-      ev_metadata->HasEVPolicyOID(kFakeFingerprint, ev_oid_data.fake_policy));
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kFakeFingerprint, ev_oid_data.fake_policy_bytes));
+      ev_metadata->HasEVPolicyOID(kFakeFingerprint, der::Input(kFakePolicy)));
 
   {
     // However, this unregistered/junk policy can be temporarily registered
@@ -157,35 +79,17 @@
     ScopedTestEVPolicy test_ev_policy(ev_metadata, kFakeFingerprint,
                                       kFakePolicyStr);
 
-    EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
-    EXPECT_TRUE(
-        ev_metadata->IsEVPolicyOIDGivenBytes(ev_oid_data.fake_policy_bytes));
+    EXPECT_TRUE(ev_metadata->IsEVPolicyOID(der::Input(kFakePolicy)));
 
     EXPECT_TRUE(
-        ev_metadata->HasEVPolicyOID(kFakeFingerprint, ev_oid_data.fake_policy));
-    EXPECT_TRUE(ev_metadata->HasEVPolicyOIDGivenBytes(
-        kFakeFingerprint, ev_oid_data.fake_policy_bytes));
+        ev_metadata->HasEVPolicyOID(kFakeFingerprint, der::Input(kFakePolicy)));
   }
 
   // It should go out of scope when the ScopedTestEVPolicy goes out of scope.
-  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
-  EXPECT_FALSE(
-      ev_metadata->IsEVPolicyOIDGivenBytes(ev_oid_data.fake_policy_bytes));
+  EXPECT_FALSE(ev_metadata->IsEVPolicyOID(der::Input(kFakePolicy)));
 
   EXPECT_FALSE(
-      ev_metadata->HasEVPolicyOID(kFakeFingerprint, ev_oid_data.fake_policy));
-  EXPECT_FALSE(ev_metadata->HasEVPolicyOIDGivenBytes(
-      kFakeFingerprint, ev_oid_data.fake_policy_bytes));
-}
-
-TEST_F(EVRootCAMetadataTest, IsCaBrowserForumEvOid) {
-  EXPECT_TRUE(
-      EVRootCAMetadata::IsCaBrowserForumEvOid(ev_oid_data.cab_ev_policy));
-
-  EXPECT_FALSE(
-      EVRootCAMetadata::IsCaBrowserForumEvOid(ev_oid_data.fake_policy));
-  EXPECT_FALSE(
-      EVRootCAMetadata::IsCaBrowserForumEvOid(ev_oid_data.starfield_policy));
+      ev_metadata->HasEVPolicyOID(kFakeFingerprint, der::Input(kFakePolicy)));
 }
 
 #endif  // defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
diff --git a/net/cert/trial_comparison_cert_verifier_util.cc b/net/cert/trial_comparison_cert_verifier_util.cc
index 1c197ae..32dd6ea 100644
--- a/net/cert/trial_comparison_cert_verifier_util.cc
+++ b/net/cert/trial_comparison_cert_verifier_util.cc
@@ -70,8 +70,9 @@
   const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
   std::set<der::Input> candidate_oids;
   for (const der::Input& oid : leaf->policy_oids()) {
-    if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
+    if (ev_metadata->IsEVPolicyOID(oid)) {
       candidate_oids.insert(oid);
+    }
   }
 
   if (candidate_oids.size() <= 1)
@@ -83,8 +84,9 @@
                            sizeof(root_fingerprint.data));
 
   for (const der::Input& oid : candidate_oids) {
-    if (ev_metadata->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
+    if (ev_metadata->HasEVPolicyOID(root_fingerprint, oid)) {
       return true;
+    }
   }
 
   return false;
diff --git a/net/dns/fuzzed_host_resolver_util.cc b/net/dns/fuzzed_host_resolver_util.cc
index 1c8b4d5..41dd985 100644
--- a/net/dns/fuzzed_host_resolver_util.cc
+++ b/net/dns/fuzzed_host_resolver_util.cc
@@ -373,7 +373,8 @@
                             nullptr /* system_dns_config_notifier */,
                             net_log),
         data_provider_(data_provider),
-        is_ipv6_reachable_(data_provider->ConsumeBool()),
+        is_globally_reachable_(data_provider->ConsumeBool()),
+        start_globally_reachable_async_(data_provider->ConsumeBool()),
         socket_factory_(data_provider_),
         net_log_(net_log),
         data_provider_weak_factory_(data_provider) {
@@ -409,9 +410,16 @@
 
  private:
   // HostResolverManager implementation:
-  bool IsGloballyReachable(const IPAddress& dest,
-                           const NetLogWithSource& net_log) override {
-    return is_ipv6_reachable_;
+  int StartGloballyReachableCheck(const IPAddress& dest,
+                                  const NetLogWithSource& net_log,
+                                  CompletionOnceCallback callback) override {
+    int reachable_rv = is_globally_reachable_ ? OK : ERR_FAILED;
+    if (start_globally_reachable_async_) {
+      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), reachable_rv));
+      return ERR_IO_PENDING;
+    }
+    return reachable_rv;
   }
 
   void RunLoopbackProbeJob() override {
@@ -420,8 +428,10 @@
 
   const raw_ptr<FuzzedDataProvider> data_provider_;
 
-  // Fixed value to be returned by IsIPv6Reachable.
-  const bool is_ipv6_reachable_;
+  // Fixed value to be returned by StartGloballyReachableCheck.
+  const bool is_globally_reachable_;
+  // Determines if StartGloballyReachableCheck returns sync or async.
+  const bool start_globally_reachable_async_;
 
   // Used for UDP and TCP sockets if the async resolver is enabled.
   FuzzedSocketFactory socket_factory_;
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index d417d43..17e5d590 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -101,6 +101,7 @@
 #include "net/dns/record_parsed.h"
 #include "net/dns/resolve_context.h"
 #include "net/dns/test_dns_config_service.h"
+#include "net/http/http_network_session.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_event_type.h"
@@ -108,7 +109,7 @@
 #include "net/log/net_log_source_type.h"
 #include "net/log/net_log_with_source.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/datagram_client_socket.h"
+#include "net/url_request/url_request_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "url/scheme_host_port.h"
@@ -203,6 +204,17 @@
   return kDefault;
 }
 
+// Returns true if NAT64 can be used in place of an IPv4 address during host
+// resolution.
+bool MayUseNAT64ForIPv4Literal(HostResolverFlags flags,
+                               HostResolverSource source,
+                               const IPAddress& ip_address) {
+  return !(flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) &&
+         ip_address.IsValid() && ip_address.IsIPv4() &&
+         base::FeatureList::IsEnabled(features::kUseNAT64ForIPv4Literal) &&
+         (source != HostResolverSource::LOCAL_ONLY);
+}
+
 //-----------------------------------------------------------------------------
 
 // Returns true if it can determine that only loopback addresses are configured.
@@ -544,6 +556,57 @@
   return true;
 }
 
+struct HostResolverManager::JobKey {
+  explicit JobKey(ResolveContext* resolve_context)
+      : resolve_context(resolve_context->AsSafeRef()) {}
+
+  bool operator<(const JobKey& other) const {
+    return std::forward_as_tuple(query_types.ToEnumBitmask(), flags, source,
+                                 secure_dns_mode, &*resolve_context, host,
+                                 network_anonymization_key) <
+           std::forward_as_tuple(other.query_types.ToEnumBitmask(), other.flags,
+                                 other.source, other.secure_dns_mode,
+                                 &*other.resolve_context, other.host,
+                                 other.network_anonymization_key);
+  }
+
+  bool operator==(const JobKey& other) const {
+    return !(*this < other || other < *this);
+  }
+
+  absl::variant<url::SchemeHostPort, std::string> host;
+  NetworkAnonymizationKey network_anonymization_key;
+  DnsQueryTypeSet query_types;
+  HostResolverFlags flags;
+  HostResolverSource source;
+  SecureDnsMode secure_dns_mode;
+  base::SafeRef<ResolveContext> resolve_context;
+
+  HostCache::Key ToCacheKey(bool secure) const {
+    if (query_types.Size() != 1) {
+      // This function will produce identical cache keys for `JobKey` structs
+      // that differ only in their (non-singleton) `query_types` fields. When we
+      // enable new query types, this behavior could lead to subtle bugs. That
+      // is why the following DCHECK restricts the allowable query types.
+      DCHECK(Difference(query_types,
+                        DnsQueryTypeSet(DnsQueryType::A, DnsQueryType::AAAA,
+                                        DnsQueryType::HTTPS))
+                 .Empty());
+    }
+    const DnsQueryType query_type_for_key = query_types.Size() == 1
+                                                ? *query_types.begin()
+                                                : DnsQueryType::UNSPECIFIED;
+    HostCache::Key key(host, query_type_for_key, flags, source,
+                       network_anonymization_key);
+    key.secure = secure;
+    return key;
+  }
+
+  handles::NetworkHandle GetTargetNetwork() const {
+    return resolve_context->GetTargetNetwork();
+  }
+};
+
 // Holds the callback and request parameters for an outstanding request.
 //
 // The RequestImpl is owned by the end user of host resolution. Deletion prior
@@ -580,6 +643,7 @@
         host_resolver_flags_(
             HostResolver::ParametersToHostResolverFlags(parameters_)),
         priority_(parameters_.initial_priority),
+        job_key_(JobKey(resolve_context_.get())),
         resolver_(std::move(resolver)),
         tick_clock_(tick_clock) {}
 
@@ -606,18 +670,147 @@
     }
 
     LogStartRequest();
-    int rv = resolver_->Resolve(this);
-    DCHECK(!complete_);
-    if (rv == ERR_IO_PENDING) {
-      CHECK(job_.has_value());
-      callback_ = std::move(callback);
-    } else {
-      CHECK(!job_.has_value());
-      complete_ = true;
-      LogFinishRequest(rv, false /* async_completion */);
-    }
-    resolver_.reset();
 
+    next_state_ = STATE_IPV6_REACHABILITY;
+    callback_ = std::move(callback);
+
+    int rv = OK;
+    rv = DoLoop(rv);
+    return rv;
+  }
+
+  int DoLoop(int rv) {
+    do {
+      ResolveState state = next_state_;
+      next_state_ = STATE_NONE;
+      switch (state) {
+        case STATE_IPV6_REACHABILITY:
+          rv = DoIPv6Reachability();
+          break;
+        case STATE_GET_PARAMETERS:
+          DCHECK_EQ(OK, rv);
+          rv = DoGetParameters();
+          break;
+        case STATE_GET_PARAMETERS_COMPLETE:
+          rv = DoGetParametersComplete(rv);
+          break;
+        case STATE_RESOLVE_LOCALLY:
+          rv = DoResolveLocally();
+          break;
+        case STATE_START_JOB:
+          rv = DoStartJob();
+          break;
+        case STATE_FINISH_REQUEST:
+          rv = DoFinishRequest(rv);
+          break;
+        default:
+          NOTREACHED() << "next_state_: " << next_state_;
+          break;
+      }
+    } while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);
+
+    return rv;
+  }
+
+  void OnIOComplete(int rv) {
+    rv = DoLoop(rv);
+    if (rv != ERR_IO_PENDING && !callback_.is_null()) {
+      std::move(callback_).Run(rv);
+    }
+  }
+
+  int DoIPv6Reachability() {
+    next_state_ = STATE_GET_PARAMETERS;
+    // If a single reachability probe has not been completed, and the latest
+    // probe will return asynchronously, return ERR_NAME_NOT_RESOLVED when the
+    // request source is LOCAL_ONLY. This is due to LOCAL_ONLY requiring a
+    // synchronous response, so it cannot wait on an async probe result and
+    // cannot make assumptions about reachability.
+    if (parameters_.source == HostResolverSource::LOCAL_ONLY) {
+      int rv = resolver_->StartIPv6ReachabilityCheck(
+          source_net_log_, base::DoNothingAs<void(int)>());
+      if (rv == ERR_IO_PENDING) {
+        next_state_ = STATE_FINISH_REQUEST;
+        return ERR_NAME_NOT_RESOLVED;
+      }
+      return OK;
+    }
+    return resolver_->StartIPv6ReachabilityCheck(
+        source_net_log_, base::BindOnce(&RequestImpl::OnIOComplete,
+                                        weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  int DoGetParameters() {
+    job_key_.host =
+        CreateHostForJobKey(request_host_, parameters_.dns_query_type,
+                            resolver_->https_svcb_options_.enable);
+    job_key_.network_anonymization_key = network_anonymization_key_;
+    job_key_.source = parameters_.source;
+
+    bool is_ip = ip_address_.AssignFromIPLiteral(GetHostname(job_key_.host));
+
+    resolver_->GetEffectiveParametersForRequest(
+        job_key_.host, parameters_.dns_query_type, host_resolver_flags_,
+        parameters_.secure_dns_policy, is_ip, source_net_log_,
+        &job_key_.query_types, &job_key_.flags, &job_key_.secure_dns_mode);
+
+    // A reachability probe to determine if the network is only reachable on
+    // IPv6 will be scheduled if the parameters are met for using NAT64 in place
+    // of an IPv4 address.
+    if (MayUseNAT64ForIPv4Literal(job_key_.flags, parameters_.source,
+                                  ip_address_) &&
+        resolver_->last_ipv6_probe_result_) {
+      next_state_ = STATE_GET_PARAMETERS_COMPLETE;
+      return resolver_->StartGloballyReachableCheck(
+          ip_address_, source_net_log_,
+          base::BindOnce(&RequestImpl::OnIOComplete,
+                         weak_ptr_factory_.GetWeakPtr()));
+    }
+    next_state_ = STATE_RESOLVE_LOCALLY;
+    return OK;
+  }
+
+  int DoGetParametersComplete(int rv) {
+    next_state_ = STATE_RESOLVE_LOCALLY;
+    only_ipv6_reachable_ = (rv == ERR_FAILED) ? true : false;
+    return OK;
+  }
+
+  int DoResolveLocally() {
+    absl::optional<HostCache::EntryStaleness> stale_info;
+    HostCache::Entry results = resolver_->ResolveLocally(
+        only_ipv6_reachable_, job_key_, ip_address_, parameters_.cache_usage,
+        parameters_.secure_dns_policy, parameters_.source, source_net_log_,
+        host_cache_, &tasks_, &stale_info);
+    if (results.error() != ERR_DNS_CACHE_MISS ||
+        parameters_.source == HostResolverSource::LOCAL_ONLY ||
+        tasks_.empty()) {
+      if (results.error() == OK && !parameters_.is_speculative) {
+        set_results(results.CopyWithDefaultPort(request_host_.GetPort()));
+      }
+      if (stale_info && !parameters_.is_speculative) {
+        set_stale_info(std::move(stale_info).value());
+      }
+      next_state_ = STATE_FINISH_REQUEST;
+      return results.error();
+    }
+    next_state_ = STATE_START_JOB;
+    return OK;
+  }
+
+  int DoStartJob() {
+    resolver_->CreateAndStartJob(std::move(job_key_), std::move(tasks_), this);
+    DCHECK(!complete_);
+    resolver_.reset();
+    return ERR_IO_PENDING;
+  }
+
+  int DoFinishRequest(int rv) {
+    CHECK(!job_.has_value());
+    complete_ = true;
+    set_error_info(rv, /*is_secure_network_error=*/false);
+    rv = HostResolver::SquashErrorCode(rv);
+    LogFinishRequest(rv, /*async_completion=*/false);
     return rv;
   }
 
@@ -754,6 +947,16 @@
   bool complete() const { return complete_; }
 
  private:
+  enum ResolveState {
+    STATE_IPV6_REACHABILITY,
+    STATE_GET_PARAMETERS,
+    STATE_GET_PARAMETERS_COMPLETE,
+    STATE_RESOLVE_LOCALLY,
+    STATE_START_JOB,
+    STATE_FINISH_REQUEST,
+    STATE_NONE,
+  };
+
   void FixUpEndpointAndAliasResults() {
     DCHECK(results_.has_value());
     DCHECK(!legacy_address_results_.has_value());
@@ -835,6 +1038,11 @@
 
   RequestPriority priority_;
 
+  ResolveState next_state_;
+  JobKey job_key_;
+  IPAddress ip_address_;
+
+  std::deque<TaskType> tasks_;
   // The resolve job that this request is dependent on.
   absl::optional<base::SafeRef<Job>> job_;
   base::WeakPtr<HostResolverManager> resolver_ = nullptr;
@@ -843,6 +1051,7 @@
   CompletionOnceCallback callback_;
 
   bool complete_ = false;
+  bool only_ipv6_reachable_ = false;
   absl::optional<HostCache::Entry> results_;
   absl::optional<HostCache::EntryStaleness> stale_info_;
   absl::optional<AddressList> legacy_address_results_;
@@ -854,6 +1063,8 @@
   base::TimeTicks request_time_;
 
   SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<RequestImpl> weak_ptr_factory_{this};
 };
 
 class HostResolverManager::ProbeRequestImpl
@@ -1758,57 +1969,6 @@
 
 //-----------------------------------------------------------------------------
 
-struct HostResolverManager::JobKey {
-  explicit JobKey(ResolveContext* resolve_context)
-      : resolve_context(resolve_context->AsSafeRef()) {}
-
-  bool operator<(const JobKey& other) const {
-    return std::forward_as_tuple(query_types.ToEnumBitmask(), flags, source,
-                                 secure_dns_mode, &*resolve_context, host,
-                                 network_anonymization_key) <
-           std::forward_as_tuple(other.query_types.ToEnumBitmask(), other.flags,
-                                 other.source, other.secure_dns_mode,
-                                 &*other.resolve_context, other.host,
-                                 other.network_anonymization_key);
-  }
-
-  bool operator==(const JobKey& other) const {
-    return !(*this < other || other < *this);
-  }
-
-  absl::variant<url::SchemeHostPort, std::string> host;
-  NetworkAnonymizationKey network_anonymization_key;
-  DnsQueryTypeSet query_types;
-  HostResolverFlags flags;
-  HostResolverSource source;
-  SecureDnsMode secure_dns_mode;
-  base::SafeRef<ResolveContext> resolve_context;
-
-  HostCache::Key ToCacheKey(bool secure) const {
-    if (query_types.Size() != 1) {
-      // This function will produce identical cache keys for `JobKey` structs
-      // that differ only in their (non-singleton) `query_types` fields. When we
-      // enable new query types, this behavior could lead to subtle bugs. That
-      // is why the following DCHECK restricts the allowable query types.
-      DCHECK(Difference(query_types,
-                        DnsQueryTypeSet(DnsQueryType::A, DnsQueryType::AAAA,
-                                        DnsQueryType::HTTPS))
-                 .Empty());
-    }
-    const DnsQueryType query_type_for_key = query_types.Size() == 1
-                                                ? *query_types.begin()
-                                                : DnsQueryType::UNSPECIFIED;
-    HostCache::Key key(host, query_type_for_key, flags, source,
-                       network_anonymization_key);
-    key.secure = secure;
-    return key;
-  }
-
-  handles::NetworkHandle GetTargetNetwork() const {
-    return resolve_context->GetTargetNetwork();
-  }
-};
-
 // Aggregates all Requests for the same Key. Dispatched via
 // PrioritizedDispatcher.
 class HostResolverManager::Job : public PrioritizedDispatcher::Job,
@@ -2694,7 +2854,7 @@
       }
       req->OnJobCompleted(
           key_, results.error(),
-          secure && results.error() != OK /* is_secure_network_error */);
+          /*is_secure_network_error=*/secure && results.error() != OK);
 
       // Check if the resolver was destroyed as a result of running the
       // callback. If it was, we could continue, but we choose to bail.
@@ -3119,61 +3279,8 @@
   }
 }
 
-int HostResolverManager::Resolve(RequestImpl* request) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // Request should not yet have a scheduled Job.
-  DCHECK(!request->HasJob());
-  // Request may only be resolved once.
-  DCHECK(!request->complete());
-  // MDNS requests do not support skipping cache or stale lookups.
-  // TODO(crbug.com/926300): Either add support for skipping the MDNS cache, or
-  // merge to use the normal host cache for MDNS requests.
-  DCHECK(request->parameters().source != HostResolverSource::MULTICAST_DNS ||
-         request->parameters().cache_usage ==
-             ResolveHostParameters::CacheUsage::ALLOWED);
-  DCHECK(!invalidation_in_progress_);
-
-  const auto& parameters = request->parameters();
-  JobKey job_key(request->resolve_context());
-  job_key.host =
-      CreateHostForJobKey(request->request_host(), parameters.dns_query_type,
-                          https_svcb_options_.enable);
-  job_key.network_anonymization_key = request->network_anonymization_key();
-  job_key.source = parameters.source;
-
-  IPAddress ip_address;
-  bool is_ip = ip_address.AssignFromIPLiteral(GetHostname(job_key.host));
-
-  GetEffectiveParametersForRequest(
-      job_key.host, parameters.dns_query_type, request->host_resolver_flags(),
-      parameters.secure_dns_policy, is_ip, request->source_net_log(),
-      &job_key.query_types, &job_key.flags, &job_key.secure_dns_mode);
-
-  std::deque<TaskType> tasks;
-  absl::optional<HostCache::EntryStaleness> stale_info;
-  HostCache::Entry results = ResolveLocally(
-      job_key, ip_address, parameters.cache_usage, parameters.secure_dns_policy,
-      parameters.source, request->source_net_log(), request->host_cache(),
-      &tasks, &stale_info);
-  if (results.error() != ERR_DNS_CACHE_MISS ||
-      request->parameters().source == HostResolverSource::LOCAL_ONLY ||
-      tasks.empty()) {
-    if (results.error() == OK && !request->parameters().is_speculative) {
-      request->set_results(
-          results.CopyWithDefaultPort(request->request_host().GetPort()));
-    }
-    if (stale_info && !request->parameters().is_speculative)
-      request->set_stale_info(std::move(stale_info).value());
-    request->set_error_info(results.error(),
-                            false /* is_secure_network_error */);
-    return HostResolver::SquashErrorCode(results.error());
-  }
-
-  CreateAndStartJob(std::move(job_key), std::move(tasks), request);
-  return ERR_IO_PENDING;
-}
-
 HostCache::Entry HostResolverManager::ResolveLocally(
+    bool only_ipv6_reachable,
     const JobKey& job_key,
     const IPAddress& ip_address,
     ResolveHostParameters::CacheUsage cache_usage,
@@ -3218,10 +3325,8 @@
 
   if (ip_address.IsValid()) {
     // Use NAT64Task for IPv4 literal when the network is IPv6 only.
-    if (!default_family_due_to_no_ipv6 && ip_address.IsIPv4() &&
-        base::FeatureList::IsEnabled(features::kUseNAT64ForIPv4Literal) &&
-        source != HostResolverSource::LOCAL_ONLY &&
-        !IsGloballyReachable(IPAddress(ip_address), source_net_log)) {
+    if (MayUseNAT64ForIPv4Literal(job_key.flags, source, ip_address) &&
+        only_ipv6_reachable) {
       out_tasks->push_front(TaskType::NAT64);
       return HostCache::Entry(ERR_DNS_CACHE_MISS,
                               HostCache::Entry::SOURCE_UNKNOWN);
@@ -3781,7 +3886,7 @@
   // resolution based on a probe. Prior logic ensures that this is an automatic
   // query, so the code requesting the resolution should be amenable to
   // receiving an IPv6 resolution.
-  if (!use_local_ipv6 && !is_ip && !IsIPv6Reachable(net_log)) {
+  if (!use_local_ipv6 && !is_ip && !last_ipv6_probe_result_) {
     *out_effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
     effective_types.Remove(DnsQueryType::AAAA);
   }
@@ -3815,59 +3920,125 @@
 
 }  // namespace
 
-bool HostResolverManager::IsIPv6Reachable(const NetLogWithSource& net_log) {
+void HostResolverManager::FinishIPv6ReachabilityCheck(
+    CompletionOnceCallback callback,
+    int rv) {
+  SetLastIPv6ProbeResult((rv == OK) ? true : false);
+  std::move(callback).Run(OK);
+  if (!ipv6_request_callbacks_.empty()) {
+    for (auto& request_callback : ipv6_request_callbacks_) {
+      std::move(request_callback).Run(OK);
+    }
+    ipv6_request_callbacks_.clear();
+  }
+}
+
+int HostResolverManager::StartIPv6ReachabilityCheck(
+    const NetLogWithSource& net_log,
+    CompletionOnceCallback callback) {
   // Don't bother checking if the request will use WiFi and IPv6 is assumed to
   // not work on WiFi.
-  if (!check_ipv6_on_wifi_ && RequestWillUseWiFi(target_network_))
-    return false;
+  if (!check_ipv6_on_wifi_ && RequestWillUseWiFi(target_network_)) {
+    probing_ipv6_ = false;
+    last_ipv6_probe_result_ = false;
+    last_ipv6_probe_time_ = base::TimeTicks();
+    return OK;
+  }
 
+  if (probing_ipv6_) {
+    ipv6_request_callbacks_.push_back(std::move(callback));
+    return ERR_IO_PENDING;
+  }
   // Cache the result for kIPv6ProbePeriodMs (measured from after
-  // IsGloballyReachable() completes).
+  // StartGloballyReachableCheck() completes).
+  int rv = OK;
   bool cached = true;
   if (last_ipv6_probe_time_.is_null() ||
       (tick_clock_->NowTicks() - last_ipv6_probe_time_).InMilliseconds() >
           kIPv6ProbePeriodMs) {
-    SetLastIPv6ProbeResult(
-        IsGloballyReachable(IPAddress(kIPv6ProbeAddress), net_log));
+    probing_ipv6_ = true;
+    rv = StartGloballyReachableCheck(
+        IPAddress(kIPv6ProbeAddress), net_log,
+        base::BindOnce(&HostResolverManager::FinishIPv6ReachabilityCheck,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    if (rv != ERR_IO_PENDING) {
+      SetLastIPv6ProbeResult((rv == OK) ? true : false);
+      rv = OK;
+    }
     cached = false;
   }
   net_log.AddEvent(
       NetLogEventType::HOST_RESOLVER_MANAGER_IPV6_REACHABILITY_CHECK, [&] {
         return NetLogIPv6AvailableParams(last_ipv6_probe_result_, cached);
       });
-  return last_ipv6_probe_result_;
+  return rv;
 }
 
 void HostResolverManager::SetLastIPv6ProbeResult(bool last_ipv6_probe_result) {
+  probing_ipv6_ = false;
   last_ipv6_probe_result_ = last_ipv6_probe_result;
   last_ipv6_probe_time_ = tick_clock_->NowTicks();
 }
 
-bool HostResolverManager::IsGloballyReachable(const IPAddress& dest,
-                                              const NetLogWithSource& net_log) {
-  std::unique_ptr<DatagramClientSocket> socket(
+int HostResolverManager::StartGloballyReachableCheck(
+    const IPAddress& dest,
+    const NetLogWithSource& net_log,
+    CompletionOnceCallback callback) {
+  std::unique_ptr<DatagramClientSocket> probing_socket =
       ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket(
-          DatagramSocket::DEFAULT_BIND, net_log.net_log(), net_log.source()));
-  int rv = socket->Connect(IPEndPoint(dest, 53));
-  if (rv != OK)
+          DatagramSocket::DEFAULT_BIND, net_log.net_log(), net_log.source());
+  DatagramClientSocket* probing_socket_ptr = probing_socket.get();
+  auto refcounted_socket = base::MakeRefCounted<
+      base::RefCountedData<std::unique_ptr<DatagramClientSocket>>>(
+      std::move(probing_socket));
+  int rv = probing_socket_ptr->ConnectAsync(
+      IPEndPoint(dest, 53),
+      base::BindOnce(&HostResolverManager::RunFinishGloballyReachableCheck,
+                     weak_ptr_factory_.GetWeakPtr(), refcounted_socket,
+                     std::move(callback)));
+  if (rv != ERR_IO_PENDING) {
+    rv = FinishGloballyReachableCheck(probing_socket_ptr, rv) ? OK : ERR_FAILED;
+  }
+  return rv;
+}
+
+bool HostResolverManager::FinishGloballyReachableCheck(
+    DatagramClientSocket* socket,
+    int rv) {
+  if (rv != OK) {
     return false;
+  }
   IPEndPoint endpoint;
   rv = socket->GetLocalAddress(&endpoint);
-  if (rv != OK)
+
+  if (rv != OK) {
     return false;
+  }
   const IPAddress& address = endpoint.address();
 
-  if (address.IsLinkLocal())
+  if (address.IsLinkLocal()) {
     return false;
+  }
 
   if (address.IsIPv6()) {
     const uint8_t kTeredoPrefix[] = {0x20, 0x01, 0, 0};
-    if (IPAddressStartsWith(address, kTeredoPrefix))
+    if (IPAddressStartsWith(address, kTeredoPrefix)) {
       return false;
+    }
   }
+
   return true;
 }
 
+void HostResolverManager::RunFinishGloballyReachableCheck(
+    scoped_refptr<base::RefCountedData<std::unique_ptr<DatagramClientSocket>>>
+        socket,
+    CompletionOnceCallback callback,
+    int rv) {
+  bool is_reachable = FinishGloballyReachableCheck(socket->data.get(), rv);
+  std::move(callback).Run(is_reachable ? OK : ERR_FAILED);
+}
+
 void HostResolverManager::RunLoopbackProbeJob() {
   // Run this asynchronously as it can take 40-100ms and should not block
   // initialization.
@@ -4135,7 +4306,10 @@
 void HostResolverManager::RequestImpl::ChangeRequestPriority(
     RequestPriority priority) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK(job_.has_value());
+  if (!job_.has_value()) {
+    priority_ = priority;
+    return;
+  }
   job_.value()->ChangeRequestPriority(this, priority);
 }
 
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 14044355..1402736 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -42,6 +42,7 @@
 #include "net/dns/resolve_context.h"
 #include "net/dns/system_dns_config_change_notifier.h"
 #include "net/log/net_log_with_source.h"
+#include "net/socket/datagram_client_socket.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "url/gurl.h"
@@ -297,10 +298,6 @@
   // Returns true if the task is local, synchronous, and instantaneous.
   static bool IsLocalTask(TaskType task);
 
-  // Attempts host resolution for |request|. Generally only expected to be
-  // called from RequestImpl::Start().
-  int Resolve(RequestImpl* request);
-
   // Attempts host resolution using fast local sources: IP literal resolution,
   // cache lookup, HOSTS lookup (if enabled), and localhost. Returns results
   // with error() OK if successful, ERR_NAME_NOT_RESOLVED if input is invalid,
@@ -317,6 +314,7 @@
   // If |cache_usage == ResolveHostParameters::CacheUsage::STALE_ALLOWED|, then
   // stale cache entries can be returned.
   HostCache::Entry ResolveLocally(
+      bool only_ipv6_reachable,
       const JobKey& job_key,
       const IPAddress& ip_address,
       ResolveHostParameters::CacheUsage cache_usage,
@@ -424,17 +422,36 @@
       HostResolverFlags* out_effective_flags,
       SecureDnsMode* out_effective_secure_dns_mode);
 
-  // Probes IPv6 support and returns true if IPv6 support is enabled.
-  // Results are cached, i.e. when called repeatedly this method returns result
-  // from the first probe for some time before probing again.
-  bool IsIPv6Reachable(const NetLogWithSource& net_log);
+  // Schedules probes to check IPv6 support. Returns OK if probe results are
+  // already cached, and ERR_IO_PENDING when a probe is scheduled to be
+  // completed asynchronously. When called repeatedly this method returns OK to
+  // confirm that results have been cached.
+  int StartIPv6ReachabilityCheck(const NetLogWithSource& net_log,
+                                 CompletionOnceCallback callback);
+
+  void FinishIPv6ReachabilityCheck(CompletionOnceCallback callback, int rv);
 
   // Sets |last_ipv6_probe_result_| and updates |last_ipv6_probe_time_|.
   void SetLastIPv6ProbeResult(bool last_ipv6_probe_result);
 
-  // Attempts to connect a UDP socket to |dest|:53. Virtual for testing.
-  virtual bool IsGloballyReachable(const IPAddress& dest,
-                                   const NetLogWithSource& net_log);
+  // Attempts to connect a UDP socket to |dest|:53. Virtual for testing. Returns
+  // the value of the attempted socket connection and the reachability check. If
+  // the return value from the connection is not ERR_IO_PENDING, callers must
+  // handle the results of the reachability check themselves. Otherwise the
+  // result of the reachability check will be set when `callback` is run.
+  // Returns OK if the reachability check succeeded, ERR_FAILED if it failed,
+  // ERR_IO_PENDING if it will be asynchronous.
+  virtual int StartGloballyReachableCheck(const IPAddress& dest,
+                                          const NetLogWithSource& net_log,
+                                          CompletionOnceCallback callback);
+
+  bool FinishGloballyReachableCheck(DatagramClientSocket* socket, int rv);
+
+  void RunFinishGloballyReachableCheck(
+      scoped_refptr<base::RefCountedData<std::unique_ptr<DatagramClientSocket>>>
+          socket,
+      CompletionOnceCallback callback,
+      int rv);
 
   // Asynchronously checks if only loopback IPs are available.
   virtual void RunLoopbackProbeJob();
@@ -534,6 +551,7 @@
 
   base::TimeTicks last_ipv6_probe_time_;
   bool last_ipv6_probe_result_ = true;
+  bool probing_ipv6_ = false;
 
   // Any resolver flags that should be added to a request by default.
   HostResolverFlags additional_resolver_flags_ = 0;
@@ -557,6 +575,8 @@
   // An experimental flag for features::kUseDnsHttpsSvcb.
   HostResolver::HttpsSvcbOptions https_svcb_options_;
 
+  std::vector<CompletionOnceCallback> ipv6_request_callbacks_;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<HostResolverManager> weak_ptr_factory_{this};
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index c4582081..3aafba4 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -32,6 +32,7 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -78,6 +79,7 @@
 #include "net/log/net_log_with_source.h"
 #include "net/log/test_net_log.h"
 #include "net/log/test_net_log_util.h"
+#include "net/proxy_resolution/configured_proxy_resolution_service.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_test_util.h"
 #include "net/test/gtest_util.h"
@@ -477,24 +479,35 @@
                           SystemDnsConfigChangeNotifier* notifier,
                           NetLog* net_log,
                           bool ipv6_reachable = true,
-                          bool ipv4_reachable = true)
+                          bool ipv4_reachable = true,
+                          bool is_async = false)
       : HostResolverManager(options, notifier, net_log),
         ipv6_reachable_(ipv6_reachable),
-        ipv4_reachable_(ipv4_reachable) {}
+        ipv4_reachable_(ipv4_reachable),
+        is_async_(is_async) {}
 
   ~TestHostResolverManager() override = default;
 
  private:
   const bool ipv6_reachable_;
   const bool ipv4_reachable_;
+  const bool is_async_;
 
-  bool IsGloballyReachable(const IPAddress& dest,
-                           const NetLogWithSource& net_log) override {
+  int StartGloballyReachableCheck(const IPAddress& dest,
+                                  const NetLogWithSource& net_log,
+                                  CompletionOnceCallback callback) override {
+    int rv = OK;
     if (dest.IsIPv6()) {
-      return ipv6_reachable_;
+      rv = ipv6_reachable_ ? OK : ERR_FAILED;
     } else {
-      return ipv4_reachable_;
+      rv = ipv4_reachable_ ? OK : ERR_FAILED;
     }
+    if (is_async_) {
+      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), rv));
+      return ERR_IO_PENDING;
+    }
+    return rv;
   }
 };
 
@@ -554,13 +567,29 @@
 
   // This HostResolverManager will only allow 1 outstanding resolve at a time
   // and perform no retries.
-  void CreateSerialResolver(bool check_ipv6_on_wifi = true) {
+  void CreateSerialResolver(bool check_ipv6_on_wifi = true,
+                            bool ipv6_reachable = true,
+                            bool is_async = false) {
     HostResolverSystemTask::Params params = DefaultParams(proc_);
     params.max_retry_attempts = 0u;
-    CreateResolverWithLimitsAndParams(1u, params, true /* ipv6_reachable */,
-                                      check_ipv6_on_wifi);
+    CreateResolverWithLimitsAndParams(1u, params, ipv6_reachable,
+                                      check_ipv6_on_wifi, is_async);
   }
 
+  void StaleAllowedFromIpTest(bool is_async);
+  void LocalOnlyFromIpTest(bool is_async);
+  void ChangePriorityTest(bool is_async);
+  void AbortOnlyExistingRequestsOnIPAddressChangeTest(bool is_async);
+  void FlushCacheOnIPAddressChangeTest(bool is_async);
+  void AbortOnIPAddressChangedTest(bool is_async);
+  void NumericIPv6AddressTest(bool is_async);
+  void NumericIPv6AddressWithSchemeTest(bool is_async);
+  void LocalhostIPV4IPV6LookupTest(bool is_async);
+  void IPv4AddressLiteralInIPv6OnlyNetworkTest(bool is_async);
+  void IPv4AddressLiteralInIPv6OnlyNetworkPort443Test(bool is_async);
+  void IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Test(bool is_async);
+  void IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(bool is_async);
+
  protected:
   // testing::Test implementation:
   void SetUp() override {
@@ -582,13 +611,14 @@
       size_t max_concurrent_resolves,
       const HostResolverSystemTask::Params& params,
       bool ipv6_reachable,
-      bool check_ipv6_on_wifi) {
+      bool check_ipv6_on_wifi,
+      bool is_async = false) {
     HostResolver::ManagerOptions options = DefaultOptions();
     options.max_concurrent_resolves = max_concurrent_resolves;
     options.check_ipv6_on_wifi = check_ipv6_on_wifi;
 
     CreateResolverWithOptionsAndParams(std::move(options), params,
-                                       ipv6_reachable);
+                                       ipv6_reachable, is_async);
   }
 
   virtual HostResolver::ManagerOptions DefaultOptions() {
@@ -602,6 +632,7 @@
       HostResolver::ManagerOptions options,
       const HostResolverSystemTask::Params& params,
       bool ipv6_reachable,
+      bool is_async = false,
       bool ipv4_reachable = true) {
     // Use HostResolverManagerDnsTest if enabling DNS client.
     DCHECK(!options.insecure_dns_client_enabled);
@@ -610,9 +641,8 @@
 
     resolver_ = std::make_unique<TestHostResolverManager>(
         options, nullptr /* notifier */, nullptr /* net_log */, ipv6_reachable,
-        ipv4_reachable);
+        ipv4_reachable, is_async);
     resolver_->set_host_resolver_system_params_for_test(params);
-
     resolver_->RegisterResolveContext(resolve_context_.get());
   }
 
@@ -631,10 +661,13 @@
     return DnsClient::kMaxInsecureFallbackFailures;
   }
 
-  bool IsIPv6Reachable(const NetLogWithSource& net_log) {
-    return resolver_->IsIPv6Reachable(net_log);
+  int StartIPv6ReachabilityCheck(const NetLogWithSource& net_log,
+                                 CompletionOnceCallback callback) {
+    return resolver_->StartIPv6ReachabilityCheck(net_log, std::move(callback));
   }
 
+  bool GetLastIpv6ProbeResult() { return resolver_->last_ipv6_probe_result_; }
+
   void PopulateCache(const HostCache::Key& key, IPEndPoint endpoint) {
     resolver_->CacheResult(resolve_context_->host_cache(), key,
                            HostCache::Entry(OK, {endpoint}, /*aliases=*/{},
@@ -843,7 +876,10 @@
               testing::Pointee(testing::IsEmpty()));
 }
 
-TEST_F(HostResolverManagerTest, LocalhostIPV4IPV6Lookup) {
+void HostResolverManagerTest::LocalhostIPV4IPV6LookupTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   HostResolver::ResolveHostParameters parameters;
 
   parameters.dns_query_type = DnsQueryType::A;
@@ -885,6 +921,14 @@
               CreateExpected("::1", 80), CreateExpected("127.0.0.1", 80))))));
 }
 
+TEST_F(HostResolverManagerTest, LocalhostIPV4IPV6LookupAsync) {
+  LocalhostIPV4IPV6LookupTest(true);
+}
+
+TEST_F(HostResolverManagerTest, LocalhostIPV4IPV6LookupSync) {
+  LocalhostIPV4IPV6LookupTest(false);
+}
+
 TEST_F(HostResolverManagerTest, ResolveIPLiteralWithHostResolverSystemOnly) {
   const char kIpLiteral[] = "178.78.32.1";
   // Add a mapping to tell if the resolver proc was called (if it was called,
@@ -1012,7 +1056,10 @@
           testing::UnorderedElementsAre(CreateExpected("127.1.2.3", 5555))))));
 }
 
-TEST_F(HostResolverManagerTest, NumericIPv6Address) {
+void HostResolverManagerTest::NumericIPv6AddressTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   // Resolve a plain IPv6 address.  Don't worry about [brackets], because
   // the caller should have removed them.
   ResolveHostResponseHelper response(resolver_->CreateRequest(
@@ -1029,7 +1076,18 @@
                       CreateExpected("2001:db8::1", 5555))))));
 }
 
-TEST_F(HostResolverManagerTest, NumericIPv6AddressWithScheme) {
+TEST_F(HostResolverManagerTest, NumericIPv6AddressAsync) {
+  NumericIPv6AddressTest(true);
+}
+
+TEST_F(HostResolverManagerTest, NumericIPv6AddressSync) {
+  NumericIPv6AddressTest(false);
+}
+
+void HostResolverManagerTest::NumericIPv6AddressWithSchemeTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       url::SchemeHostPort(url::kFtpScheme, "[2001:db8::1]", 5555),
       NetworkAnonymizationKey(), NetLogWithSource(), absl::nullopt,
@@ -1044,6 +1102,14 @@
                       CreateExpected("2001:db8::1", 5555))))));
 }
 
+TEST_F(HostResolverManagerTest, NumericIPv6AddressWithSchemeAsync) {
+  NumericIPv6AddressWithSchemeTest(true);
+}
+
+TEST_F(HostResolverManagerTest, NumericIPv6AddressWithSchemeSync) {
+  NumericIPv6AddressWithSchemeTest(false);
+}
+
 TEST_F(HostResolverManagerTest, EmptyHost) {
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair(std::string(), 5555), NetworkAnonymizationKey(),
@@ -1599,9 +1665,10 @@
   EXPECT_EQ(2u, proc_->GetCaptureList().size());
 }
 
-// Test that IP address changes flush the cache but initial DNS config reads
-// do not.
-TEST_F(HostResolverManagerTest, FlushCacheOnIPAddressChange) {
+void HostResolverManagerTest::FlushCacheOnIPAddressChangeTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   proc_->SignalMultiple(2u);  // One before the flush, one after.
 
   ResolveHostResponseHelper initial_response(resolver_->CreateRequest(
@@ -1629,13 +1696,27 @@
   EXPECT_EQ(2u, proc_->GetCaptureList().size());  // Expected increase.
 }
 
-// Test that IP address changes send ERR_NETWORK_CHANGED to pending requests.
-TEST_F(HostResolverManagerTest, AbortOnIPAddressChanged) {
+// Test that IP address changes flush the cache but initial DNS config reads
+// do not.
+TEST_F(HostResolverManagerTest, FlushCacheOnIPAddressChangeAsync) {
+  FlushCacheOnIPAddressChangeTest(true);
+}
+TEST_F(HostResolverManagerTest, FlushCacheOnIPAddressChangeSync) {
+  FlushCacheOnIPAddressChangeTest(false);
+}
+
+void HostResolverManagerTest::AbortOnIPAddressChangedTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair("host1", 70), NetworkAnonymizationKey(), NetLogWithSource(),
       absl::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
 
   ASSERT_FALSE(response.complete());
+  if (is_async) {
+    base::RunLoop().RunUntilIdle();
+  }
   ASSERT_TRUE(proc_->WaitFor(1u));
 
   // Triggering an IP address change.
@@ -1649,6 +1730,14 @@
   EXPECT_EQ(0u, resolve_context_->host_cache()->size());
 }
 
+// Test that IP address changes send ERR_NETWORK_CHANGED to pending requests.
+TEST_F(HostResolverManagerTest, AbortOnIPAddressChangedAsync) {
+  AbortOnIPAddressChangedTest(true);
+}
+TEST_F(HostResolverManagerTest, AbortOnIPAddressChangedSync) {
+  AbortOnIPAddressChangedTest(false);
+}
+
 // Obey pool constraints after IP address has changed.
 TEST_F(HostResolverManagerTest, ObeyPoolConstraintsAfterIPAddressChange) {
   // Runs at most one job at a time.
@@ -1694,9 +1783,11 @@
   EXPECT_THAT(responses[2]->result_error(), IsOk());
 }
 
-// Tests that a new Request made from the callback of a previously aborted one
-// will not be aborted.
-TEST_F(HostResolverManagerTest, AbortOnlyExistingRequestsOnIPAddressChange) {
+void HostResolverManagerTest::AbortOnlyExistingRequestsOnIPAddressChangeTest(
+    bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   auto custom_callback_template = base::BindLambdaForTesting(
       [&](const HostPortPair& next_host,
           std::unique_ptr<ResolveHostResponseHelper>* next_response,
@@ -1735,8 +1826,12 @@
       base::BindOnce(custom_callback_template, HostPortPair("eee", 80),
                      &next_responses[2]));
 
+  if (is_async) {
+    base::RunLoop().RunUntilIdle();
+  }
   // Wait until all are blocked;
   ASSERT_TRUE(proc_->WaitFor(3u));
+
   // Trigger an IP address change.
   NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
   // This should abort all running jobs.
@@ -1762,6 +1857,16 @@
   EXPECT_EQ(6u, proc_->GetCaptureList().size());
   EXPECT_EQ(3u, resolve_context_->host_cache()->size());
 }
+// Tests that a new Request made from the callback of a previously aborted one
+// will not be aborted.
+TEST_F(HostResolverManagerTest,
+       AbortOnlyExistingRequestsOnIPAddressChangeAsync) {
+  AbortOnlyExistingRequestsOnIPAddressChangeTest(true);
+}
+TEST_F(HostResolverManagerTest,
+       AbortOnlyExistingRequestsOnIPAddressChangeSync) {
+  AbortOnlyExistingRequestsOnIPAddressChangeTest(false);
+}
 
 // Tests that when the maximum threads is set to 1, requests are dequeued
 // in order of priority.
@@ -1848,9 +1953,9 @@
   EXPECT_EQ("req6", capture_list[6].hostname);
 }
 
-// Test that changing a job's priority affects the dequeueing order.
-TEST_F(HostResolverManagerTest, ChangePriority) {
-  CreateSerialResolver();
+void HostResolverManagerTest::ChangePriorityTest(bool is_async) {
+  CreateSerialResolver(true /* check_ipv6_on_wifi */, true /* ipv6_reachable */,
+                       is_async);
 
   HostResolver::ResolveHostParameters lowest_priority;
   lowest_priority.initial_priority = LOWEST;
@@ -1901,6 +2006,15 @@
   EXPECT_EQ("req1", capture_list[2].hostname);
 }
 
+// Test that changing a job's priority affects the dequeueing order.
+TEST_F(HostResolverManagerTest, ChangePriorityAsync) {
+  ChangePriorityTest(true);
+}
+
+TEST_F(HostResolverManagerTest, ChangePrioritySync) {
+  ChangePriorityTest(false);
+}
+
 // Try cancelling a job which has not started yet.
 TEST_F(HostResolverManagerTest, CancelPendingRequest) {
   CreateSerialResolver();
@@ -2280,7 +2394,10 @@
   EXPECT_FALSE(stale_request.request()->GetStaleInfo());
 }
 
-TEST_F(HostResolverManagerTest, LocalOnly_FromIp) {
+void HostResolverManagerTest::LocalOnlyFromIpTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   HostResolver::ResolveHostParameters source_none_parameters;
   source_none_parameters.source = HostResolverSource::LOCAL_ONLY;
 
@@ -2289,15 +2406,49 @@
       NetLogWithSource(), source_none_parameters, resolve_context_.get(),
       resolve_context_->host_cache()));
 
-  // Expected to resolve synchronously.
-  EXPECT_TRUE(response.complete());
-  EXPECT_THAT(response.result_error(), IsOk());
-  EXPECT_THAT(response.request()->GetAddressResults()->endpoints(),
-              testing::ElementsAre(CreateExpected("1.2.3.4", 56)));
-  EXPECT_THAT(response.request()->GetEndpointResults(),
-              testing::Pointee(testing::ElementsAre(ExpectEndpointResult(
-                  testing::ElementsAre(CreateExpected("1.2.3.4", 56))))));
-  EXPECT_FALSE(response.request()->GetStaleInfo());
+  // If IPv6 reachability is asynchronous, the first request will return
+  // NAME_NOT_RESOLVED. Do a second request to confirm that it returns OK once
+  // reachability check completes.
+  if (is_async) {
+    // Expected to resolve synchronously.
+    EXPECT_TRUE(response.complete());
+    EXPECT_EQ(response.result_error(), ERR_NAME_NOT_RESOLVED);
+    EXPECT_FALSE(response.request()->GetAddressResults());
+    EXPECT_FALSE(response.request()->GetEndpointResults());
+    EXPECT_FALSE(response.request()->GetStaleInfo());
+    base::RunLoop().RunUntilIdle();
+
+    ResolveHostResponseHelper response2(resolver_->CreateRequest(
+        HostPortPair("1.2.3.4", 56), NetworkAnonymizationKey(),
+        NetLogWithSource(), source_none_parameters, resolve_context_.get(),
+        resolve_context_->host_cache()));
+    EXPECT_TRUE(response2.complete());
+    EXPECT_THAT(response2.result_error(), IsOk());
+    EXPECT_THAT(response2.request()->GetAddressResults()->endpoints(),
+                testing::ElementsAre(CreateExpected("1.2.3.4", 56)));
+    EXPECT_THAT(response2.request()->GetEndpointResults(),
+                testing::Pointee(testing::ElementsAre(ExpectEndpointResult(
+                    testing::ElementsAre(CreateExpected("1.2.3.4", 56))))));
+    EXPECT_FALSE(response2.request()->GetStaleInfo());
+  } else {
+    // Expected to resolve synchronously.
+    EXPECT_TRUE(response.complete());
+    EXPECT_THAT(response.result_error(), IsOk());
+    EXPECT_THAT(response.request()->GetAddressResults()->endpoints(),
+                testing::ElementsAre(CreateExpected("1.2.3.4", 56)));
+    EXPECT_THAT(response.request()->GetEndpointResults(),
+                testing::Pointee(testing::ElementsAre(ExpectEndpointResult(
+                    testing::ElementsAre(CreateExpected("1.2.3.4", 56))))));
+    EXPECT_FALSE(response.request()->GetStaleInfo());
+  }
+}
+
+TEST_F(HostResolverManagerTest, LocalOnly_FromIpAsync) {
+  LocalOnlyFromIpTest(true);
+}
+
+TEST_F(HostResolverManagerTest, LocalOnly_FromIpSync) {
+  LocalOnlyFromIpTest(false);
 }
 
 TEST_F(HostResolverManagerTest, LocalOnly_InvalidName) {
@@ -2406,7 +2557,10 @@
   EXPECT_FALSE(response.request()->GetStaleInfo());
 }
 
-TEST_F(HostResolverManagerTest, StaleAllowed_FromIp) {
+void HostResolverManagerTest::StaleAllowedFromIpTest(bool is_async) {
+  CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
+                                    true /* ipv6_reachable */,
+                                    true /* check_ipv6_on_wifi */, is_async);
   HostResolver::ResolveHostParameters stale_allowed_parameters;
   stale_allowed_parameters.cache_usage =
       HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
@@ -2416,8 +2570,10 @@
       NetLogWithSource(), stale_allowed_parameters, resolve_context_.get(),
       resolve_context_->host_cache()));
 
-  // Expected to resolve synchronously without stale info.
-  EXPECT_TRUE(response.complete());
+  if (!is_async) {
+    // Expected to resolve synchronously without stale info.
+    EXPECT_TRUE(response.complete());
+  }
   EXPECT_THAT(response.result_error(), IsOk());
   EXPECT_THAT(response.request()->GetAddressResults()->endpoints(),
               testing::ElementsAre(CreateExpected("1.2.3.4", 57)));
@@ -2428,6 +2584,14 @@
   EXPECT_FALSE(response.request()->GetStaleInfo());
 }
 
+TEST_F(HostResolverManagerTest, StaleAllowed_FromIpAsync) {
+  StaleAllowedFromIpTest(true);
+}
+
+TEST_F(HostResolverManagerTest, StaleAllowed_FromIpSync) {
+  StaleAllowedFromIpTest(false);
+}
+
 // TODO(mgersh): add a test case for errors with positive TTL after
 // https://crbug.com/115051 is fixed.
 
@@ -2523,7 +2687,6 @@
   CreateResolverWithLimitsAndParams(kMaxJobs, params,
                                     false /* ipv6_reachable */,
                                     false /* check_ipv6_on_wifi */);
-
   // Resolve "host1". The resolver proc will hang all requests so this
   // resolution should remain stalled until calling SetResolvedAttemptNumber().
   ResolveHostResponseHelper response(resolver_->CreateRequest(
@@ -2627,22 +2790,27 @@
   EXPECT_THAT(similar_response3.result_error(), IsOk());
 }
 
-TEST_F(HostResolverManagerTest, IsIPv6Reachable) {
+TEST_F(HostResolverManagerTest, StartIPv6ReachabilityCheck) {
   // The real HostResolverManager is needed since TestHostResolverManager will
   // bypass the IPv6 reachability tests.
   DestroyResolver();
   resolver_ = std::make_unique<HostResolverManager>(
       DefaultOptions(), nullptr /* system_dns_config_notifier */,
       nullptr /* net_log */);
-
   // Verify that two consecutive calls return the same value.
   RecordingNetLogObserver net_log_observer;
   NetLogWithSource net_log =
       NetLogWithSource::Make(net::NetLog::Get(), NetLogSourceType::NONE);
-  bool result1 = IsIPv6Reachable(net_log);
-  bool result2 = IsIPv6Reachable(net_log);
-  EXPECT_EQ(result1, result2);
 
+  int attempt1 =
+      StartIPv6ReachabilityCheck(net_log, base::DoNothingAs<void(int)>());
+  EXPECT_EQ(attempt1, OK);
+  int result1 = GetLastIpv6ProbeResult();
+  int attempt2 =
+      StartIPv6ReachabilityCheck(net_log, base::DoNothingAs<void(int)>());
+  EXPECT_EQ(attempt2, OK);
+  int result2 = GetLastIpv6ProbeResult();
+  EXPECT_EQ(result1, result2);
   // Filter reachability check events and verify that there are two of them.
   auto probe_event_list = net_log_observer.GetEntriesWithType(
       NetLogEventType::HOST_RESOLVER_MANAGER_IPV6_REACHABILITY_CHECK);
@@ -4206,7 +4374,6 @@
       NetLogWithSource(), absl::nullopt, &resolve_context2,
       resolve_context2.host_cache()));
   EXPECT_FALSE(response2.complete());
-
   EXPECT_EQ(2u, resolver_->num_jobs_for_testing());
 
   // Wait for and complete the 2 over-the-wire DNS resolutions.
@@ -4254,6 +4421,9 @@
         notifier_task_runner_, std::move(config_service));
   }
 
+  void Ipv6UnreachableTest(bool is_async);
+  void Ipv6UnreachableInvalidConfigTest(bool is_async);
+
  protected:
   void TearDown() override {
     HostResolverManagerTest::TearDown();
@@ -4277,11 +4447,13 @@
       HostResolver::ManagerOptions options,
       const HostResolverSystemTask::Params& params,
       bool ipv6_reachable,
+      bool is_async = false,
       bool ipv4_reachable = true) override {
     DestroyResolver();
 
     resolver_ = std::make_unique<TestHostResolverManager>(
-        options, notifier_.get(), nullptr /* net_log */, ipv6_reachable);
+        options, notifier_.get(), nullptr /* net_log */, ipv6_reachable,
+        ipv4_reachable, is_async);
     auto dns_client =
         std::make_unique<MockDnsClient>(DnsConfig(), CreateDefaultDnsRules());
     dns_client_ = dns_client.get();
@@ -4290,7 +4462,6 @@
         options.insecure_dns_client_enabled,
         options.additional_types_via_insecure_dns_enabled);
     resolver_->set_host_resolver_system_params_for_test(params);
-
     resolver_->RegisterResolveContext(resolve_context_.get());
   }
 
@@ -4466,7 +4637,6 @@
 
   void ChangeDnsConfig(const DnsConfig& config) {
     DCHECK(config.IsValid());
-
     notifier_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&TestDnsConfigService::OnHostsRead,
@@ -5480,10 +5650,10 @@
   EXPECT_THAT(final_response.result_error(), IsOk());
 }
 
-TEST_F(HostResolverManagerDnsTest, Ipv6Unreachable) {
+void HostResolverManagerDnsTest::Ipv6UnreachableTest(bool is_async) {
   CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
                                     false /* ipv6_reachable */,
-                                    true /* check_ipv6_on_wifi */);
+                                    true /* check_ipv6_on_wifi */, is_async);
   ChangeDnsConfig(CreateValidDnsConfig());
 
   ResolveHostResponseHelper response(resolver_->CreateRequest(
@@ -5499,11 +5669,19 @@
                   testing::ElementsAre(CreateExpected("127.0.0.1", 500))))));
 }
 
-// Without a valid DnsConfig, assume IPv6 is needed and ignore prober.
-TEST_F(HostResolverManagerDnsTest, Ipv6Unreachable_InvalidConfig) {
+TEST_F(HostResolverManagerDnsTest, Ipv6UnreachableAsync) {
+  Ipv6UnreachableTest(true);
+}
+
+TEST_F(HostResolverManagerDnsTest, Ipv6UnreachableSync) {
+  Ipv6UnreachableTest(false);
+}
+
+void HostResolverManagerDnsTest::Ipv6UnreachableInvalidConfigTest(
+    bool is_async) {
   CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
                                     false /* ipv6_reachable */,
-                                    true /* check_ipv6_on_wifi */);
+                                    true /* check_ipv6_on_wifi */, is_async);
 
   proc_->AddRule("example.com", ADDRESS_FAMILY_UNSPECIFIED, "1.2.3.4,::5");
   proc_->SignalMultiple(1u);
@@ -5522,6 +5700,14 @@
           ExpectEndpointResult(testing::UnorderedElementsAre(
               CreateExpected("::5", 500), CreateExpected("1.2.3.4", 500))))));
 }
+// Without a valid DnsConfig, assume IPv6 is needed and ignore prober.
+TEST_F(HostResolverManagerDnsTest, Ipv6Unreachable_InvalidConfigAsync) {
+  Ipv6UnreachableInvalidConfigTest(true);
+}
+
+TEST_F(HostResolverManagerDnsTest, Ipv6Unreachable_InvalidConfigSync) {
+  Ipv6UnreachableInvalidConfigTest(false);
+}
 
 TEST_F(HostResolverManagerDnsTest, Ipv6Unreachable_UseLocalIpv6) {
   CreateResolverWithLimitsAndParams(kMaxJobs, DefaultParams(proc_),
@@ -13374,7 +13560,8 @@
                   ExpectEndpointResult(AddressesMatch(kRemoteAddrs)))));
 }
 
-TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetwork) {
+void HostResolverManagerTest::IPv4AddressLiteralInIPv6OnlyNetworkTest(
+    bool is_async) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       /*enabled_features=*/{features::kUseNAT64ForIPv4Literal},
@@ -13382,7 +13569,7 @@
 
   HostResolver::ManagerOptions options = DefaultOptions();
   CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
-                                     true /* ipv6_reachable */,
+                                     true /* ipv6_reachable */, is_async,
                                      false /* ipv4_reachable */);
   proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6,
                  "64:ff9b::c000:aa,64:ff9b::c000:ab,2001:db8:43::c000:aa,"
@@ -13417,7 +13604,16 @@
   EXPECT_TRUE(cache_result);
 }
 
-TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetworkPort443) {
+TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetworkAsync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkTest(true);
+}
+
+TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetworkSync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkTest(false);
+}
+
+void HostResolverManagerTest::IPv4AddressLiteralInIPv6OnlyNetworkPort443Test(
+    bool is_async) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       /*enabled_features=*/{features::kUseNAT64ForIPv4Literal},
@@ -13425,7 +13621,7 @@
 
   HostResolver::ManagerOptions options = DefaultOptions();
   CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
-                                     true /* ipv6_reachable */,
+                                     true /* ipv6_reachable */, is_async,
                                      false /* ipv4_reachable */);
   proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6,
                  "64:ff9b::c000:aa,64:ff9b::c000:ab,2001:db8:43::c000:aa,"
@@ -13460,7 +13656,18 @@
   EXPECT_TRUE(cache_result);
 }
 
-TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetworkNoDns64) {
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkPort443Async) {
+  IPv4AddressLiteralInIPv6OnlyNetworkPort443Test(true);
+}
+
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkPort443Sync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkPort443Test(false);
+}
+
+void HostResolverManagerTest::IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Test(
+    bool is_async) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       /*enabled_features=*/{features::kUseNAT64ForIPv4Literal},
@@ -13468,7 +13675,7 @@
 
   HostResolver::ManagerOptions options = DefaultOptions();
   CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
-                                     true /* ipv6_reachable */,
+                                     true /* ipv6_reachable */, is_async,
                                      false /* ipv4_reachable */);
   proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6, std::string());
   proc_->SignalMultiple(1u);
@@ -13488,9 +13695,18 @@
   EXPECT_FALSE(response.request()->GetStaleInfo());
 }
 
-// Test when DNS returns bad IPv6 address of ipv4only.arpa., and the
-// IPv4 address of ipv4only.arpa is not contained in the IPv6 address.
-TEST_F(HostResolverManagerTest, IPv4AddressLiteralInIPv6OnlyNetworkBadAddress) {
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Async) {
+  IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Test(true);
+}
+
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Sync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkNoDns64Test(false);
+}
+
+void HostResolverManagerTest::IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(
+    bool is_async) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       /*enabled_features=*/{features::kUseNAT64ForIPv4Literal},
@@ -13498,7 +13714,7 @@
 
   HostResolver::ManagerOptions options = DefaultOptions();
   CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
-                                     true /* ipv6_reachable */,
+                                     true /* ipv6_reachable */, is_async,
                                      false /* ipv4_reachable */);
   proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6, "2001:db8::1");
   proc_->SignalMultiple(1u);
@@ -13517,5 +13733,16 @@
                   testing::ElementsAre(CreateExpected("192.168.1.42", 80))))));
   EXPECT_FALSE(response.request()->GetStaleInfo());
 }
+// Test when DNS returns bad IPv6 address of ipv4only.arpa., and the
+// IPv4 address of ipv4only.arpa is not contained in the IPv6 address.
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkBadAddressAsync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(true);
+}
+
+TEST_F(HostResolverManagerTest,
+       IPv4AddressLiteralInIPv6OnlyNetworkBadAddressSync) {
+  IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(false);
+}
 
 }  // namespace net
diff --git a/net/dns/public/host_resolver_source.h b/net/dns/public/host_resolver_source.h
index de761d5d..220fcc6 100644
--- a/net/dns/public/host_resolver_source.h
+++ b/net/dns/public/host_resolver_source.h
@@ -29,7 +29,9 @@
   // No external sources will be used. Results will only come from fast local
   // sources that are available no matter the source setting, e.g. cache, hosts
   // file, IP literal resolution, etc. Resolves with this setting are guaranteed
-  // to finish synchronously.
+  // to finish synchronously. Resolves with this settings will return
+  // ERR_NAME_NOT_RESOLVED if an asynchronous IPv6 reachability probe needs to
+  // be done.
   LOCAL_ONLY,
 
   MAX = LOCAL_ONLY
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 23174a2..193a7c2 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -7,6 +7,7 @@
 import("//build/util/process_version.gni")
 import("//remoting/build/config/remoting_build.gni")
 import("//remoting/build/config/remoting_logging.gni")
+import("//third_party/webrtc/webrtc.gni")
 
 assert(!remoting_use_x11 || is_linux, "X11 is only supported on Linux")
 
@@ -555,10 +556,7 @@
       "x11_display_util.cc",
       "x11_display_util.h",
     ]
-    configs += [
-      "//third_party/webrtc/modules/portal:gio",
-      ":libevdev",
-    ]
+    configs += [ ":libevdev" ]
     libs += [ "//third_party/libei/lib64/libei.a" ]
     public_deps += [
       "//third_party/wayland:wayland_client",
@@ -594,6 +592,11 @@
         "linux/keyboard_layout_monitor_wayland.h",
       ]
     }
+
+    # webRTC only enables portal support if PipeWire support is enabled
+    if (rtc_use_pipewire) {
+      configs += [ "//third_party/webrtc/modules/portal:gio" ]
+    }
   }
 
   if (is_chromeos_ash) {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index df83dda..8ddcd16f 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5615,9 +5615,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5628,8 +5628,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -5780,9 +5780,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5793,8 +5793,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -5927,9 +5927,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5940,8 +5940,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 30342b6..ecb5b34 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -25212,9 +25212,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -25225,8 +25225,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -25377,9 +25377,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -25390,8 +25390,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -25524,9 +25524,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -25537,8 +25537,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 04e55aec..4c3bbe58 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -33839,9 +33839,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -33851,8 +33851,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -34004,9 +34004,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -34016,8 +34016,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -34151,9 +34151,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -34163,8 +34163,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -35591,9 +35591,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -35603,8 +35603,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -35756,9 +35756,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -35768,8 +35768,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -35903,9 +35903,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -35915,8 +35915,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -36632,9 +36632,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -36644,8 +36644,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 2700772..2061e560 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -17723,12 +17723,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -17739,8 +17739,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -17908,12 +17908,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -17924,8 +17924,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
@@ -18070,12 +18070,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5705.0",
+        "description": "Run with ash-chrome version 114.0.5707.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -18086,8 +18086,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5705.0",
-              "revision": "version:114.0.5705.0"
+              "location": "lacros_version_skew_tests_v114.0.5707.0",
+              "revision": "version:114.0.5707.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 14c2ad51..87d8aac 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5705.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5707.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 114.0.5705.0',
+    'description': 'Run with ash-chrome version 114.0.5707.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v114.0.5705.0',
-          'revision': 'version:114.0.5705.0',
+          'location': 'lacros_version_skew_tests_v114.0.5707.0',
+          'revision': 'version:114.0.5707.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ecbed0f..64d78683 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -131,6 +131,21 @@
             ]
         }
     ],
+    "AndroidAnimatedImageDragShadow": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AnimatedImageDragShadow"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidBackPressRefactor": [
         {
             "platforms": [
@@ -4057,26 +4072,6 @@
             ]
         }
     ],
-    "DesktopPartialTranslateGWSVisible": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20221129",
-                    "params": {
-                        "DesktopPartialTranslateTextSelectionMaxCharacters": "500"
-                    },
-                    "enable_features": [
-                        "DesktopPartialTranslate"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopReadingListAddFromDialog": [
         {
             "platforms": [
@@ -10708,6 +10703,26 @@
             ]
         }
     ],
+    "ReadAnything": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ReadAnything",
+                        "ReadAnythingWithScreen2x"
+                    ]
+                }
+            ]
+        }
+    ],
     "ReadLaterReminderNotification": [
         {
             "platforms": [
@@ -15041,5 +15056,20 @@
                 }
             ]
         }
+    ],
+    "ZeroCopyTabCaptureStudyWin": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20230222",
+                    "enable_features": [
+                        "ZeroCopyTabCapture"
+                    ]
+                }
+            ]
+        }
     ]
 }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 54d75ba..050b793 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -306,6 +306,15 @@
 constexpr base::FeatureParam<int> kPrivateAggregationApiMaxBudgetPerScope{
     &kPrivateAggregationApi, "max_budget_per_scope", /*default_value=*/65536};
 
+// Has the same effect as enabling
+// kPrivateAggregationApiFledgeExtensionsEnabled. This is intended as a
+// convenience for local testing only.
+// TODO(alexmt): Remove when kPrivateAggregationApiFledgeExtensionsEnabled is
+// enabled by default.
+BASE_FEATURE(kPrivateAggregationApiFledgeExtensionsLocalTestingOverride,
+             "PrivateAggregationApiFledgeExtensionsLocalTestingOverride",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enable the shared storage API. Note that enabling this feature does not
 // automatically expose this API to the web, it only allows the element to be
 // enabled by the runtime enabled feature, for origin trials.
diff --git a/third_party/blink/common/storage_key/storage_key.cc b/third_party/blink/common/storage_key/storage_key.cc
index 964476f..da0925a 100644
--- a/third_party/blink/common/storage_key/storage_key.cc
+++ b/third_party/blink/common/storage_key/storage_key.cc
@@ -234,15 +234,17 @@
 
       // The ancestor_chain_bit is the portion beyond the first separator.
       int raw_bit;
-      if (!base::StringToInt(in.substr(pos_first_caret + 2, std::string::npos),
-                             &raw_bit)) {
+      const base::StringPiece raw_bit_substr =
+          in.substr(pos_first_caret + 2, std::string::npos);
+      if (!base::StringToInt(raw_bit_substr, &raw_bit)) {
         return absl::nullopt;
       }
 
       // If the integer conversion results in a value outside the enumerated
-      // indices of [0,1]
-      if (raw_bit < 0 || raw_bit > 1)
+      // indices of [0,1] or trimmed leading 0s we must reject the key.
+      if (raw_bit < 0 || raw_bit > 1 || raw_bit_substr.size() > 1) {
         return absl::nullopt;
+      }
       ancestor_chain_bit = static_cast<blink::mojom::AncestorChainBit>(raw_bit);
 
       // There is no nonce or top level site.
diff --git a/third_party/blink/common/storage_key/storage_key_corpus/15 b/third_party/blink/common/storage_key/storage_key_corpus/15
new file mode 100644
index 0000000..9a69eb2
--- /dev/null
+++ b/third_party/blink/common/storage_key/storage_key_corpus/15
@@ -0,0 +1 @@
+file:///^301
\ No newline at end of file
diff --git a/third_party/blink/common/storage_key/storage_key_unittest.cc b/third_party/blink/common/storage_key/storage_key_unittest.cc
index 2539892..b4d9fdf 100644
--- a/third_party/blink/common/storage_key/storage_key_unittest.cc
+++ b/third_party/blink/common/storage_key/storage_key_unittest.cc
@@ -708,6 +708,11 @@
             "https://example.com/^30",
             absl::nullopt,
         },
+        // An origin cannot be serialized with a malformed CrossSite bit.
+        {
+            "https://example.com/^301",
+            absl::nullopt,
+        },
         // An origin can be serialized with a CrossSite bit.
         {
             "https://example.com/^31",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 4114685..3b1532f4 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -89,6 +89,9 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kPrivateAggregationApiMaxBudgetPerScope;
 
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
+    kPrivateAggregationApiFledgeExtensionsLocalTestingOverride);
+
 enum class SharedStorageWorkletImplementationType {
   // The worklet thread is created via base::SequenceBound, and JS bindings are
   // added with native v8 and/or Gin library.
diff --git a/third_party/blink/public/web/web_ax_context.h b/third_party/blink/public/web/web_ax_context.h
index 674c6d7..2484d27 100644
--- a/third_party/blink/public/web/web_ax_context.h
+++ b/third_party/blink/public/web/web_ax_context.h
@@ -65,8 +65,7 @@
 
   void Thaw();
 
-  bool SerializeEntireTree(bool exclude_offscreen,
-                           size_t max_node_count,
+  bool SerializeEntireTree(size_t max_node_count,
                            base::TimeDelta timeout,
                            ui::AXTreeUpdate* response);
 
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 172ba978a..bcd6a715 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -191,6 +191,16 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_prf_outputs.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_prf_values.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_prf_values.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_registration_response_js_on.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_registration_response_js_on.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_response_js_on.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_response_js_on.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_assertion_response_js_on.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_assertion_response_js_on.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_attestation_response_js_on.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_attestation_response_js_on.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_client_outputs_js_on.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_client_outputs_js_on.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_selection_criteria.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_authenticator_selection_criteria.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_avc_encoder_config.cc",
@@ -989,6 +999,10 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage_set_method_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage_url_with_metadata.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage_url_with_metadata.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_debug_mode_options.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_debug_mode_options.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_histogram_contribution.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_histogram_contribution.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection_status.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection_status.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_error_options.cc",
@@ -2327,6 +2341,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_pressure_observer.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_pressure_record.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_pressure_record.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_push_event.cc",
@@ -2824,6 +2840,8 @@
 generated_union_sources_in_modules = [
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_abortsignal_schedulersignalinherit.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_abortsignal_schedulersignalinherit.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_authenticationresponsejson_registrationresponsejson.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_authenticationresponsejson_registrationresponsejson.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_adproperties_adpropertiessequence.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_adproperties_adpropertiessequence.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_arraybuffer_arraybufferview_blob_usvstring_writeparams.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 753bdaf..35d8b43 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -178,6 +178,11 @@
           "//third_party/blink/renderer/modules/credentialmanagement/password_credential_data.idl",
           "//third_party/blink/renderer/modules/credentialmanagement/payment_credential_instrument.idl",
           "//third_party/blink/renderer/modules/credentialmanagement/public_key_credential.idl",
+          "//third_party/blink/renderer/modules/credentialmanagement/registration_response_json.idl",
+          "//third_party/blink/renderer/modules/credentialmanagement/authentication_response_json.idl",
+          "//third_party/blink/renderer/modules/credentialmanagement/authenticator_assertion_response_json.idl",
+          "//third_party/blink/renderer/modules/credentialmanagement/authenticator_attestation_response_json.idl",
+          "//third_party/blink/renderer/modules/credentialmanagement/authentication_extensions_client_outputs_json.idl",
           "//third_party/blink/renderer/modules/credentialmanagement/public_key_credential_creation_options.idl",
           "//third_party/blink/renderer/modules/credentialmanagement/public_key_credential_descriptor.idl",
           "//third_party/blink/renderer/modules/credentialmanagement/public_key_credential_entity.idl",
@@ -710,13 +715,16 @@
           "//third_party/blink/renderer/modules/shapedetection/face_detector_options.idl",
           "//third_party/blink/renderer/modules/shapedetection/landmark.idl",
           "//third_party/blink/renderer/modules/shapedetection/text_detector.idl",
+          "//third_party/blink/renderer/modules/shared_storage/private_aggregation.idl",
+          "//third_party/blink/renderer/modules/shared_storage/private_aggregation_histogram_contribution.idl",
+          "//third_party/blink/renderer/modules/shared_storage/private_aggregation_debug_mode_options.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage_run_operation_method_options.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage_set_method_options.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage_url_with_metadata.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.idl",
-          "//third_party/blink/renderer/modules/shared_storage/window_shared_storage.idl",
           "//third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.idl",
+          "//third_party/blink/renderer/modules/shared_storage/window_shared_storage.idl",
           "//third_party/blink/renderer/modules/smart_card/navigator_smart_card.idl",
           "//third_party/blink/renderer/modules/smart_card/smart_card_connection.idl",
           "//third_party/blink/renderer/modules/smart_card/smart_card_connection_status.idl",
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
index f4a6dbc..cb1334f3 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -218,8 +218,7 @@
   virtual AXObject* GetPluginRoot() = 0;
 
   // Serialize entire tree, returning true if successful.
-  virtual bool SerializeEntireTree(bool exclude_offscreen,
-                                   size_t max_node_count,
+  virtual bool SerializeEntireTree(size_t max_node_count,
                                    base::TimeDelta timeout,
                                    ui::AXTreeUpdate*) = 0;
 
diff --git a/third_party/blink/renderer/core/context_features/context_feature_settings.h b/third_party/blink/renderer/core/context_features/context_feature_settings.h
index b0d6503..7cb0440 100644
--- a/third_party/blink/renderer/core/context_features/context_feature_settings.h
+++ b/third_party/blink/renderer/core/context_features/context_feature_settings.h
@@ -43,11 +43,20 @@
     return enable_mojo_js_file_system_access_helper_;
   }
 
+  // ContextEnabled=PrivateAggregationInSharedStorage
+  void EnablePrivateAggregationInSharedStorage(bool enable) {
+    enable_private_aggregation_in_shared_storage_ = enable;
+  }
+  bool isPrivateAggregationInSharedStorageEnabled() const {
+    return enable_private_aggregation_in_shared_storage_;
+  }
+
   void Trace(Visitor*) const override;
 
  private:
   bool enable_mojo_js_ = false;
   bool enable_mojo_js_file_system_access_helper_ = false;
+  bool enable_private_aggregation_in_shared_storage_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/abort_signal.cc b/third_party/blink/renderer/core/dom/abort_signal.cc
index 10ed36c..fd53f6bd 100644
--- a/third_party/blink/renderer/core/dom/abort_signal.cc
+++ b/third_party/blink/renderer/core/dom/abort_signal.cc
@@ -434,7 +434,9 @@
   }
   DCHECK(RuntimeEnabledFeatures::AbortSignalCompositionEnabled());
   // Settled signals cannot signal abort, so they can be GCed.
-  if (composition_manager_->IsSettled()) {
+  // `composition_manager_` will be null if this is called before the object is
+  // fully constructed.
+  if (!composition_manager_ || composition_manager_->IsSettled()) {
     return false;
   }
   // Otherwise the signal needs to be kept alive if aborting can be observed.
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni
index df4af74..665bc9b9 100644
--- a/third_party/blink/renderer/core/dom/build.gni
+++ b/third_party/blink/renderer/core/dom/build.gni
@@ -68,6 +68,8 @@
   "css_toggle_event.h",
   "css_toggle_inference.cc",
   "css_toggle_inference.h",
+  "css_toggle_key_handling.cc",
+  "css_toggle_key_handling.h",
   "css_toggle_map.cc",
   "css_toggle_map.h",
   "css_toggle_traversal.h",
diff --git a/third_party/blink/renderer/core/dom/css_toggle.cc b/third_party/blink/renderer/core/dom/css_toggle.cc
index 6b2819cd..39c80086 100644
--- a/third_party/blink/renderer/core/dom/css_toggle.cc
+++ b/third_party/blink/renderer/core/dom/css_toggle.cc
@@ -437,12 +437,12 @@
   }
 }
 
-void CSSToggle::FireToggleActivation(Element& activated_element,
+bool CSSToggle::FireToggleActivation(Element& activated_element,
                                      const ToggleTrigger& activation) {
   const AtomicString& name = activation.Name();
   CSSToggle* toggle = FindToggleInScope(activated_element, name);
   if (!toggle)
-    return;
+    return false;
 
   CSSToggle::State old_value = toggle->Value();
   toggle->ChangeToggle(activation, toggle->FindToggleSpecifier());
@@ -450,6 +450,8 @@
 
   if (old_value != new_value)
     toggle->FireToggleChangeEvent();
+
+  return true;
 }
 
 // Implement https://tabatkins.github.io/css-toggle/#change-a-toggle
diff --git a/third_party/blink/renderer/core/dom/css_toggle.h b/third_party/blink/renderer/core/dom/css_toggle.h
index 60c7d51e..eba2414 100644
--- a/third_party/blink/renderer/core/dom/css_toggle.h
+++ b/third_party/blink/renderer/core/dom/css_toggle.h
@@ -49,7 +49,7 @@
                                       const AtomicString& name);
 
   // Implement https://tabatkins.github.io/css-toggle/#fire-a-toggle-activation
-  static void FireToggleActivation(Element& activated_element,
+  static bool FireToggleActivation(Element& activated_element,
                                    const ToggleTrigger& activation);
 
   void ChangeToggle(const ToggleTrigger& action,
diff --git a/third_party/blink/renderer/core/dom/css_toggle_inference.cc b/third_party/blink/renderer/core/dom/css_toggle_inference.cc
index 82cc8641..ea0944d 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_inference.cc
+++ b/third_party/blink/renderer/core/dom/css_toggle_inference.cc
@@ -22,7 +22,7 @@
 
 void CSSToggleInference::Trace(Visitor* visitor) const {
   visitor->Trace(document_);
-  visitor->Trace(element_roles_);
+  visitor->Trace(element_data_);
 }
 
 void CSSToggleInference::RebuildIfNeeded() {
@@ -108,8 +108,8 @@
         }
       }
     }
-    // TODO(dbaron): Should we have a condition that the number of tabs
-    // and panels are similar or equal?
+    // TODO(https://crbug.com/1250716): Should we have a condition that
+    // the number of tabs and panels are similar or equal?
     if (child_tab_count > 0 && child_panel_count > 0 &&
         (child_tab_count + child_panel_count) * 2 > child_count) {
       return toggle_group.Name();
@@ -215,16 +215,16 @@
 void CSSToggleInference::Rebuild() {
   DCHECK(!needs_rebuild_) << "Rebuild should be called via RebuildIfNeeded";
 
-  element_roles_.clear();
+  element_data_.clear();
 
   const HeapHashSet<WeakMember<Element>> elements_with_toggles =
       document_->ElementsWithCSSToggles();
 
-  // TODO(dbaron): There are various things here that count siblings and
-  // have thresholds at 50%+1.  We don't currently invalidate (i.e.,
-  // set needs_rebuild_) for many of the cases that could change the
-  // result of these checks, since we only invalidate in response to
-  // changes on things that have toggles.
+  // TODO(https://crbug.com/1250716): There are various things here that
+  // count siblings and have thresholds at 50%+1.  We don't currently
+  // invalidate (i.e., set needs_rebuild_) for many of the cases that
+  // could change the result of these checks, since we only invalidate
+  // in response to changes on things that have toggles.
   //
   // In fact, there are probably other conditions that we don't properly
   // invalidate for as well.  Before this code is every used for
@@ -294,10 +294,12 @@
     Element* parent = toggle_root->parentElement();
     const auto disclosure_ish_iter = disclosure_ish_elements.find(toggle_root);
     if (disclosure_ish_iter != disclosure_ish_elements.end()) {
+      const AtomicString& toggle_name = disclosure_ish_iter->value;
       if (parent && accordion_ish_elements.Contains(parent)) {
         CSSToggleRole parent_role;
         {  // scope for lifetime of add_result
-          auto add_result = element_roles_.insert(parent, CSSToggleRole::kNone);
+          auto add_result = element_data_.insert(
+              parent, ElementData{CSSToggleRole::kNone, g_null_atom});
           // We might have already handled parent as the parent of a
           // different one of its children.
           if (add_result.is_new_entry) {
@@ -316,20 +318,20 @@
             for (Element& child : ElementTraversal::ChildrenOf(*parent)) {
               if (disclosure_ish_elements.Contains(&child)) {
                 DCHECK(child.GetToggleMap());
-                for (const auto& [toggle_name, toggle] :
+                for (const auto& [child_toggle_name, toggle] :
                      child.GetToggleMap()->Toggles()) {
-                  for (Element* e : CSSToggleScopeRange(&child, toggle_name,
-                                                        toggle->Scope())) {
+                  for (Element* e : CSSToggleScopeRange(
+                           &child, child_toggle_name, toggle->Scope())) {
                     const ComputedStyle* e_style = e->GetComputedStyle();
                     if (!e_style) {
                       continue;
                     }
-                    if (e_style->ToggleVisibility() == toggle_name) {
+                    if (e_style->ToggleVisibility() == child_toggle_name) {
                       ++panel_count;
                       if (toggle->IsGroup()) {
                         auto count_add_result =
-                            panel_with_toggle_group_counts.insert(toggle_name,
-                                                                  0);
+                            panel_with_toggle_group_counts.insert(
+                                child_toggle_name, 0);
                         DCHECK(count_add_result.is_new_entry ==
                                (count_add_result.stored_value->value == 0));
                         ++count_add_result.stored_value->value;
@@ -347,6 +349,7 @@
             bool enough_accordions =
                 accordion_ish_panel_count * 2 > panel_count;
             bool enough_toggle_groups = false;
+            AtomicString toggle_group_name = g_null_atom;
             if (const ComputedStyle* parent_style =
                     parent->GetComputedStyle()) {
               if (const ToggleGroupList* parent_groups =
@@ -356,6 +359,7 @@
                   if (iter != panel_with_toggle_group_counts.end()) {
                     if (iter->value * 2 > panel_count) {
                       enough_toggle_groups = true;
+                      toggle_group_name = group.Name();
                     }
                   }
                 }
@@ -364,37 +368,49 @@
             if (enough_accordions ^ enough_toggle_groups) {
               if (enough_accordions) {
                 DCHECK(!enough_toggle_groups);
-                add_result.stored_value->value = CSSToggleRole::kTree;
+                add_result.stored_value->value =
+                    ElementData{CSSToggleRole::kTree, g_null_atom};
               } else {
                 DCHECK(enough_toggle_groups);
-                add_result.stored_value->value = CSSToggleRole::kAccordion;
+                DCHECK_NE(toggle_group_name, g_null_atom);
+                add_result.stored_value->value =
+                    ElementData{CSSToggleRole::kAccordion, toggle_group_name};
               }
             } else {
-              // TODO(dbaron): For now, we don't know what this is, but
-              // maybe we could do better.
-              add_result.stored_value->value = CSSToggleRole::kNone;
+              // TODO(https://crbug.com/1250716): For now, we don't know
+              // what this is, but maybe we could do better.
+              add_result.stored_value->value =
+                  ElementData{CSSToggleRole::kNone, g_null_atom};
             }
           }
-          parent_role = add_result.stored_value->value;
+          parent_role = add_result.stored_value->value.role;
         }
         switch (parent_role) {
           case CSSToggleRole::kTree:
           case CSSToggleRole::kTreeGroup: {
-            // TODO(dbaron): This role assignment is likely wrong!  Need to
-            // examine closely!
+            // TODO(https://crbug.com/1250716): This role assignment is
+            // likely wrong!  Need to examine closely!
             Element* trigger_element =
                 disclosure_ish_element_triggers.at(toggle_root);
-            element_roles_.insert(toggle_root, CSSToggleRole::kTreeGroup);
-            // TODO(dbaron): What if the trigger is just part of the tree item?
-            element_roles_.insert(trigger_element, CSSToggleRole::kTreeItem);
+            element_data_.insert(
+                toggle_root,
+                ElementData{CSSToggleRole::kTreeGroup, toggle_name});
+            // TODO(https://crbug.com/1250716): What if the trigger is
+            // just part of the tree item?
+            element_data_.insert(
+                trigger_element,
+                ElementData{CSSToggleRole::kTreeItem, toggle_name});
             break;
           }
           case CSSToggleRole::kAccordion: {
             Element* trigger_element =
                 disclosure_ish_element_triggers.at(toggle_root);
-            element_roles_.insert(toggle_root, CSSToggleRole::kAccordionItem);
-            element_roles_.insert(trigger_element,
-                                  CSSToggleRole::kAccordionItemButton);
+            element_data_.insert(
+                toggle_root,
+                ElementData{CSSToggleRole::kAccordionItem, toggle_name});
+            element_data_.insert(
+                trigger_element,
+                ElementData{CSSToggleRole::kAccordionItemButton, toggle_name});
             break;
           }
           case CSSToggleRole::kNone:
@@ -403,12 +419,11 @@
           default:
             // This could happen if some other part of the assignment
             // logic touches it.
-            // TODO(dbaron): Do we want to do this?
+            // TODO(https://crbug.com/1250716): Do we want to do this?
             find_trigger_and_make_it_a_button = true;
             break;
         }
       } else {
-        const AtomicString& toggle_name = disclosure_ish_iter->value;
         DCHECK(toggle_root->GetToggleMap());
         CSSToggle* toggle =
             toggle_root->GetToggleMap()->Toggles().at(toggle_name);
@@ -433,12 +448,16 @@
               Element* trigger_element =
                   disclosure_ish_element_triggers.at(toggle_root);
               if (positioned_or_popover) {
-                element_roles_.insert(trigger_element,
-                                      CSSToggleRole::kButtonWithPopup);
+                element_data_.insert(
+                    trigger_element,
+                    ElementData{CSSToggleRole::kButtonWithPopup, toggle_name});
               } else {
-                element_roles_.insert(toggle_root, CSSToggleRole::kDisclosure);
-                element_roles_.insert(trigger_element,
-                                      CSSToggleRole::kDisclosureButton);
+                element_data_.insert(
+                    toggle_root,
+                    ElementData{CSSToggleRole::kDisclosure, toggle_name});
+                element_data_.insert(
+                    trigger_element,
+                    ElementData{CSSToggleRole::kDisclosureButton, toggle_name});
               }
               break;
             }
@@ -465,15 +484,18 @@
                  DCHECK_EQ(toggle->Name(), toggle_name);
                  return toggle;
                }()) {
-      element_roles_.insert(parent, CSSToggleRole::kTabContainer);
-      element_roles_.insert(toggle_root, CSSToggleRole::kTab);
+      element_data_.insert(parent, ElementData{CSSToggleRole::kTabContainer,
+                                               tabs_ish_toggle->Name()});
+      element_data_.insert(toggle_root, ElementData{CSSToggleRole::kTab,
+                                                    tabs_ish_toggle->Name()});
 
       for (Element* e :
            CSSToggleScopeRange(toggle_root, tabs_ish_toggle->Name(),
                                tabs_ish_toggle->Scope())) {
         const ComputedStyle* e_style = e->GetComputedStyle();
         if (e_style && e_style->ToggleVisibility() == tabs_ish_toggle->Name()) {
-          element_roles_.insert(e, CSSToggleRole::kTabPanel);
+          element_data_.insert(e, ElementData{CSSToggleRole::kTabPanel,
+                                              tabs_ish_toggle->Name()});
         }
       }
     } else {
@@ -511,17 +533,36 @@
 
         CSSToggleRole parent_role = CSSToggleRole::kNone;
         if (found_visibility) {
-          // TODO(dbaron): The current spec draft says that this should
-          // be "no pattern detected", but it seems bad to fail to
-          // report something.  I'm going to report button instead.
-          element_roles_.insert(toggle_root, CSSToggleRole::kButton);
+          // TODO(https://crbug.com/1250716): The current spec draft
+          // says that this should be "no pattern detected", but it
+          // seems bad to fail to report something.  I'm going to report
+          // button instead.
+          element_data_.insert(
+              toggle_root, ElementData{CSSToggleRole::kButton, toggle_name});
         } else if (toggle_with_trigger->IsGroup()) {
-          element_roles_.insert(toggle_root, CSSToggleRole::kRadioItem);
+          // TODO(https://crbug.com/1250716): Detecting this pattern as
+          // radio item and radio group is not quite right since the
+          // underlying toggle behavior here doesn't match the normal
+          // interaction of radios.  In particular, this toggle pattern
+          // allows users to unselect a selected radio item by clicking
+          // on it.  The ability to do this leads to a keyboard
+          // interaction behavior that also doesn't really match the
+          // normal interaction for radios (where focus and selection
+          // are the same whenever focused, with the sole exception of
+          // focusing into a radio group with no current selection).
+          // It's not entirely clear if we should change the detection
+          // behavior here or change the keyboard interactions to match
+          // normal radio behavior.  (Changing the keyboard interactions
+          // has the disadvantage that it would then leave keyboard
+          // users without the ability to do something that remains
+          // possible with the mouse.)
+          element_data_.insert(
+              toggle_root, ElementData{CSSToggleRole::kRadioItem, toggle_name});
           if (const ComputedStyle* parent_style = parent->GetComputedStyle()) {
             if (const ToggleGroupList* parent_groups =
                     parent_style->ToggleGroup()) {
               for (const ToggleGroup& group : parent_groups->Groups()) {
-                if (toggle_with_trigger->Name() == group.Name()) {
+                if (toggle_name == group.Name()) {
                   parent_role = CSSToggleRole::kRadioGroup;
                   break;
                 }
@@ -529,30 +570,33 @@
             }
           }
         } else {
-          // TODO(dbaron): Maybe this should be Switch, depending on
-          // device.
-          element_roles_.insert(toggle_root, CSSToggleRole::kCheckbox);
-          // TODO(dbaron): We should only set parent_role here when
-          // there are multiple checkbox siblings with few non-checkbox
-          // siblings!  (Once we do this we can probably remove the
-          // tabs_ish_elements.Contains(parent) test below.)
+          // TODO(https://crbug.com/1250716): Maybe this should be
+          // Switch, depending on device.
+          element_data_.insert(
+              toggle_root, ElementData{CSSToggleRole::kCheckbox, toggle_name});
+          // TODO(https://crbug.com/1250716): We should only set
+          // parent_role here when there are multiple checkbox siblings
+          // with few non-checkbox siblings!  (Once we do this we can
+          // probably remove the tabs_ish_elements.Contains(parent) test
+          // below.)
           parent_role = CSSToggleRole::kCheckboxGroup;
         }
 
-        // TODO(dbaron): Figure out if we need to exclude additional
-        // cases where the parent would be assigned a different role (in
-        // order to ensure that hash map processing order doesn't affect
-        // the result).
+        // TODO(https://crbug.com/1250716): Figure out if we need to
+        // exclude additional cases where the parent would be assigned a
+        // different role (in order to ensure that hash map processing
+        // order doesn't affect the result).
         if (parent_role != CSSToggleRole::kNone && parent &&
             !accordion_ish_elements.Contains(parent) &&
             !tabs_ish_elements.Contains(parent)) {
-          auto parent_add_result = element_roles_.insert(parent, parent_role);
+          ElementData data{parent_role, toggle_name};
+          auto parent_add_result = element_data_.insert(parent, data);
           // prefer checkbox group to radio group if some children
           // lead to either
-          if (parent_add_result.stored_value->value != parent_role &&
-              parent_add_result.stored_value->value ==
+          if (parent_add_result.stored_value->value.role != parent_role &&
+              parent_add_result.stored_value->value.role ==
                   CSSToggleRole::kRadioGroup) {
-            parent_add_result.stored_value->value = parent_role;
+            parent_add_result.stored_value->value = data;
           }
         }
       } else {
@@ -570,7 +614,8 @@
             if (const ToggleTriggerList* triggers = e_style->ToggleTrigger()) {
               for (const ToggleTrigger& trigger : triggers->Triggers()) {
                 if (trigger.Name() == toggle_name) {
-                  element_roles_.insert(e, CSSToggleRole::kButton);
+                  element_data_.insert(
+                      e, ElementData{CSSToggleRole::kButton, toggle_name});
                   break;
                 }
               }
@@ -582,15 +627,28 @@
   }
 }
 
-CSSToggleRole CSSToggleInference::RoleForElement(blink::Element* element) {
+CSSToggleRole CSSToggleInference::RoleForElement(
+    const blink::Element* element) {
   RebuildIfNeeded();
 
-  auto iter = element_roles_.find(element);
-  if (iter == element_roles_.end()) {
+  auto iter = element_data_.find(element);
+  if (iter == element_data_.end()) {
     return CSSToggleRole::kNone;
   }
 
-  return iter->value;
+  return iter->value.role;
+}
+
+AtomicString CSSToggleInference::ToggleNameForElement(
+    const blink::Element* element) {
+  RebuildIfNeeded();
+
+  auto iter = element_data_.find(element);
+  if (iter == element_data_.end()) {
+    return g_null_atom;
+  }
+
+  return iter->value.toggle_name;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/css_toggle_inference.h b/third_party/blink/renderer/core/dom/css_toggle_inference.h
index ac679a31..f3e05f64 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_inference.h
+++ b/third_party/blink/renderer/core/dom/css_toggle_inference.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -84,19 +85,31 @@
   //
   // This role information is somewhat expensive to rebuild, and is
   // information that does *not* change when toggle state changes.
-  CSSToggleRole RoleForElement(blink::Element* element);
+  CSSToggleRole RoleForElement(const blink::Element* element);
 
-  // TODO(dbaron): Add a separate API here for property (e.g., state)
-  // information that *does* sometimes change when toggle state is
-  // changed.
+  // Return the toggle name associated with an element's role.
+  //
+  // ToggleNameForElement should return g_null_atom in exactly the same
+  // cases that RoleForElement returns CSSToggleRole::kNone or
+  // CSSToggleRole::kTree.
+  AtomicString ToggleNameForElement(const blink::Element* element);
+
+  // TODO(https://crbug.com/1250716): Add a separate API here for
+  // property (e.g., state) information that *does* sometimes change
+  // when toggle state is changed.
 
  private:
   void RebuildIfNeeded();
   void Rebuild();
 
+  struct ElementData {
+    CSSToggleRole role;
+    AtomicString toggle_name;
+  };
+
   bool needs_rebuild_ = true;
   Member<Document> document_;
-  HeapHashMap<Member<Element>, CSSToggleRole> element_roles_;
+  HeapHashMap<Member<const Element>, ElementData> element_data_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/css_toggle_key_handling.cc b/third_party/blink/renderer/core/dom/css_toggle_key_handling.cc
new file mode 100644
index 0000000..f966c1a8
--- /dev/null
+++ b/third_party/blink/renderer/core/dom/css_toggle_key_handling.cc
@@ -0,0 +1,199 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/dom/css_toggle_key_handling.h"
+
+#include "third_party/blink/renderer/core/dom/css_toggle.h"
+#include "third_party/blink/renderer/core/dom/css_toggle_inference.h"
+#include "third_party/blink/renderer/core/dom/css_toggle_map.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/toggle_trigger.h"
+#include "third_party/blink/renderer/core/style/toggle_trigger_list.h"
+
+namespace blink {
+
+namespace {
+
+const ToggleTrigger* FindToggleTrigger(Element* element,
+                                       const AtomicString& toggle_name) {
+  const ComputedStyle* style = element->GetComputedStyle();
+  if (!style) {
+    return nullptr;
+  }
+  const ToggleTriggerList* trigger_list = style->ToggleTrigger();
+  if (!trigger_list) {
+    return nullptr;
+  }
+  for (const ToggleTrigger& trigger : trigger_list->Triggers()) {
+    // TODO(https://crbug.com/1250716): Should consider what to do if
+    // the element has more than one trigger for the toggle we're
+    // looking for.
+    if (trigger.Name() == toggle_name) {
+      return &trigger;
+    }
+  }
+
+  return nullptr;
+}
+
+bool ActivateToggle(Element* element, const AtomicString& toggle_name) {
+  // Check (somewhat defensively, but it can probably happen) that the
+  // element has a toggle named toggle_name, so that we don't activate
+  // some random toggle on an ancestor.
+  const ToggleTrigger* element_trigger =
+      FindToggleTrigger(element, toggle_name);
+  if (!element_trigger) {
+    return false;
+  }
+
+  // Make a copy of the trigger since FireToggleActivation might change
+  // style.
+  // TODO(https://crbug.com/1250716): Is always using the trigger
+  // specified the right thing here?  Might we want something that
+  // varies by role?
+  ToggleTrigger trigger(*element_trigger);
+  return CSSToggle::FireToggleActivation(*element, trigger);
+}
+
+// TODO(https://crbug.com/1250716): Use this when adding
+// arrows-to-open/close, and for handling of focusing of a popup when
+// opening it.
+#if 0
+bool IsToggleActive(Element* element, const AtomicString& toggle_name) {
+  CSSToggle* toggle = CSSToggle::FindToggleInScope(*element, toggle_name);
+  if (!toggle) {
+    return false;
+  }
+
+  return toggle->ValueIsActive();
+}
+#endif
+
+}  // namespace
+
+namespace css_toggle_key_handling {
+
+bool HandleKeydownEvent(Element* element, KeyboardEvent& event) {
+  bool handled = false;
+  DCHECK_EQ(event.type(), event_type_names::kKeydown);
+
+  if (CSSToggleInference* toggle_inference =
+          element->GetDocument().GetCSSToggleInference()) {
+    AtomicString toggle_name = toggle_inference->ToggleNameForElement(element);
+    CSSToggleRole toggle_role = toggle_inference->RoleForElement(element);
+
+    // For many roles, space or enter should activate the toggle.
+    // Handle that in a separate switch so we don't need to repeat it
+    // below.
+    switch (toggle_role) {
+      case CSSToggleRole::kNone:
+      case CSSToggleRole::kAccordion:
+      case CSSToggleRole::kAccordionItem:
+      case CSSToggleRole::kCheckboxGroup:
+      case CSSToggleRole::kDisclosure:
+      case CSSToggleRole::kListbox:
+      case CSSToggleRole::kRadioGroup:
+      case CSSToggleRole::kTabContainer:
+      case CSSToggleRole::kTabPanel:
+      case CSSToggleRole::kTree:
+      case CSSToggleRole::kTreeGroup:
+      case CSSToggleRole::kTreeItem:
+        break;
+
+      case CSSToggleRole::kCheckbox:
+      case CSSToggleRole::kRadioItem:
+        // Checkboxes and radios should be activated by space but not
+        // enter.
+        if (event.key() == " ") {
+          if (ActivateToggle(element, toggle_name)) {
+            return true;
+          }
+        }
+        break;
+
+      case CSSToggleRole::kAccordionItemButton:
+      case CSSToggleRole::kButtonWithPopup:
+      case CSSToggleRole::kDisclosureButton:
+      case CSSToggleRole::kButton:
+      case CSSToggleRole::kListboxItem:
+      case CSSToggleRole::kTab: {
+        if (event.key() == " " || event.key() == "Enter") {
+          if (ActivateToggle(element, toggle_name)) {
+            return true;
+          }
+        }
+        break;
+      }
+    }
+
+    switch (toggle_role) {
+      case CSSToggleRole::kNone:
+      case CSSToggleRole::kAccordion:
+      case CSSToggleRole::kAccordionItem:
+      case CSSToggleRole::kCheckboxGroup:
+      case CSSToggleRole::kDisclosure:
+      case CSSToggleRole::kListbox:
+      case CSSToggleRole::kRadioGroup:
+      case CSSToggleRole::kTabContainer:
+      case CSSToggleRole::kTabPanel:
+        // No special keyboard handling.
+
+        // Accordion items could optionally have up/down and home/end handling.
+        break;
+
+      case CSSToggleRole::kTree:
+      case CSSToggleRole::kTreeGroup:
+      case CSSToggleRole::kTreeItem:
+        // TODO(https://crbug.com/1250716): Figure out keyboard handling.
+        break;
+
+      case CSSToggleRole::kAccordionItemButton: {
+        // Note that for these roles, the toggle root might not be
+        // element.
+        //
+        // Activation is handled above.
+
+        // TODO(https://crbug.com/1250716): handle arrows and home/end.
+        break;
+      }
+
+      case CSSToggleRole::kButton:
+      case CSSToggleRole::kButtonWithPopup:
+      case CSSToggleRole::kDisclosureButton: {
+        // Note that for button, we expect element to be the toggle
+        // root, but that may not be true for button with popup or
+        // disclosure button.
+        //
+        // Activation is handled above.
+        //
+        // TODO(https://crbug.com/1250716): Button with popup, when
+        // opening the popup, should move focus into the popup.
+        break;
+      }
+
+      case CSSToggleRole::kCheckbox:
+      case CSSToggleRole::kListboxItem:
+      case CSSToggleRole::kRadioItem:
+      case CSSToggleRole::kTab: {
+        // Activation is handled above.
+        //
+        // Arrow keys are handled by
+        // FocusgroupController::HandleArrowKeyboardEvent .
+        //
+        // TODO(https://crbug.com/1250716): Handle home/end, at least
+        // for some of these.
+        break;
+      }
+    }
+  }
+
+  return handled;
+}
+
+}  // namespace css_toggle_key_handling
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/css_toggle_key_handling.h b/third_party/blink/renderer/core/dom/css_toggle_key_handling.h
new file mode 100644
index 0000000..dc44294
--- /dev/null
+++ b/third_party/blink/renderer/core/dom/css_toggle_key_handling.h
@@ -0,0 +1,25 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_KEY_HANDLING_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_KEY_HANDLING_H_
+
+namespace blink {
+
+class Element;
+class KeyboardEvent;
+
+namespace css_toggle_key_handling {
+
+// Handle keydown events that should have default behavior based on
+// inferred roles for CSS toggles (see css_toggle_inference.h).
+//
+// Returns whether it handled the event.
+bool HandleKeydownEvent(Element* element, KeyboardEvent& event);
+
+}  // namespace css_toggle_key_handling
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_KEY_HANDLING_H_
diff --git a/third_party/blink/renderer/core/dom/css_toggle_map.cc b/third_party/blink/renderer/core/dom/css_toggle_map.cc
index 5fc02e7..e16abee 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_map.cc
+++ b/third_party/blink/renderer/core/dom/css_toggle_map.cc
@@ -146,7 +146,8 @@
   // TODO(https://crbug.com/1354597): Could this be simplified?
   toggles_snapshot_.ReserveInitialCapacity(toggle_map.size());
   for (const auto& [name, toggle] : toggle_map.toggles_) {
-    // TODO(dbaron): Should this copy-construct the toggle?  (Highlight does!)
+    // TODO(https://crbug.com/1250716): Should this copy-construct the
+    // toggle?  (Highlight does!)
     toggles_snapshot_.push_back(toggle.Get());
   }
 }
diff --git a/third_party/blink/renderer/core/dom/css_toggle_traversal.h b/third_party/blink/renderer/core/dom/css_toggle_traversal.h
index f8cb9fe..7e83d60e 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_traversal.h
+++ b/third_party/blink/renderer/core/dom/css_toggle_traversal.h
@@ -20,7 +20,7 @@
 struct CSSToggleGroupScopeIteratorTraits {
   static absl::optional<ToggleScope> Lookup(Element* element,
                                             const AtomicString& name) {
-    // TODO(dbaron): What if style is null?  See
+    // TODO(https://crbug.com/1250716): What if style is null?  See
     // https://github.com/tabatkins/css-toggle/issues/24 .
     if (const ComputedStyle* style = element->GetComputedStyle()) {
       if (const ToggleGroupList* toggle_groups = style->ToggleGroup()) {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 48513c4..b3cfc15f 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -84,6 +84,8 @@
 #include "third_party/blink/renderer/core/dom/attr.h"
 #include "third_party/blink/renderer/core/dom/container_node.h"
 #include "third_party/blink/renderer/core/dom/css_toggle.h"
+#include "third_party/blink/renderer/core/dom/css_toggle_inference.h"
+#include "third_party/blink/renderer/core/dom/css_toggle_key_handling.h"
 #include "third_party/blink/renderer/core/dom/css_toggle_map.h"
 #include "third_party/blink/renderer/core/dom/dataset_dom_string_map.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -191,7 +193,8 @@
 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
 #include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
-#include "third_party/blink/renderer/core/style/toggle_root_list.h"
+#include "third_party/blink/renderer/core/style/toggle_trigger.h"
+#include "third_party/blink/renderer/core/style/toggle_trigger_list.h"
 #include "third_party/blink/renderer/core/svg/svg_a_element.h"
 #include "third_party/blink/renderer/core/svg/svg_animated_href.h"
 #include "third_party/blink/renderer/core/svg/svg_element.h"
@@ -4912,6 +4915,19 @@
         }
       }
     }
+  } else if (event.type() == event_type_names::kKeydown) {
+    auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
+    if (keyboard_event) {
+      bool handled =
+          css_toggle_key_handling::HandleKeydownEvent(this, *keyboard_event);
+      if (handled) {
+        event.SetDefaultHandled();
+        // We don't want to continue to the default handlers for base
+        // classes, because those are likely to treat the same event as
+        // scrolling the page.
+        return;
+      }
+    }
   }
 
   ContainerNode::DefaultEventHandler(event);
@@ -8177,10 +8193,56 @@
 
 FocusgroupFlags Element::GetFocusgroupFlags() const {
   ExecutionContext* context = GetExecutionContext();
-  if (!RuntimeEnabledFeatures::FocusgroupEnabled(context) || !HasRareData()) {
-    return FocusgroupFlags::kNone;
+
+  // Explicit flags from the focusgroup attribute take priority, when present.
+  if (RuntimeEnabledFeatures::FocusgroupEnabled(context) && HasRareData()) {
+    FocusgroupFlags flags = GetElementRareData()->GetFocusgroupFlags();
+    if (flags != FocusgroupFlags::kNone) {
+      return flags;
+    }
   }
-  return GetElementRareData()->GetFocusgroupFlags();
+
+  // We can also have flags from inferred roles from CSS toggles.
+  if (CSSToggleInference* toggle_inference =
+          GetDocument().GetCSSToggleInference()) {
+    DCHECK(RuntimeEnabledFeatures::CSSTogglesEnabled(context));
+    switch (toggle_inference->RoleForElement(this)) {
+      case CSSToggleRole::kNone:
+      case CSSToggleRole::kAccordion:
+      case CSSToggleRole::kAccordionItem:
+      case CSSToggleRole::kAccordionItemButton:
+      case CSSToggleRole::kButton:
+      case CSSToggleRole::kButtonWithPopup:
+      case CSSToggleRole::kCheckbox:
+      case CSSToggleRole::kDisclosure:
+      case CSSToggleRole::kDisclosureButton:
+      case CSSToggleRole::kListboxItem:
+      case CSSToggleRole::kRadioItem:
+      case CSSToggleRole::kTab:
+      case CSSToggleRole::kTabPanel:
+      case CSSToggleRole::kTreeItem:
+        break;
+      case CSSToggleRole::kCheckboxGroup:
+        return FocusgroupFlags::kHorizontal | FocusgroupFlags::kVertical |
+               FocusgroupFlags::kForCSSToggleCheckbox;
+      case CSSToggleRole::kListbox:
+        return FocusgroupFlags::kHorizontal | FocusgroupFlags::kVertical |
+               FocusgroupFlags::kForCSSToggleListboxItem;
+      case CSSToggleRole::kRadioGroup:
+        return FocusgroupFlags::kHorizontal | FocusgroupFlags::kVertical |
+               FocusgroupFlags::kForCSSToggleRadioItem;
+      case CSSToggleRole::kTabContainer:
+        return FocusgroupFlags::kHorizontal | FocusgroupFlags::kVertical |
+               FocusgroupFlags::kForCSSToggleTab;
+      case CSSToggleRole::kTree:
+      case CSSToggleRole::kTreeGroup:
+        // TODO(https://crbug.com/1250716): This needs more work!
+        return FocusgroupFlags::kVertical |
+               FocusgroupFlags::kForCSSToggleTreeItem;
+    }
+  }
+
+  return FocusgroupFlags::kNone;
 }
 
 bool Element::checkVisibility(CheckVisibilityOptions* options) const {
diff --git a/third_party/blink/renderer/core/dom/focusgroup_flags.h b/third_party/blink/renderer/core/dom/focusgroup_flags.h
index d918b82..c22d929 100644
--- a/third_party/blink/renderer/core/dom/focusgroup_flags.h
+++ b/third_party/blink/renderer/core/dom/focusgroup_flags.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FOCUSGROUP_FLAGS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FOCUSGROUP_FLAGS_H_
 
+#include <type_traits>
+
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
@@ -13,7 +15,7 @@
 
 namespace focusgroup {
 
-enum FocusgroupFlags : uint8_t {
+enum FocusgroupFlags : uint16_t {
   kNone = 0,
   kExtend = 1 << 0,
   kHorizontal = 1 << 1,
@@ -23,18 +25,28 @@
   kWrapVertically = 1 << 5,
   kRowFlow = 1 << 6,
   kColFlow = 1 << 7,
+  kForCSSToggleCheckbox = 1 << 8,
+  kForCSSToggleListboxItem = 1 << 9,
+  kForCSSToggleRadioItem = 1 << 10,
+  kForCSSToggleTab = 1 << 11,
+  kForCSSToggleTreeItem = 1 << 12,
+
+  // union of the above kForCSSToggle*
+  kCSSToggleRestrictions = (1 << 13) - (1 << 8),
 };
 
 inline constexpr FocusgroupFlags operator&(FocusgroupFlags a,
                                            FocusgroupFlags b) {
-  return static_cast<FocusgroupFlags>(static_cast<uint8_t>(a) &
-                                      static_cast<uint8_t>(b));
+  return static_cast<FocusgroupFlags>(
+      static_cast<std::underlying_type_t<FocusgroupFlags>>(a) &
+      static_cast<std::underlying_type_t<FocusgroupFlags>>(b));
 }
 
 inline constexpr FocusgroupFlags operator|(FocusgroupFlags a,
                                            FocusgroupFlags b) {
-  return static_cast<FocusgroupFlags>(static_cast<uint8_t>(a) |
-                                      static_cast<uint8_t>(b));
+  return static_cast<FocusgroupFlags>(
+      static_cast<std::underlying_type_t<FocusgroupFlags>>(a) |
+      static_cast<std::underlying_type_t<FocusgroupFlags>>(b));
 }
 
 inline FocusgroupFlags& operator|=(FocusgroupFlags& a, FocusgroupFlags b) {
@@ -46,7 +58,8 @@
 }
 
 inline constexpr FocusgroupFlags operator~(FocusgroupFlags flags) {
-  return static_cast<FocusgroupFlags>(~static_cast<uint8_t>(flags));
+  return static_cast<FocusgroupFlags>(
+      ~static_cast<std::underlying_type_t<FocusgroupFlags>>(flags));
 }
 
 FocusgroupFlags FindNearestFocusgroupAncestorFlags(const Element* element);
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.cc b/third_party/blink/renderer/core/events/pointer_event_factory.cc
index 3d0315a9..6950388 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.cc
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.cc
@@ -357,6 +357,8 @@
   SetLastPosition(pointer_event_init->pointerId(),
                   web_pointer_event.PositionInScreen(), event_type);
 
+  pointer_event_init->setDeviceId(GetBlinkDeviceId(web_pointer_event));
+
   return PointerEvent::Create(type, pointer_event_init,
                               web_pointer_event.TimeStamp());
 }
@@ -513,6 +515,8 @@
   pointer_id_mapping_.clear();
   pointer_id_last_position_mapping_.clear();
 
+  device_id_browser_to_blink_mapping_.clear();
+
   // Always add mouse pointer in initialization and never remove it.
   // No need to add it to |pointer_incoming_id_mapping_| as it is not going to
   // be used with the existing APIs
@@ -523,6 +527,7 @@
                     false, true));
 
   current_id_ = PointerEventFactory::kMouseId + 1;
+  current_device_id_ = PointerEventFactory::kMouseId + 1;
 }
 
 PointerId PointerEventFactory::AddIdAndActiveButtons(
@@ -643,4 +648,23 @@
   return kInvalidId;
 }
 
+int32_t PointerEventFactory::GetBlinkDeviceId(
+    const WebPointerEvent& web_pointer_event) {
+  if (web_pointer_event.pointer_type ==
+      WebPointerProperties::PointerType::kMouse) {
+    return PointerEventFactory::kMouseId;
+  }
+
+  const int32_t incoming_id = web_pointer_event.device_id;
+  if (incoming_id == -1) {
+    return -1;
+  }
+
+  auto result = device_id_browser_to_blink_mapping_.insert(incoming_id, -1);
+  if (result.is_new_entry) {
+    result.stored_value->value = current_device_id_++;
+  }
+  return result.stored_value->value;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.h b/third_party/blink/renderer/core/events/pointer_event_factory.h
index 323fe7d..6b8855b0 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.h
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/events/pointer_event.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace blink {
 
@@ -126,6 +127,11 @@
               PairHashTraits<IntWithZeroKeyHashTraits<int>,
                              IntWithZeroKeyHashTraits<int>>>;
 
+  // Map the device id from the browser to the one generated by blink for the
+  // current document.
+  using BrowserDeviceIdToBlinkDeviceIdMap =
+      HashMap<int32_t, int32_t, IntWithZeroKeyHashTraits<int64_t>>;
+
   typedef struct PointerAttributes {
     IncomingId incoming_id;
     bool is_active_buttons;
@@ -164,6 +170,11 @@
       const Vector<WebPointerEvent>& event_list,
       LocalDOMWindow* view);
 
+  // Retrieves entry in device_id_browser_to_blink_mapping_ if it exists or
+  // creates one with an incremented int starting at -1 for the browser device
+  // id. This ensures untraceable ids across sessions.
+  int32_t GetBlinkDeviceId(const WebPointerEvent&);
+
   PointerId current_id_;
   IncomingIdToPointerIdMap pointer_incoming_id_mapping_;
   PointerIdKeyMap<PointerAttributes> pointer_id_mapping_;
@@ -175,6 +186,10 @@
 
   PointerIdKeyMap<gfx::PointF> pointer_id_last_position_mapping_;
   PointerIdKeyMap<gfx::PointF> pointerrawupdate_last_position_mapping_;
+
+  // This map contains every received device id from browser since page load.
+  BrowserDeviceIdToBlinkDeviceIdMap device_id_browser_to_blink_mapping_;
+  int32_t current_device_id_ = -1;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory_test.cc b/third_party/blink/renderer/core/events/pointer_event_factory_test.cc
index 9634da8..7686011 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory_test.cc
+++ b/third_party/blink/renderer/core/events/pointer_event_factory_test.cc
@@ -13,6 +13,12 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/pointer_type_names.h"
+#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+
+namespace {
+const int32_t kBrowserDeviceId0 = 0;
+const int32_t kBrowserDeviceId1 = 1;
+}  // namespace
 
 namespace blink {
 
@@ -643,4 +649,134 @@
                                        event_type_names::kPointerover);
 }
 
+class PointerEventFactoryDeviceIdTest : public SimTest {
+ protected:
+  PointerEvent* CreatePointerEvent(
+      WebPointerProperties::PointerType pointer_type,
+      int raw_id,
+      int32_t device_id) {
+    WebPointerEvent web_pointer_event;
+    web_pointer_event.pointer_type = pointer_type;
+    web_pointer_event.id = raw_id;
+    web_pointer_event.SetType(WebInputEvent::Type::kPointerDown);
+    web_pointer_event.SetTimeStamp(WebInputEvent::GetStaticTimeStampForTests());
+    web_pointer_event.SetModifiers(WebInputEvent::kNoModifiers);
+    web_pointer_event.force = 1.0;
+    web_pointer_event.hovering = false;
+    web_pointer_event.button = WebPointerProperties::Button::kNoButton;
+    web_pointer_event.SetPositionInScreen(100, 100);
+    web_pointer_event.device_id = device_id;
+    Vector<WebPointerEvent> coalesced_events;
+    Vector<WebPointerEvent> predicted_events;
+
+    LocalDOMWindow* window = GetDocument().domWindow();
+    return pointer_event_factory_.Create(web_pointer_event, coalesced_events,
+                                         predicted_events, window);
+  }
+
+  int32_t CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType pointer_type,
+      int raw_id,
+      int32_t device_id) {
+    PointerEvent* pointer_event =
+        CreatePointerEvent(pointer_type, raw_id, device_id);
+    // Pointer events of type eraser are converted to pen events here:
+    // PointerEventFactory::ConvertIdTypeButtonsEvent. Therefore check below to
+    // make sure the conversion is done as expected.
+    if (pointer_type == WebPointerProperties::PointerType::kEraser) {
+      pointer_type = WebPointerProperties::PointerType::kPen;
+    }
+    const String& expected_pointer_type =
+        PointerEventFactory::PointerTypeNameForWebPointPointerType(
+            pointer_type);
+    EXPECT_EQ(expected_pointer_type, pointer_event->pointerType());
+    return pointer_event->deviceId();
+  }
+
+  PointerEventFactory pointer_event_factory_;
+};
+
+// This test validates that the unique device id provided to blink is reset upon
+// a new document being created. Furthermore, it validates that the id is random
+// for the same pen but across different documents.
+TEST_F(PointerEventFactoryDeviceIdTest, DeviceIdResetAfterClear) {
+  int32_t blink_device_id_1 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 0,
+      /* Device id */ kBrowserDeviceId0);
+  int32_t blink_device_id_2 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 1,
+      /* Device id */ kBrowserDeviceId0);
+  // Id is the same for the same pen.
+  ASSERT_EQ(blink_device_id_1, blink_device_id_2);
+
+  int32_t blink_device_id_3 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 2,
+      /* Device id */ kBrowserDeviceId1);
+  // Id is different for a different pen.
+  ASSERT_NE(blink_device_id_1, blink_device_id_3);
+
+  pointer_event_factory_.Clear();
+
+  int32_t blink_device_id_4 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 0,
+      /* Device id */ kBrowserDeviceId1);
+  // Id not the same as before clear, even though the pen is the same.
+  ASSERT_NE(blink_device_id_3, blink_device_id_4);
+
+  int32_t blink_device_id_5 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 1,
+      /* Device id */ kBrowserDeviceId0);
+  // Id is not the same fore a different pen.
+  ASSERT_NE(blink_device_id_4, blink_device_id_5);
+  // Id not the same as before clear, even though the pen is the same.
+  ASSERT_NE(blink_device_id_5, blink_device_id_1);
+
+  int32_t blink_device_id_6 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 2,
+      /* Device id */ kBrowserDeviceId0);
+  // Id is the same for the same pen.
+  ASSERT_EQ(blink_device_id_5, blink_device_id_6);
+  pointer_event_factory_.Clear();
+}
+
+// Erasers on the surface hub have a pointer type of
+// WebPointerProperties::PointerType::kEraser. Verify that an eraser is treated
+// just like a pen event would be.
+TEST_F(PointerEventFactoryDeviceIdTest, DeviceIdForMousePointerType) {
+  int32_t blink_device_id_1 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kEraser, /* Raw pointer id */ 0,
+      /* Device id */ kBrowserDeviceId0);
+  int32_t blink_device_id_2 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kEraser, /* Raw pointer id */ 1,
+      /* Device id */ kBrowserDeviceId0);
+  // Id is the same for the same pen.
+  ASSERT_EQ(blink_device_id_1, blink_device_id_2);
+  ASSERT_GT(blink_device_id_1, 1);
+
+  int32_t blink_device_id_3 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kPen, /* Raw pointer id */ 2,
+      /* Device id */ kBrowserDeviceId0);
+  // Id is same for the same pen.
+  ASSERT_EQ(blink_device_id_1, blink_device_id_3);
+
+  // Different blink device id for different pen id.
+  int32_t blink_device_id_4 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kEraser, /* Raw pointer id */ 2,
+      /* Device id */ kBrowserDeviceId1);
+  ASSERT_NE(blink_device_id_1, blink_device_id_4);
+
+  // Invalid device id.
+  int32_t blink_device_id_5 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kEraser, /* Raw pointer id */ 2,
+      /* Device id */ -1);
+  ASSERT_EQ(-1, blink_device_id_5);
+
+  // Mouse (device id of the web pointer event does not matter).
+  int32_t blink_device_id_6 = CreatePointerEventAndGetDeviceId(
+      WebPointerProperties::PointerType::kMouse, /* Raw pointer id */ 2,
+      /* Device id */ -1);
+  ASSERT_EQ(1, blink_device_id_6);
+  pointer_event_factory_.Clear();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index b0504264..3bf4ea5 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -269,7 +269,7 @@
   bool is_in_back_forward_cache() const { return is_in_back_forward_cache_; }
 
   void SetLifecycleState(mojom::FrameLifecycleState);
-  void NotifyContextDestroyed();
+  virtual void NotifyContextDestroyed();
 
   using ConsoleLogger::AddConsoleMessage;
 
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fence.cc b/third_party/blink/renderer/core/html/fenced_frame/fence.cc
index 280ba21..2b3245c 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fence.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fence.cc
@@ -173,7 +173,11 @@
                                           const String& event,
                                           ExceptionState& exception_state) {
   if (!base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi) ||
-      !blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get()) {
+      !blink::features::kPrivateAggregationApiEnabledInFledge.Get() ||
+      (!blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get() &&
+       !base::FeatureList::IsEnabled(
+           blink::features::
+               kPrivateAggregationApiFledgeExtensionsLocalTestingOverride))) {
     exception_state.ThrowSecurityError(
         "FLEDGE extensions must be enabled to use reportEvent() for private "
         "aggregation events.");
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 ad14232..3f951c0 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
@@ -394,14 +394,18 @@
           HidePopoverFocusBehavior::kNone,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
-      PseudoStateChanged(CSSSelector::kPseudoClosed);
-      PseudoStateChanged(CSSSelector::kPseudoOpen);
     }
-    if (button_part_) {
-      button_part_->Focus();
-    }
-    if (selectedOption() != selected_option_when_listbox_opened_)
-      DispatchChangeEvent();
+  }
+}
+
+void HTMLSelectMenuElement::ListboxWasClosed() {
+  PseudoStateChanged(CSSSelector::kPseudoClosed);
+  PseudoStateChanged(CSSSelector::kPseudoOpen);
+  if (button_part_) {
+    button_part_->Focus();
+  }
+  if (selectedOption() != selected_option_when_listbox_opened_) {
+    DispatchChangeEvent();
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
index c76f81e..1cbda0c 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
@@ -84,6 +84,8 @@
   // HTMLSelectElement::GetOptionList().
   ListItems GetListItems() const;
 
+  void ListboxWasClosed();
+
  private:
   class SelectMutationCallback;
 
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 74f9cd57..cf3f6b0 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1785,6 +1785,10 @@
           /*gate_on_user_activation=*/true));
     }
   }
+
+  if (auto* selectmenu = ownerSelectMenuElement()) {
+    selectmenu->ListboxWasClosed();
+  }
 }
 
 void HTMLElement::SetPopoverFocusOnShow() {
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.cc b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
index 03f491a0..ad9e29c 100644
--- a/third_party/blink/renderer/core/input/keyboard_event_manager.cc
+++ b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
@@ -467,7 +467,8 @@
     return;
 
   ExecutionContext* context = frame_->GetDocument()->GetExecutionContext();
-  if (RuntimeEnabledFeatures::FocusgroupEnabled(context) &&
+  if ((RuntimeEnabledFeatures::FocusgroupEnabled(context) ||
+       RuntimeEnabledFeatures::CSSTogglesEnabled(context)) &&
       FocusgroupController::HandleArrowKeyboardEvent(event, frame_)) {
     event->SetDefaultHandled();
     return;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 97cadcad..79bf5279 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1657,16 +1657,13 @@
 void FrameLoader::DispatchDocumentElementAvailable() {
   ScriptForbiddenScope forbid_scripts;
 
-  // Notify the browser about non-blank documents loading in the top frame.
-  KURL url = frame_->GetDocument()->Url();
-  if (url.IsValid() && !url.IsAboutBlankURL()) {
-    if (frame_->IsMainFrame()) {
-      // For now, don't remember plugin zoom values.  We don't want to mix them
-      // with normal web content (i.e. a fixed layout plugin would usually want
-      // them different).
-      frame_->GetLocalFrameHostRemote().MainDocumentElementAvailable(
-          frame_->GetDocument()->IsPluginDocument());
-    }
+  // Notify the browser about documents loading in the top frame.
+  if (frame_->GetDocument()->Url().IsValid() && frame_->IsMainFrame()) {
+    // For now, don't remember plugin zoom values.  We don't want to mix them
+    // with normal web content (i.e. a fixed layout plugin would usually want
+    // them different).
+    frame_->GetLocalFrameHostRemote().MainDocumentElementAvailable(
+        frame_->GetDocument()->IsPluginDocument());
   }
 
   Client()->DocumentElementAvailable();
diff --git a/third_party/blink/renderer/core/page/focusgroup_controller.cc b/third_party/blink/renderer/core/page/focusgroup_controller.cc
index 8287b7f..bfa35724 100644
--- a/third_party/blink/renderer/core/page/focusgroup_controller.cc
+++ b/third_party/blink/renderer/core/page/focusgroup_controller.cc
@@ -29,7 +29,10 @@
   DCHECK(frame);
   DCHECK(frame->DomWindow());
   ExecutionContext* context = frame->DomWindow()->GetExecutionContext();
-  DCHECK(RuntimeEnabledFeatures::FocusgroupEnabled(context));
+  DCHECK(RuntimeEnabledFeatures::FocusgroupEnabled(context) ||
+         RuntimeEnabledFeatures::CSSTogglesEnabled(context));
+  // TODO(https://crbug.com/1250716): Test
+  // RuntimeEnabledFeatures::FocusgroupEnabled(context) elsewhere?
 
   FocusgroupDirection direction = utils::FocusgroupDirectionForEvent(event);
   if (direction == FocusgroupDirection::kNone)
@@ -604,4 +607,4 @@
                              nullptr));
 }
 
-}  // namespace blink
\ No newline at end of file
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/page/focusgroup_controller_utils.cc b/third_party/blink/renderer/core/page/focusgroup_controller_utils.cc
index b5372062..8f7a16e3 100644
--- a/third_party/blink/renderer/core/page/focusgroup_controller_utils.cc
+++ b/third_party/blink/renderer/core/page/focusgroup_controller_utils.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/page/focusgroup_controller_utils.h"
 
+#include "third_party/blink/renderer/core/dom/css_toggle_inference.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/focusgroup_flags.h"
@@ -109,6 +110,7 @@
           }
           break;
         case FocusgroupType::kLinear:
+          // TODO(https://crbug.com/1250716): Check CSS toggle restrictions?
           if (!(ancestor_flags & FocusgroupFlags::kGrid))
             return ancestor;
           break;
@@ -178,13 +180,48 @@
     return false;
 
   // All children of a focusgroup are considered focusgroup items if they are
-  // focusable.
+  // focusable, except for some special cases for CSS toggles.
   Element* parent = FlatTreeTraversal::ParentElement(*element);
   if (!parent)
     return false;
 
   FocusgroupFlags parent_flags = parent->GetFocusgroupFlags();
-  return parent_flags != FocusgroupFlags::kNone;
+  if (parent_flags == FocusgroupFlags::kNone) {
+    return false;
+  }
+
+  FocusgroupFlags toggle_restrictions =
+      parent_flags & FocusgroupFlags::kCSSToggleRestrictions;
+  if (toggle_restrictions) {
+    CSSToggleInference* toggle_inference =
+        element->GetDocument().GetCSSToggleInference();
+    DCHECK(toggle_inference)
+        << "toggle restrictions should only exist because of toggle inference";
+    CSSToggleRole element_role = toggle_inference->RoleForElement(element);
+    if (toggle_restrictions & FocusgroupFlags::kForCSSToggleCheckbox) {
+      DCHECK_EQ(toggle_restrictions, FocusgroupFlags::kForCSSToggleCheckbox);
+      return element_role == CSSToggleRole::kCheckbox;
+    }
+    if (toggle_restrictions & FocusgroupFlags::kForCSSToggleListboxItem) {
+      DCHECK_EQ(toggle_restrictions, FocusgroupFlags::kForCSSToggleListboxItem);
+      return element_role == CSSToggleRole::kListboxItem;
+    }
+    if (toggle_restrictions & FocusgroupFlags::kForCSSToggleRadioItem) {
+      DCHECK_EQ(toggle_restrictions, FocusgroupFlags::kForCSSToggleRadioItem);
+      return element_role == CSSToggleRole::kRadioItem;
+    }
+    if (toggle_restrictions & FocusgroupFlags::kForCSSToggleTab) {
+      DCHECK_EQ(toggle_restrictions, FocusgroupFlags::kForCSSToggleTab);
+      return element_role == CSSToggleRole::kTab;
+    }
+    if (toggle_restrictions & FocusgroupFlags::kForCSSToggleTreeItem) {
+      DCHECK_EQ(toggle_restrictions, FocusgroupFlags::kForCSSToggleTreeItem);
+      return element_role == CSSToggleRole::kTreeItem;
+    }
+    NOTREACHED();
+  }
+
+  return true;
 }
 
 // This function is called whenever the |element| passed by parameter has fallen
@@ -286,4 +323,4 @@
   }
 }
 
-}  // namespace blink
\ No newline at end of file
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
index 72e10ec..eda534c2cd 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -2487,15 +2487,13 @@
   auto* scrollable_area = ScrollableAreaByDOMElementId("textinput");
   const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area);
   ASSERT_TRUE(scroll_node);
-  ASSERT_EQ(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText,
+  ASSERT_EQ(RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+                ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+                // No main thread scrolling reasons for non-composited
+                // scrollable input/select.
+                : cc::MainThreadScrollingReason::kNotScrollingOnMain,
             scroll_node->main_thread_scrolling_reasons);
-
-  EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*scroll_node));
+  EXPECT_FALSE(scroll_node->is_composited);
 }
 
 class ScrollingSimTest : public SimTest,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index fb3433bc..f53c5af9 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2457,6 +2457,20 @@
   return !properties->ScrollTranslation()->HasDirectCompositingReasons();
 }
 
+bool PaintLayerScrollableArea::PrefersNonCompositedScrolling() const {
+  if (Node* node = GetLayoutBox()->GetNode()) {
+    if (IsA<HTMLSelectElement>(node)) {
+      return true;
+    }
+    if (TextControlElement* text_control = EnclosingTextControl(node)) {
+      if (IsA<HTMLInputElement>(text_control)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling(
     bool force_prefer_compositing_to_lcd_text) {
   const auto* box = GetLayoutBox();
@@ -2497,6 +2511,9 @@
   if (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) {
     return false;
   }
+  if (PrefersNonCompositedScrolling()) {
+    return false;
+  }
 
   const auto* box = GetLayoutBox();
   bool needs_composited_scrolling = true;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 037a6cc..62c40622 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -448,6 +448,10 @@
   }
 #endif
 
+  // TODO(crbug.com/1414885): Move this function into
+  // paint_property_tree_builder.cc as a local function.
+  bool PrefersNonCompositedScrolling() const;
+
   gfx::Rect ResizerCornerRect(ResizerHitTestType) const;
 
   PaintLayer* Layer() const override;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index 6968ebb..0187818 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -346,19 +346,7 @@
 
   Element* element = GetDocument().getElementById("select");
   EXPECT_FALSE(HasDirectCompositingReasons(element->GetLayoutObject()));
-  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
-    // PaintArtifactCompositor can detect the opaque background of <select>
-    // and use composited scrolling.
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    // <select> implementation is different and not scrollable on Android and
-    // iOS.
-    EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
-#else
-    EXPECT_TRUE(UsesCompositedScrolling(element->GetLayoutBox()));
-#endif
-  } else {
-    EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
-  }
+  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
 
   element->setAttribute("class", "composited");
   UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index b368262..923fb908 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -20,6 +20,9 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
 #include "third_party/blink/renderer/core/layout/anchor_scroll_data.h"
 #include "third_party/blink/renderer/core/layout/fragmentainer_iterator.h"
 #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
@@ -106,7 +109,9 @@
       &ScrollPaintPropertyNode::Root();
 }
 
-PaintPropertyTreeBuilderContext::PaintPropertyTreeBuilderContext() = default;
+PaintPropertyTreeBuilderContext::PaintPropertyTreeBuilderContext()
+    : composited_scrolling_preference(
+          static_cast<unsigned>(CompositedScrollingPreference::kDefault)) {}
 
 PaintPropertyTreeBuilderContext::~PaintPropertyTreeBuilderContext() {
   fragments.clear();
@@ -2330,8 +2335,9 @@
         object_.GetFrameView()->RemoveUserScrollableArea(scrollable_area);
       }
 
-      state.prefers_composited_scrolling =
-          full_context_.prefers_composited_scrolling;
+      state.composited_scrolling_preference =
+          static_cast<CompositedScrollingPreference>(
+              full_context_.composited_scrolling_preference);
       state.main_thread_scrolling_reasons = GetMainThreadScrollingReasons();
 
       state.compositor_element_id = scrollable_area->GetScrollElementId();
@@ -3149,11 +3155,17 @@
   context_.was_main_thread_scrolling = false;
   if (const auto* box = DynamicTo<LayoutBox>(object_)) {
     if (auto* scrollable_area = box->GetScrollableArea()) {
-      context_.prefers_composited_scrolling =
+      bool force_prefer_compositing =
           CompositingReasonFinder::ShouldForcePreferCompositingToLCDText(
               object_, context_.direct_compositing_reasons);
-      scrollable_area->UpdateNeedsCompositedScrolling(
-          context_.prefers_composited_scrolling);
+      if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+        context_.composited_scrolling_preference = static_cast<unsigned>(
+            force_prefer_compositing ? CompositedScrollingPreference::kPreferred
+            : scrollable_area->PrefersNonCompositedScrolling()
+                ? CompositedScrollingPreference::kNotPreferred
+                : CompositedScrollingPreference::kDefault);
+      }
+      scrollable_area->UpdateNeedsCompositedScrolling(force_prefer_compositing);
       context_.was_main_thread_scrolling =
           scrollable_area->ShouldScrollOnMainThread();
       context_.direct_compositing_reasons =
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
index 1f3bf8b..54723242 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
@@ -225,7 +225,6 @@
 
   unsigned was_main_thread_scrolling : 1 = false;
   unsigned scroll_unification_enabled : 1 = false;
-  unsigned prefers_composited_scrolling : 1 = false;
 
   // Main thread scrolling reasons that apply to all scrollers in the current
   // LocalFrameView subtree.
@@ -237,6 +236,8 @@
           cc::MainThreadScrollingReason::kPopupNoThreadedInput;
   static_assert(kGlobalMainThreadScrollingReasons < (1 << 6));
 
+  unsigned composited_scrolling_preference : 2;
+
   // This is always recalculated in PaintPropertyTreeBuilder::UpdateForSelf()
   // which overrides the inherited value.
   CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index b3a294e..c44a05d9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1395,10 +1395,10 @@
     case CSSToggleRole::kAccordionItemButton:
       return ax::mojom::blink::Role::kButton;
     case CSSToggleRole::kTabContainer:
-      // TODO(dbaron): We should verify that using kTabList really
-      // works here, since this is a container that has both the tab
-      // list *and* the tab panels.  We should also make sure that
-      // posinset/setsize work correctly for the tabs.
+      // TODO(https://crbug.com/1250716): We should verify that using
+      // kTabList really works here, since this is a container that has
+      // both the tab list *and* the tab panels.  We should also make
+      // sure that posinset/setsize work correctly for the tabs.
       return ax::mojom::blink::Role::kTabList;
     case CSSToggleRole::kTab:
       return ax::mojom::blink::Role::kTab;
@@ -1445,8 +1445,8 @@
   //   3. Native markup role
   // but we may decide to change how the CSS Toggle inference fits in.
   //
-  // TODO(dbaron): Perhaps revisit whether there are types of elements
-  // where toggles should not work.
+  // TODO(https://crbug.com/1250716): Perhaps revisit whether there are
+  // types of elements where toggles should not work.
 
   if (aria_role_ != ax::mojom::blink::Role::kUnknown) {
     return aria_role_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 659d533..7779f1e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -3910,14 +3910,11 @@
   }
 }
 
-bool AXObjectCacheImpl::SerializeEntireTree(bool exclude_offscreen,
-                                            size_t max_node_count,
+bool AXObjectCacheImpl::SerializeEntireTree(size_t max_node_count,
                                             base::TimeDelta timeout,
                                             ui::AXTreeUpdate* response) {
   BlinkAXTreeSource* tree_source = BlinkAXTreeSource::Create(*this);
 
-  tree_source->set_exclude_offscreen(exclude_offscreen);
-
   // The serializer returns an ui::AXTreeUpdate, which can store a complete
   // or a partial accessibility tree. AXTreeSerializer is stateful, but the
   // first time you serialize from a brand-new tree you're guaranteed to get a
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 5f394bc2..1c6cd2f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -455,8 +455,7 @@
     return ax_tree_source_->GetPluginRoot();
   }
 
-  bool SerializeEntireTree(bool exclude_offscreen,
-                           size_t max_node_count,
+  bool SerializeEntireTree(size_t max_node_count,
                            base::TimeDelta timeout,
                            ui::AXTreeUpdate*) override;
 
diff --git a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
index 3363c214..11fd8d3 100644
--- a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
+++ b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
@@ -327,10 +327,6 @@
   CheckParentUnignoredOf(node, child);
 #endif
 
-  if (exclude_offscreen_ && child->IsOffScreen()) {
-    return nullptr;
-  }
-
   return child;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
index 5bcd65316..57db9b0 100644
--- a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
+++ b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
@@ -66,8 +66,6 @@
     max_image_data_size_ = max_size;
   }
 
-  void set_exclude_offscreen(bool exclude) { exclude_offscreen_ = exclude; }
-
   // Ignore code that limits based on the protocol (like https, file, etc.)
   // to enable tests to run.
   static void IgnoreProtocolChecksForTesting();
@@ -110,11 +108,6 @@
   // for debugging.
   bool image_annotation_debugging_ = false;
 
-  // If true, excludes nodes and their entire subtrees if they're entirely
-  // offscreen. This is only meant to be used when snapshotting the
-  // accessibility tree.
-  bool exclude_offscreen_ = false;
-
   Member<AXObjectCacheImpl> ax_object_cache_;
 
   // These are updated when calling |Freeze|.
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_extensions_client_outputs_json.idl b/third_party/blink/renderer/modules/credentialmanagement/authentication_extensions_client_outputs_json.idl
new file mode 100644
index 0000000..7ab29f3
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_extensions_client_outputs_json.idl
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#dictdef-authenticationextensionsclientoutputsjson
+
+dictionary AuthenticationExtensionsClientOutputsJSON {
+  boolean appid;
+
+  // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#sctn-hmac-secret-extension
+  boolean hmacCreateSecret;
+
+  // https://w3c.github.io/webauthn/#sctn-authenticator-credential-properties-extension
+  CredentialPropertiesOutput credProps;
+
+  // https://w3c.github.io/webauthn/#sctn-large-blob-extension
+  [RuntimeEnabled=WebAuthenticationLargeBlobExtension] object largeBlob;
+
+  // https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html#sctn-credBlob-extension
+  boolean credBlob;
+  Base64URLString getCredBlob;
+
+  // Device-bound public-key support.
+  // https://github.com/w3c/webauthn/pull/1663
+  [RuntimeEnabled=WebAuthenticationDevicePublicKey] object devicePubKey;
+
+  // https://w3c.github.io/webauthn/#prf-extension
+  [RuntimeEnabled=WebAuthenticationPRF] object prf;
+};
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_response_json.idl b/third_party/blink/renderer/modules/credentialmanagement/authentication_response_json.idl
new file mode 100644
index 0000000..1d0c04f
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_response_json.idl
@@ -0,0 +1,14 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson
+
+dictionary AuthenticationResponseJSON {
+    Base64URLString id;
+    Base64URLString rawId;
+    AuthenticatorAssertionResponseJSON response;
+    DOMString? authenticatorAttachment;
+    AuthenticationExtensionsClientOutputsJSON clientExtensionResults;
+    DOMString type;
+};
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authenticator_assertion_response_json.idl b/third_party/blink/renderer/modules/credentialmanagement/authenticator_assertion_response_json.idl
new file mode 100644
index 0000000..ede85a6
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/authenticator_assertion_response_json.idl
@@ -0,0 +1,12 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#dictdef-authenticatorassertionresponsejson
+
+dictionary AuthenticatorAssertionResponseJSON {
+    Base64URLString clientDataJSON;
+    Base64URLString authenticatorData;
+    Base64URLString signature;
+    Base64URLString? userHandle;
+};
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authenticator_attestation_response_json.idl b/third_party/blink/renderer/modules/credentialmanagement/authenticator_attestation_response_json.idl
new file mode 100644
index 0000000..a137e76
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/authenticator_attestation_response_json.idl
@@ -0,0 +1,11 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#dictdef-authenticatorattestationresponsejson
+
+dictionary AuthenticatorAttestationResponseJSON {
+    Base64URLString clientDataJSON;
+    Base64URLString attestationObject;
+    sequence<DOMString> transports;
+};
diff --git a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
index f0ee96e3..44c9e56 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/notreached.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-shared.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -115,6 +116,12 @@
   return promise;
 }
 
+const V8UnionAuthenticationResponseJSONOrRegistrationResponseJSON*
+PublicKeyCredential::toJSON(ScriptState* script_state) const {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 void PublicKeyCredential::Trace(Visitor* visitor) const {
   visitor->Trace(raw_id_);
   visitor->Trace(response_);
diff --git a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.h b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.h
index 0c29b93d..b0fdbcb 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.h
+++ b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.h
@@ -23,6 +23,7 @@
 class AuthenticatorResponse;
 class ScriptPromise;
 class ScriptState;
+class V8UnionAuthenticationResponseJSONOrRegistrationResponseJSON;
 
 class MODULES_EXPORT PublicKeyCredential : public Credential {
   DEFINE_WRAPPERTYPEINFO();
@@ -45,6 +46,8 @@
       ScriptState*);
   AuthenticationExtensionsClientOutputs* getClientExtensionResults() const;
   static ScriptPromise isConditionalMediationAvailable(ScriptState*);
+  const V8UnionAuthenticationResponseJSONOrRegistrationResponseJSON* toJSON(
+      ScriptState*) const;
 
   // Credential:
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.idl b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.idl
index dd9c6f4..d2541eaa 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.idl
+++ b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.idl
@@ -4,6 +4,9 @@
 
 // https://w3c.github.io/webauthn/#publickeycredential
 
+typedef DOMString Base64URLString;
+typedef (RegistrationResponseJSON or AuthenticationResponseJSON) PublicKeyCredentialJSON;
+
 [
     RuntimeEnabled=WebAuth,
     SecureContext,
@@ -15,4 +18,5 @@
     [CallWith=ScriptState] static Promise<boolean> isUserVerifyingPlatformAuthenticatorAvailable();
     AuthenticationExtensionsClientOutputs getClientExtensionResults();
     [CallWith=ScriptState] static Promise<boolean> isConditionalMediationAvailable();
+    [RuntimeEnabled=WebAuthenticationJSONSerialization, CallWith=ScriptState] PublicKeyCredentialJSON toJSON();
 };
diff --git a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential_json.idl b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential_json.idl
new file mode 100644
index 0000000..88c4dfff
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential_json.idl
@@ -0,0 +1,8 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#typedefdef-publickeycredentialjson
+
+typedef DOMString Base64URLString;
+typedef (RegistrationResponseJSON or AuthenticationResponseJSON) PublicKeyCredentialJSON;
diff --git a/third_party/blink/renderer/modules/credentialmanagement/registration_response_json.idl b/third_party/blink/renderer/modules/credentialmanagement/registration_response_json.idl
new file mode 100644
index 0000000..bfacf66
--- /dev/null
+++ b/third_party/blink/renderer/modules/credentialmanagement/registration_response_json.idl
@@ -0,0 +1,14 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webauthn/#dictdef-registrationresponsejson
+
+dictionary RegistrationResponseJSON {
+    Base64URLString id;
+    Base64URLString rawId;
+    AuthenticatorAttestationResponseJSON response;
+    DOMString? authenticatorAttachment;
+    AuthenticationExtensionsClientOutputsJSON clientExtensionResults;
+    DOMString type;
+};
diff --git a/third_party/blink/renderer/modules/exported/web_ax_context.cc b/third_party/blink/renderer/modules/exported/web_ax_context.cc
index 043a858..91a1c39 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_context.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_context.cc
@@ -78,8 +78,7 @@
   private_->GetAXObjectCache().Thaw();
 }
 
-bool WebAXContext::SerializeEntireTree(bool exclude_offscreen,
-                                       size_t max_node_count,
+bool WebAXContext::SerializeEntireTree(size_t max_node_count,
                                        base::TimeDelta timeout,
                                        ui::AXTreeUpdate* response) {
   if (!HasActiveDocument()) {
@@ -91,8 +90,8 @@
     return false;
   }
 
-  return private_->GetAXObjectCache().SerializeEntireTree(
-      exclude_offscreen, max_node_count, timeout, response);
+  return private_->GetAXObjectCache().SerializeEntireTree(max_node_count,
+                                                          timeout, response);
 }
 
 void WebAXContext::MarkAllImageAXObjectsDirty() {
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc b/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc
index 41848bf..fa695074 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc
@@ -144,10 +144,8 @@
             *priority_composition_manager_.Get())) {
       // Dependents can be added while dispatching events, but none are removed
       // since having an active iterator will strongify weak references, making
-      // the following iteration safe.
-      //
-      // TODO(crbug.com/1323391): Should this ignore newly added signals or not?
-      // It probably doesn't matter much, but it should be specced and tested.
+      // the following iteration safe. Signaling priority change on newly added
+      // dependent signals has no effect since the new priority is already set.
       for (auto& abort_signal : source_signal_manager->GetDependentSignals()) {
         To<DOMTaskSignal>(abort_signal.Get())
             ->SignalPriorityChange(priority, exception_state);
@@ -203,7 +201,10 @@
   }
   DCHECK(RuntimeEnabledFeatures::AbortSignalCompositionEnabled());
   // True if priority changes for this signal can occur and be observed.
+  // `priority_composition_manager_` can be null if this object isn't fully
+  // constructed.
   bool has_pending_priority_activity =
+      priority_composition_manager_ &&
       !priority_composition_manager_->IsSettled() &&
       (HasEventListeners(event_type_names::kPrioritychange) ||
        !priority_change_algorithms_.empty());
diff --git a/third_party/blink/renderer/modules/shared_storage/BUILD.gn b/third_party/blink/renderer/modules/shared_storage/BUILD.gn
index 641aa24..229ee07a 100644
--- a/third_party/blink/renderer/modules/shared_storage/BUILD.gn
+++ b/third_party/blink/renderer/modules/shared_storage/BUILD.gn
@@ -11,6 +11,8 @@
   ]
 
   sources = [
+    "private_aggregation.cc",
+    "private_aggregation.h",
     "shared_storage.cc",
     "shared_storage.h",
     "shared_storage_operation_definition.cc",
diff --git a/third_party/blink/renderer/modules/shared_storage/private_aggregation.cc b/third_party/blink/renderer/modules/shared_storage/private_aggregation.cc
new file mode 100644
index 0000000..50babf3
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/private_aggregation.cc
@@ -0,0 +1,206 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/shared_storage/private_aggregation.h"
+
+#include <stdint.h>
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/ranges/algorithm.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom-blink.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_debug_mode_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_private_aggregation_histogram_contribution.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h"
+#include "third_party/blink/renderer/modules/shared_storage/util.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+constexpr char kPermissionsPolicyErrorMessage[] =
+    "The \"private-aggregation\" Permissions Policy denied the method on "
+    "privateAggregation";
+
+}  // namespace
+
+PrivateAggregation::PrivateAggregation(
+    SharedStorageWorkletGlobalScope* global_scope)
+    : global_scope_(global_scope) {}
+
+PrivateAggregation::~PrivateAggregation() = default;
+
+void PrivateAggregation::Trace(Visitor* visitor) const {
+  visitor->Trace(global_scope_);
+  visitor->Trace(operation_states_);
+  ScriptWrappable::Trace(visitor);
+}
+
+// TODO(alexmt): Consider merging parsing logic with FLEDGE worklet.
+void PrivateAggregation::sendHistogramReport(
+    ScriptState* script_state,
+    const PrivateAggregationHistogramContribution* contribution,
+    ExceptionState& exception_state) {
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state)) {
+    return;
+  }
+
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  CHECK(execution_context->IsSharedStorageWorkletGlobalScope());
+
+  EnsureUseCountersAreRecorded();
+
+  if (!global_scope_->private_aggregation_permissions_policy_allowed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
+                                      kPermissionsPolicyErrorMessage);
+    return;
+  }
+
+  absl::optional<absl::uint128> bucket = contribution->bucket().ToUInt128();
+  if (!bucket) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kDataError,
+        "contribution['bucket'] is negative or does not fit in 128 bits");
+    return;
+  }
+
+  int32_t value = contribution->value();
+  if (value < 0) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
+                                      "contribution['value'] is negative");
+    return;
+  }
+
+  mojom::blink::AggregatableReportHistogramContributionPtr mojom_contribution =
+      mojom::blink::AggregatableReportHistogramContribution::New(bucket.value(),
+                                                                 value);
+
+  int64_t operation_id = global_scope_->GetCurrentOperationId();
+  CHECK(operation_states_.Contains(operation_id));
+  OperationState* operation_state = operation_states_.at(operation_id);
+
+  operation_state->private_aggregation_contributions.push_back(
+      std::move(mojom_contribution));
+}
+
+void PrivateAggregation::enableDebugMode(ScriptState* script_state,
+                                         ExceptionState& exception_state) {
+  enableDebugMode(script_state, /*options=*/nullptr, exception_state);
+}
+
+void PrivateAggregation::enableDebugMode(
+    ScriptState* script_state,
+    const PrivateAggregationDebugModeOptions* options,
+    ExceptionState& exception_state) {
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state)) {
+    return;
+  }
+
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  CHECK(execution_context->IsSharedStorageWorkletGlobalScope());
+
+  EnsureUseCountersAreRecorded();
+
+  if (!global_scope_->private_aggregation_permissions_policy_allowed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
+                                      kPermissionsPolicyErrorMessage);
+    return;
+  }
+
+  int64_t operation_id = global_scope_->GetCurrentOperationId();
+  CHECK(base::Contains(operation_states_, operation_id));
+  OperationState* operation_state = operation_states_.at(operation_id);
+
+  mojom::blink::DebugModeDetails& debug_mode_details =
+      operation_state->debug_mode_details;
+  if (debug_mode_details.is_enabled) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kDataError,
+        "enableDebugMode may be called at most once");
+    return;
+  }
+
+  // If `options` is not provided, no debug key is set.
+  if (options) {
+    absl::optional<absl::uint128> maybe_debug_key =
+        options->debugKey().ToUInt128();
+
+    if (!maybe_debug_key || absl::Uint128High64(maybe_debug_key.value()) != 0) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kDataError,
+          "options['debug_key'] is negative or does not fit in 64 bits");
+      return;
+    }
+
+    uint64_t debug_key = absl::Uint128Low64(maybe_debug_key.value());
+    debug_mode_details.debug_key = mojom::blink::DebugKey::New(debug_key);
+  }
+
+  debug_mode_details.is_enabled = true;
+}
+
+void PrivateAggregation::OnOperationStarted(int64_t operation_id) {
+  CHECK(!operation_states_.Contains(operation_id));
+  operation_states_.insert(operation_id,
+                           MakeGarbageCollected<OperationState>());
+}
+
+void PrivateAggregation::OnOperationFinished(int64_t operation_id) {
+  CHECK(operation_states_.Contains(operation_id));
+  OperationState* operation_state = operation_states_.at(operation_id);
+
+  if (!operation_state->private_aggregation_contributions.empty()) {
+    global_scope_->GetPrivateAggregationHost()->SendHistogramReport(
+        std::move(operation_state->private_aggregation_contributions),
+        // TODO(alexmt): consider allowing this to be set
+        mojom::blink::AggregationServiceMode::kDefault,
+        operation_state->debug_mode_details.Clone());
+  }
+
+  operation_states_.erase(operation_id);
+}
+
+void PrivateAggregation::OnWorkletDestroyed() {
+  // Ensure any unfinished operations are properly handled.
+  Vector<int64_t> remaining_operation_ids;
+  remaining_operation_ids.reserve(operation_states_.size());
+  base::ranges::transform(operation_states_,
+                          std::back_inserter(remaining_operation_ids),
+                          [](auto& elem) { return elem.key; });
+
+  base::ranges::for_each(remaining_operation_ids, [this](int64_t operation_id) {
+    OnOperationFinished(operation_id);
+  });
+
+  CHECK(operation_states_.empty());
+}
+
+void PrivateAggregation::EnsureUseCountersAreRecorded() {
+  if (!has_recorded_use_counters_) {
+    has_recorded_use_counters_ = true;
+    global_scope_->GetSharedStorageWorkletServiceClient()->RecordUseCounters(
+        {mojom::blink::WebFeature::kPrivateAggregationApiAll,
+         mojom::blink::WebFeature::kPrivateAggregationApiSharedStorage});
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/shared_storage/private_aggregation.h b/third_party/blink/renderer/modules/shared_storage/private_aggregation.h
new file mode 100644
index 0000000..fc0344be
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/private_aggregation.h
@@ -0,0 +1,75 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_PRIVATE_AGGREGATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_PRIVATE_AGGREGATION_H_
+
+#include <stdint.h>
+
+#include "third_party/blink/public/mojom/private_aggregation/aggregatable_report.mojom-blink.h"
+#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom-blink.h"
+#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom-blink-forward.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+
+namespace blink {
+
+class ExceptionState;
+class PrivateAggregationDebugModeOptions;
+class PrivateAggregationHistogramContribution;
+class ScriptState;
+class SharedStorageWorkletGlobalScope;
+
+class MODULES_EXPORT PrivateAggregation final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  struct OperationState : public GarbageCollected<OperationState> {
+    // Defaults to debug mode being disabled.
+    mojom::blink::DebugModeDetails debug_mode_details;
+
+    // Pending contributions
+    Vector<mojom::blink::AggregatableReportHistogramContributionPtr>
+        private_aggregation_contributions;
+
+    void Trace(Visitor* visitor) const {}
+  };
+
+  explicit PrivateAggregation(SharedStorageWorkletGlobalScope* global_scope);
+
+  ~PrivateAggregation() override;
+
+  void Trace(Visitor*) const override;
+
+  // PrivateAggregation IDL
+  void sendHistogramReport(ScriptState*,
+                           const PrivateAggregationHistogramContribution*,
+                           ExceptionState&);
+  void enableDebugMode(ScriptState*, ExceptionState&);
+  void enableDebugMode(ScriptState*,
+                       const PrivateAggregationDebugModeOptions*,
+                       ExceptionState&);
+
+  void OnOperationStarted(int64_t operation_id);
+  void OnOperationFinished(int64_t operation_id);
+
+  void OnWorkletDestroyed();
+
+ private:
+  void EnsureUseCountersAreRecorded();
+
+  bool has_recorded_use_counters_ = false;
+
+  Member<SharedStorageWorkletGlobalScope> global_scope_;
+  HeapHashMap<int64_t,
+              Member<OperationState>,
+              IntWithZeroKeyHashTraits<int64_t>>
+      operation_states_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_PRIVATE_AGGREGATION_H_
diff --git a/third_party/blink/renderer/modules/shared_storage/private_aggregation.idl b/third_party/blink/renderer/modules/shared_storage/private_aggregation.idl
new file mode 100644
index 0000000..3c7e5440
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/private_aggregation.idl
@@ -0,0 +1,18 @@
+// Copyright 2023 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.
+
+[
+  Exposed=(SharedStorageWorklet),
+  ContextEnabled=PrivateAggregationInSharedStorage
+] interface PrivateAggregation {
+  [
+    CallWith=ScriptState,
+    RaisesException
+  ] void sendHistogramReport(PrivateAggregationHistogramContribution contribution);
+
+  [
+    CallWith=ScriptState,
+    RaisesException
+  ] void enableDebugMode(optional PrivateAggregationDebugModeOptions options);
+};
diff --git a/third_party/blink/renderer/modules/shared_storage/private_aggregation_debug_mode_options.idl b/third_party/blink/renderer/modules/shared_storage/private_aggregation_debug_mode_options.idl
new file mode 100644
index 0000000..e3d4f80
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/private_aggregation_debug_mode_options.idl
@@ -0,0 +1,9 @@
+// Copyright 2023 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.
+
+[
+  Exposed=(SharedStorageWorklet)
+] dictionary PrivateAggregationDebugModeOptions {
+  required bigint debug_key;
+};
diff --git a/third_party/blink/renderer/modules/shared_storage/private_aggregation_histogram_contribution.idl b/third_party/blink/renderer/modules/shared_storage/private_aggregation_histogram_contribution.idl
new file mode 100644
index 0000000..d1fa4043
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/private_aggregation_histogram_contribution.idl
@@ -0,0 +1,10 @@
+// Copyright 2023 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.
+
+[
+  Exposed=(SharedStorageWorklet)
+] dictionary PrivateAggregationHistogramContribution {
+  required bigint bucket;
+  required long value;
+};
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.cc
index c373cc9..f9c53b63 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.cc
@@ -4,14 +4,19 @@
 
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h"
 
+#include <stdint.h>
+
 #include <memory>
 #include <utility>
 
 #include "base/check.h"
+#include "base/functional/callback.h"
 #include "gin/converter.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/shared_storage/module_script_downloader.h"
+#include "third_party/blink/public/platform/cross_variant_mojo_util.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
@@ -19,13 +24,20 @@
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_run_function_for_shared_storage_run_operation.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_run_function_for_shared_storage_select_url_operation.h"
+#include "third_party/blink/renderer/core/context_features/context_feature_settings.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h"
+#include "third_party/blink/renderer/modules/shared_storage/private_aggregation.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage_operation_definition.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_thread.h"
 #include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-value.h"
 
 namespace blink {
 
@@ -225,7 +237,13 @@
     : WorkletGlobalScope(std::move(creation_params),
                          thread->GetWorkerReportingProxy(),
                          thread,
-                         /*create_microtask_queue=*/true) {}
+                         /*create_microtask_queue=*/true) {
+  ContextFeatureSettings::From(
+      this, ContextFeatureSettings::CreationMode::kCreateIfNotExists)
+      ->EnablePrivateAggregationInSharedStorage(
+          base::FeatureList::IsEnabled(features::kPrivateAggregationApi) &&
+          features::kPrivateAggregationApiEnabledInSharedStorage.Get());
+}
 
 SharedStorageWorkletGlobalScope::~SharedStorageWorkletGlobalScope() = default;
 
@@ -286,9 +304,36 @@
   WorkerOrWorkletGlobalScope::OnConsoleApiMessage(level, message, location);
 }
 
+void SharedStorageWorkletGlobalScope::NotifyContextDestroyed() {
+  if (private_aggregation_) {
+    CHECK(private_aggregation_host_);
+    private_aggregation_->OnWorkletDestroyed();
+  }
+
+  WorkletGlobalScope::NotifyContextDestroyed();
+}
+
+bool SharedStorageWorkletGlobalScope::FeatureEnabled(
+    OriginTrialFeature feature) const {
+  // The shared storage worklet infrastructure doesn't yet support checking the
+  // origin trial features. We'll go over each feature that can potentially be
+  // checked (e.g. IDL attribute/interface exposures conditioned on
+  // RuntimeEnabled=XXX), and replicate their status manually.
+
+  // The worklet must have been created from a context eligible for shared
+  // storage. It's okay to treat `kSharedStorageAPI` as enabled.
+  if (feature == OriginTrialFeature::kSharedStorageAPI) {
+    return true;
+  }
+
+  NOTREACHED_NORETURN() << "Attempted to check OriginTrialFeature: "
+                        << static_cast<int32_t>(feature);
+}
+
 void SharedStorageWorkletGlobalScope::Trace(Visitor* visitor) const {
   visitor->Trace(receiver_);
   visitor->Trace(shared_storage_);
+  visitor->Trace(private_aggregation_);
   visitor->Trace(operation_definition_map_);
   visitor->Trace(client_);
   visitor->Trace(private_aggregation_host_);
@@ -311,7 +356,9 @@
       private_aggregation_permissions_policy_allowed;
   if (private_aggregation_host) {
     private_aggregation_host_.Bind(
-        std::move(private_aggregation_host),
+        CrossVariantMojoRemote<
+            mojom::blink::PrivateAggregationHostInterfaceBase>(
+            std::move(private_aggregation_host)),
         GetTaskRunner(blink::TaskType::kMiscPlatformAPI));
   }
 
@@ -350,6 +397,11 @@
     return;
   }
 
+  base::OnceClosure operation_completion_cb = StartOperation();
+  mojom::SharedStorageWorkletService::RunURLSelectionOperationCallback
+      combined_operation_completion_cb =
+          std::move(callback).Then(std::move(operation_completion_cb));
+
   DCHECK(operation_definition);
 
   ScriptState* script_state = operation_definition->GetScriptState();
@@ -376,20 +428,21 @@
 
   if (try_catch.HasCaught()) {
     v8::Local<v8::Value> exception = try_catch.Exception();
-    std::move(callback).Run(/*success=*/false,
-                            ExceptionToString(script_state, exception),
-                            /*index=*/0);
+    std::move(combined_operation_completion_cb)
+        .Run(/*success=*/false, ExceptionToString(script_state, exception),
+             /*index=*/0);
     return;
   }
 
   if (result.IsNothing()) {
-    std::move(callback).Run(/*success=*/false, "Internal error.",
-                            /*index=*/0);
+    std::move(combined_operation_completion_cb)
+        .Run(/*success=*/false, "Internal error.",
+             /*index=*/0);
     return;
   }
 
   auto* unresolved_request = MakeGarbageCollected<UnresolvedSelectURLRequest>(
-      urls.size(), std::move(callback));
+      urls.size(), std::move(combined_operation_completion_cb));
 
   ScriptPromise promise = result.FromJust();
 
@@ -416,6 +469,11 @@
     return;
   }
 
+  base::OnceClosure operation_completion_cb = StartOperation();
+  mojom::SharedStorageWorkletService::RunOperationCallback
+      combined_operation_completion_cb =
+          std::move(callback).Then(std::move(operation_completion_cb));
+
   DCHECK(operation_definition);
 
   ScriptState* script_state = operation_definition->GetScriptState();
@@ -437,18 +495,19 @@
 
   if (try_catch.HasCaught()) {
     v8::Local<v8::Value> exception = try_catch.Exception();
-    std::move(callback).Run(/*success=*/false,
-                            ExceptionToString(script_state, exception));
+    std::move(combined_operation_completion_cb)
+        .Run(/*success=*/false, ExceptionToString(script_state, exception));
     return;
   }
 
   if (result.IsNothing()) {
-    std::move(callback).Run(/*success=*/false, "Internal error.");
+    std::move(combined_operation_completion_cb)
+        .Run(/*success=*/false, "Internal error.");
     return;
   }
 
-  auto* unresolved_request =
-      MakeGarbageCollected<UnresolvedRunRequest>(std::move(callback));
+  auto* unresolved_request = MakeGarbageCollected<UnresolvedRunRequest>(
+      std::move(combined_operation_completion_cb));
 
   ScriptPromise promise = result.FromJust();
 
@@ -488,6 +547,44 @@
   return shared_storage_.Get();
 }
 
+PrivateAggregation* SharedStorageWorkletGlobalScope::privateAggregation(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  if (!private_aggregation_host_) {
+    CHECK(!private_aggregation_);
+
+    // This could due to the worklet origin not "potentially trustworthy".
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "privateAggregation cannot be accessed in this worklet.");
+
+    return nullptr;
+  }
+
+  if (!add_module_finished_) {
+    CHECK(!private_aggregation_);
+
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "privateAggregation cannot be accessed during addModule().");
+
+    return nullptr;
+  }
+
+  return GetOrCreatePrivateAggregation();
+}
+
+// Returns the unique ID for the currently running operation.
+int64_t SharedStorageWorkletGlobalScope::GetCurrentOperationId() {
+  ScriptState* script_state = ScriptController()->GetScriptState();
+  DCHECK(script_state);
+
+  v8::Local<v8::Context> context = script_state->GetContext();
+
+  v8::Local<v8::Value> data = context->GetContinuationPreservedEmbedderData();
+  return data.As<v8::BigInt>()->Int64Value();
+}
+
 void SharedStorageWorkletGlobalScope::OnModuleScriptDownloaded(
     const GURL& script_source_url,
     mojom::SharedStorageWorkletService::AddModuleCallback callback,
@@ -582,4 +679,45 @@
   return true;
 }
 
+base::OnceClosure SharedStorageWorkletGlobalScope::StartOperation() {
+  CHECK(add_module_finished_);
+
+  int64_t operation_id = operation_counter_++;
+
+  ScriptState* script_state = ScriptController()->GetScriptState();
+  DCHECK(script_state);
+
+  v8::HandleScope handle_scope(script_state->GetIsolate());
+  v8::Local<v8::Context> context = script_state->GetContext();
+
+  context->SetContinuationPreservedEmbedderData(
+      v8::BigInt::New(context->GetIsolate(), operation_id));
+
+  if (private_aggregation_host_) {
+    GetOrCreatePrivateAggregation()->OnOperationStarted(operation_id);
+  }
+
+  return WTF::BindOnce(&SharedStorageWorkletGlobalScope::FinishOperation,
+                       WrapPersistent(this), operation_id);
+}
+
+void SharedStorageWorkletGlobalScope::FinishOperation(int64_t operation_id) {
+  if (private_aggregation_host_) {
+    CHECK(private_aggregation_);
+    private_aggregation_->OnOperationFinished(operation_id);
+  }
+}
+
+PrivateAggregation*
+SharedStorageWorkletGlobalScope::GetOrCreatePrivateAggregation() {
+  CHECK(private_aggregation_host_);
+  CHECK(add_module_finished_);
+
+  if (!private_aggregation_) {
+    private_aggregation_ = MakeGarbageCollected<PrivateAggregation>(this);
+  }
+
+  return private_aggregation_.Get();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h
index 81c5ecc..01d1595 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.h
@@ -5,6 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_SHARED_STORAGE_WORKLET_GLOBAL_SCOPE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_SHARED_STORAGE_WORKLET_GLOBAL_SCOPE_H_
 
+#include <stdint.h>
+
+#include "base/check.h"
+#include "base/functional/callback_forward.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -12,7 +16,9 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom.h"
 #include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom-blink.h"
 #include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom.h"
@@ -37,6 +43,7 @@
 class SharedStorageOperationDefinition;
 class V8NoArgumentConstructor;
 class SharedStorage;
+class PrivateAggregation;
 
 // mojom::SharedStorageWorkletService implementation. Responsible for
 // handling worklet operations. This object lives on the worklet thread.
@@ -73,6 +80,13 @@
     return token_;
   }
 
+  // Do the finalization jobs (e.g. flush the private aggregation reports). At
+  // the end of this call, `client_` and `private_aggregation_host_` will be
+  // reset, thus we cannot rely on observer method like `Dispose()`.
+  void NotifyContextDestroyed() override;
+
+  bool FeatureEnabled(OriginTrialFeature feature) const override;
+
   void Trace(Visitor*) const override;
 
   // mojom::SharedStorageWorkletService implementation:
@@ -96,17 +110,31 @@
                     const std::vector<uint8_t>& serialized_data,
                     RunOperationCallback callback) override;
 
+  // SharedStorageWorkletGlobalScope IDL
   SharedStorage* sharedStorage(ScriptState*, ExceptionState&);
+  PrivateAggregation* privateAggregation(ScriptState*, ExceptionState&);
+
+  // Returns the unique ID for the currently running operation.
+  int64_t GetCurrentOperationId();
 
   mojom::blink::SharedStorageWorkletServiceClient*
   GetSharedStorageWorkletServiceClient() {
     return client_.get();
   }
 
+  mojom::blink::PrivateAggregationHost* GetPrivateAggregationHost() {
+    CHECK(private_aggregation_host_);
+    return private_aggregation_host_.get();
+  }
+
   const absl::optional<String>& embedder_context() const {
     return embedder_context_;
   }
 
+  bool private_aggregation_permissions_policy_allowed() const {
+    return private_aggregation_permissions_policy_allowed_;
+  }
+
  private:
   void OnModuleScriptDownloaded(
       const GURL& script_source_url,
@@ -135,8 +163,21 @@
     return network::mojom::RequestDestination::kEmpty;
   }
 
+  // Sets continuation-preserved embedder data to allow us to identify this
+  // particular operation invocation later, even after asynchronous operations.
+  // Returns a closure that should be run when the operation finishes.
+  base::OnceClosure StartOperation();
+
+  // Notifies the `private_aggregation_` that the operation with the given ID
+  // has finished.
+  void FinishOperation(int64_t operation_id);
+
+  PrivateAggregation* GetOrCreatePrivateAggregation();
+
   bool add_module_finished_ = false;
 
+  int64_t operation_counter_ = 0;
+
   // `receiver_`'s disconnect handler explicitly deletes the worklet thread
   // object that owns this service, thus deleting `this` upon disconnect. To
   // ensure that the worklet thread object and this service are not leaked,
@@ -156,6 +197,10 @@
   // `sharedStorage`.
   Member<SharedStorage> shared_storage_;
 
+  // The per-global-scope private aggregation object. Created on the first
+  // access of `privateAggregation`.
+  Member<PrivateAggregation> private_aggregation_;
+
   // The map from the registered operation names to their definition.
   HeapHashMap<String, Member<SharedStorageOperationDefinition>>
       operation_definition_map_;
@@ -181,7 +226,8 @@
 
   // No need to be associated as message ordering (relative to shared storage
   // operations) is unimportant.
-  HeapMojoRemote<mojom::PrivateAggregationHost> private_aggregation_host_{this};
+  HeapMojoRemote<mojom::blink::PrivateAggregationHost>
+      private_aggregation_host_{this};
 
   const SharedStorageWorkletToken token_;
 };
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.idl b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.idl
index af82900..dc847b2 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.idl
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_global_scope.idl
@@ -14,6 +14,13 @@
 
   [CallWith=ScriptState, RaisesException]
   readonly attribute SharedStorage sharedStorage;
+
+  [
+    CallWith=ScriptState,
+    RaisesException,
+    ContextEnabled=PrivateAggregationInSharedStorage
+  ]
+  readonly attribute PrivateAggregation privateAggregation;
 };
 
 // TODO(yaoxia): Figure out whether `urls`'s type can be `FrozenArray` instead
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
index 1ee51b2..0a73aee 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_unittest.cc
@@ -2,18 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/check_op.h"
+#include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "gin/array_buffer.h"
 #include "gin/dictionary.h"
 #include "gin/public/isolate_holder.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
+#include "third_party/blink/public/mojom/private_aggregation/aggregatable_report.mojom-blink.h"
+#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage_worklet_messaging_proxy.h"
@@ -197,7 +212,7 @@
 
   void RecordUseCounters(
       const std::vector<blink::mojom::WebFeature>& features) override {
-    NOTREACHED();
+    observed_use_counters_.push_back(features);
   }
 
   std::vector<SetParams> observed_set_params_;
@@ -208,6 +223,7 @@
   size_t observed_length_count_ = 0;
   size_t observed_remaining_budget_count_ = 0;
   std::vector<std::string> observed_console_log_messages_;
+  std::vector<std::vector<blink::mojom::WebFeature>> observed_use_counters_;
 
   // Default results to be returned for corresponding operations. They can be
   // overridden.
@@ -224,13 +240,34 @@
       receiver_{this};
 };
 
+class MockMojomPrivateAggregationHost
+    : public blink::mojom::blink::PrivateAggregationHost {
+ public:
+  explicit MockMojomPrivateAggregationHost(
+      mojo::PendingReceiver<blink::mojom::blink::PrivateAggregationHost>
+          receiver)
+      : receiver_(this, std::move(receiver)) {}
+
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
+  // blink::mojom::blink::PrivateAggregationHost:
+  MOCK_METHOD(
+      void,
+      SendHistogramReport,
+      (Vector<blink::mojom::blink::AggregatableReportHistogramContributionPtr>,
+       blink::mojom::blink::AggregationServiceMode,
+       blink::mojom::blink::DebugModeDetailsPtr),
+      (override));
+
+ private:
+  mojo::Receiver<blink::mojom::blink::PrivateAggregationHost> receiver_{this};
+};
+
 }  // namespace
 
 class SharedStorageWorkletTest : public testing::Test {
  public:
-  SharedStorageWorkletTest() {
-    WebRuntimeFeatures::EnableSharedStorageAPI(true);
-  }
+  SharedStorageWorkletTest() = default;
 
   AddModuleResult AddModule(const std::string& script_content,
                             std::string mime_type = "application/javascript") {
@@ -287,10 +324,14 @@
   Persistent<SharedStorageWorkletMessagingProxy> messaging_proxy_;
 
   absl::optional<std::u16string> embedder_context_;
+  bool create_private_aggregation_host_ = true;
+  bool private_aggregation_permissions_policy_allowed_ = true;
 
   base::test::TestFuture<void> worklet_terminated_future_;
 
   std::unique_ptr<TestClient> test_client_;
+  std::unique_ptr<MockMojomPrivateAggregationHost>
+      mock_private_aggregation_host_;
 
   base::HistogramTester histogram_tester_;
 
@@ -310,16 +351,34 @@
         worklet_terminated_future_.GetCallback());
 
     mojo::PendingAssociatedRemote<mojom::SharedStorageWorkletServiceClient>
-        pending_remote;
+        pending_shared_storage_service_client_remote;
     mojo::PendingAssociatedReceiver<mojom::SharedStorageWorkletServiceClient>
-        pending_receiver = pending_remote.InitWithNewEndpointAndPassReceiver();
+        pending_shared_storage_service_client_receiver =
+            pending_shared_storage_service_client_remote
+                .InitWithNewEndpointAndPassReceiver();
 
-    test_client_ = std::make_unique<TestClient>(std::move(pending_receiver));
+    test_client_ = std::make_unique<TestClient>(
+        std::move(pending_shared_storage_service_client_receiver));
+
+    mojo::PendingRemote<mojom::blink::PrivateAggregationHost>
+        pending_pa_host_remote;
+
+    if (create_private_aggregation_host_) {
+      mojo::PendingReceiver<mojom::blink::PrivateAggregationHost>
+          pending_pa_host_receiver =
+              pending_pa_host_remote.InitWithNewPipeAndPassReceiver();
+
+      mock_private_aggregation_host_ =
+          std::make_unique<MockMojomPrivateAggregationHost>(
+              std::move(pending_pa_host_receiver));
+    }
 
     shared_storage_worklet_service_->Initialize(
-        std::move(pending_remote),
-        /*private_aggregation_permissions_policy_allowed=*/true,
-        mojo::PendingRemote<mojom::PrivateAggregationHost>(),
+        std::move(pending_shared_storage_service_client_remote),
+        private_aggregation_permissions_policy_allowed_,
+        CrossVariantMojoRemote<
+            mojom::blink::PrivateAggregationHostInterfaceBase>(
+            std::move(pending_pa_host_remote)),
         embedder_context_);
 
     worklet_service_initialized_ = true;
@@ -384,13 +443,16 @@
     ];
 
     var expectedFunctions = [
+      "SharedStorage",
       "register",
-      "console.log",
+      "console.log"
     ];
 
-    // `privateAggregation` is not implemented yet.
     var expectedUndefinedVariables = [
-      "privateAggregation"
+      // PrivateAggregation related variables are undefined because the
+      // corresponding base::Feature(s) are not enabled.
+      "privateAggregation",
+      "PrivateAggregation"
     ];
 
     for (let expectedObject of expectedObjects) {
@@ -1054,10 +1116,11 @@
       class TestClass {
         async run() {
           var expectedObjects = [
-            "sharedStorage"
+            "sharedStorage",
           ];
 
           var expectedFunctions = [
+            "SharedStorage",
             "register",
             "sharedStorage.set",
             "sharedStorage.append",
@@ -1076,7 +1139,11 @@
             "sharedStorage.context",
             "sharedStorage.keys",
             "sharedStorage.entries",
-            "privateAggregation"
+
+            // PrivateAggregation related variables are undefined because the
+            // corresponding base::Feature(s) are not enabled.
+            "privateAggregation",
+            "PrivateAggregation"
           ];
 
           for (let expectedObject of expectedObjects) {
@@ -1120,6 +1187,7 @@
           ];
 
           var expectedFunctions = [
+            "SharedStorage",
             "register",
             "sharedStorage.set",
             "sharedStorage.append",
@@ -1138,7 +1206,11 @@
             "sharedStorage.context",
             "sharedStorage.keys",
             "sharedStorage.entries",
-            "privateAggregation"
+
+            // PrivateAggregation related variables are undefined because the
+            // corresponding base::Feature(s) are not enabled.
+            "privateAggregation",
+            "PrivateAggregation"
           ];
 
           for (let expectedObject of expectedObjects) {
@@ -2152,4 +2224,443 @@
   EXPECT_TRUE(run_result.error_message.empty());
 }
 
+class SharedStoragePrivateAggregationTest : public SharedStorageWorkletTest {
+ public:
+  SharedStoragePrivateAggregationTest() {
+    private_aggregation_feature_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{blink::features::kPrivateAggregationApi,
+          {{"enabled_in_shared_storage", "true"}}}},
+        /*disabled_features=*/{});
+  }
+
+  // `error_message` being `nullptr` indicates no error is expected.
+  void ExecuteScriptAndValidateContribution(
+      const std::string& script_body,
+      absl::uint128 expected_bucket,
+      int expected_value,
+      mojom::blink::DebugModeDetailsPtr expected_debug_mode_details =
+          mojom::blink::DebugModeDetails::New(),
+      std::string* error_message = nullptr) {
+    AddModuleResult add_module_result =
+        AddModule(/*script_content=*/base::StrCat(
+            {"class TestClass { async run() {", script_body,
+             "}}; register(\"test-operation\", TestClass);"}));
+
+    EXPECT_CALL(*mock_private_aggregation_host_, SendHistogramReport)
+        .WillOnce(testing::Invoke(
+            [&](Vector<blink::mojom::blink::
+                           AggregatableReportHistogramContributionPtr>
+                    contributions,
+                blink::mojom::blink::AggregationServiceMode aggregation_mode,
+                mojom::blink::DebugModeDetailsPtr debug_mode_details) {
+              ASSERT_EQ(contributions.size(), 1u);
+              EXPECT_EQ(contributions[0]->bucket, expected_bucket);
+              EXPECT_EQ(contributions[0]->value, expected_value);
+              EXPECT_EQ(aggregation_mode,
+                        blink::mojom::blink::AggregationServiceMode::kDefault);
+              EXPECT_TRUE(debug_mode_details == expected_debug_mode_details);
+            }));
+
+    RunResult run_result = Run("test-operation", /*serialized_data=*/{});
+
+    EXPECT_EQ(run_result.success, (error_message == nullptr));
+
+    if (error_message != nullptr) {
+      *error_message = run_result.error_message;
+    }
+
+    // Use counters are recorded.
+    EXPECT_EQ(test_client_->observed_use_counters_.size(), 1u);
+    EXPECT_THAT(
+        test_client_->observed_use_counters_[0],
+        testing::UnorderedElementsAre(
+            blink::mojom::WebFeature::kPrivateAggregationApiAll,
+            blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage));
+
+    mock_private_aggregation_host_->FlushForTesting();
+  }
+
+  std::string ExecuteScriptReturningError(
+      const std::string& script_body,
+      bool expect_use_counter,
+      bool create_private_aggregation_host = true) {
+    create_private_aggregation_host_ = create_private_aggregation_host;
+
+    AddModuleResult add_module_result =
+        AddModule(/*script_content=*/base::StrCat(
+            {"class TestClass { async run() {", script_body,
+             "}}; register(\"test-operation\", TestClass);"}));
+
+    CHECK_EQ(create_private_aggregation_host_,
+             !!mock_private_aggregation_host_);
+
+    if (mock_private_aggregation_host_) {
+      EXPECT_CALL(*mock_private_aggregation_host_, SendHistogramReport)
+          .Times(0);
+    }
+
+    RunResult run_result = Run("test-operation", /*serialized_data=*/{});
+    EXPECT_FALSE(run_result.success);
+
+    if (expect_use_counter) {
+      EXPECT_EQ(test_client_->observed_use_counters_.size(), 1u);
+      EXPECT_THAT(
+          test_client_->observed_use_counters_[0],
+          testing::UnorderedElementsAre(
+              blink::mojom::WebFeature::kPrivateAggregationApiAll,
+              blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage));
+    } else {
+      EXPECT_EQ(test_client_->observed_use_counters_.size(), 0u);
+    }
+
+    if (mock_private_aggregation_host_) {
+      mock_private_aggregation_host_->FlushForTesting();
+    }
+
+    return run_result.error_message;
+  }
+
+ private:
+  base::test::ScopedFeatureList private_aggregation_feature_;
+};
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       InterfaceAndObjectExposure_DuringAddModule) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+    // This will succeed.
+    PrivateAggregation;
+
+    // This will fail.
+    privateAggregation;
+  )");
+
+  EXPECT_FALSE(add_module_result.success);
+  EXPECT_THAT(add_module_result.error_message,
+              testing::HasSubstr(
+                  "privateAggregation cannot be accessed during addModule()"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       InterfaceAndObjectExposure_AfterAddModuleSuccess) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+      class TestClass {
+        async run() {
+          PrivateAggregation;
+          privateAggregation;
+        }
+      }
+
+      register("test-operation", TestClass);
+  )");
+
+  EXPECT_TRUE(add_module_result.success);
+
+  RunResult run_result = Run("test-operation", /*serialized_data=*/{});
+
+  EXPECT_TRUE(run_result.success);
+  EXPECT_EQ(run_result.error_message, "");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       InterfaceAndObjectExposure_AfterAddModuleFailure) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+      class TestClass {
+        async run() {
+          PrivateAggregation;
+          privateAggregation;
+        }
+      }
+
+      register("test-operation", TestClass);
+
+      // This should fail the addModule()
+      a;
+  )");
+
+  EXPECT_FALSE(add_module_result.success);
+
+  RunResult run_result = Run("test-operation", /*serialized_data=*/{});
+
+  EXPECT_TRUE(run_result.success);
+  EXPECT_EQ(run_result.error_message, "");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, BasicTest) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: 2});",
+      /*expected_bucket=*/1, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, ZeroBucket) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 0n, value: 2});",
+      /*expected_bucket=*/0, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, ZeroValue) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1n, 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, NonIntegerValue) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: 2.3});",
+      /*expected_bucket=*/1, /*expected_value=*/2);
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       PrivateAggregationHostNotCreated_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: 2});",
+      /*expect_use_counter=*/false,
+      /*create_private_aggregation_host=*/false);
+
+  EXPECT_THAT(error_str,
+              testing::HasSubstr(
+                  "privateAggregation cannot be accessed in this worklet"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       PrivateAggregationPermissionsPolicyNotAllowed_Rejected) {
+  private_aggregation_permissions_policy_allowed_ = false;
+
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: 2});",
+      /*expect_use_counter=*/true);
+
+  EXPECT_THAT(error_str, testing::HasSubstr(
+                             "The \"private-aggregation\" Permissions Policy "
+                             "denied the method on privateAggregation"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, TooLargeBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "340282366920938463463374607431768211456n, value: 2});",
+      /*expect_use_counter=*/true);
+
+  EXPECT_THAT(
+      error_str,
+      testing::HasSubstr(
+          "contribution['bucket'] is negative or does not fit in 128 bits"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: "
+      "-1n, value: 2});",
+      /*expect_use_counter=*/true);
+
+  EXPECT_THAT(
+      error_str,
+      testing::HasSubstr(
+          "contribution['bucket'] is negative or does not fit in 128 bits"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NonBigIntBucket_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 2});",
+      /*expect_use_counter=*/false);
+
+  EXPECT_THAT(error_str,
+              testing::HasSubstr("The provided value is not a BigInt"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeValue_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.sendHistogramReport({bucket: 1n, value: -1});",
+      /*expect_use_counter=*/true);
+
+  EXPECT_THAT(error_str,
+              testing::HasSubstr("contribution['value'] is negative"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       InvalidEnableDebugModeArgument_Rejected) {
+  // The debug key is not wrapped in a dictionary.
+  std::string error_str =
+      ExecuteScriptReturningError("privateAggregation.enableDebugMode(1234n);",
+                                  /*expect_use_counter=*/false);
+
+  EXPECT_THAT(error_str,
+              testing::HasSubstr("The provided value is not of type "
+                                 "'PrivateAggregationDebugModeOptions'"));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       EnableDebugModeCalledTwice_SecondCallFails) {
+  std::string error_str;
+
+  // Note that the first call still applies to future requests if the error is
+  // caught. Here, we rethrow it to check its value.
+  ExecuteScriptAndValidateContribution(
+      R"(
+        let error;
+        try {
+          privateAggregation.enableDebugMode({debug_key: 1234n});
+          privateAggregation.enableDebugMode();
+        } catch (e) {
+          error = e;
+        }
+        privateAggregation.sendHistogramReport({bucket: 1n, value: 2});
+        throw error;
+      )",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      mojom::blink::DebugModeDetails::New(
+          /*is_enabled=*/true,
+          /*debug_key=*/mojom::blink::DebugKey::New(1234u)),
+      &error_str);
+
+  EXPECT_THAT(error_str,
+              testing::HasSubstr("enableDebugMode may be called at most once"));
+}
+
+// Note that FLEDGE worklets have different behavior in this case.
+TEST_F(SharedStoragePrivateAggregationTest,
+       EnableDebugModeCalledAfterRequest_DoesntApply) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+      class SendHistogramReport {
+        async run() {
+          privateAggregation.sendHistogramReport({bucket: 1n, value: 2});
+        }
+      }
+
+      class EnableDebugMode {
+        async run() {
+          privateAggregation.enableDebugMode({debug_key: 1234n});
+        }
+      }
+
+      register("send-histogram-report", SendHistogramReport);
+      register("enable-debug-mode", EnableDebugMode);
+  )");
+
+  EXPECT_CALL(*mock_private_aggregation_host_, SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [&](Vector<blink::mojom::blink::
+                         AggregatableReportHistogramContributionPtr>
+                  contributions,
+              blink::mojom::blink::AggregationServiceMode aggregation_mode,
+              mojom::blink::DebugModeDetailsPtr debug_mode_details) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(aggregation_mode,
+                      blink::mojom::blink::AggregationServiceMode::kDefault);
+            EXPECT_TRUE(debug_mode_details ==
+                        mojom::blink::DebugModeDetails::New());
+          }));
+
+  RunResult run_result = Run("send-histogram-report", /*serialized_data=*/{});
+  EXPECT_TRUE(run_result.success);
+
+  RunResult run_result2 = Run("enable-debug-mode", /*serialized_data=*/{});
+  EXPECT_TRUE(run_result2.success);
+
+  mock_private_aggregation_host_->FlushForTesting();
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, MultipleDebugModeRequests) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+      class TestClass {
+        async run() {
+          privateAggregation.enableDebugMode({debug_key: 1234n});
+          privateAggregation.sendHistogramReport({bucket: 1n, value: 2});
+          privateAggregation.sendHistogramReport({bucket: 3n, value: 4});
+        }
+      }
+
+      register("test-operation", TestClass);
+  )");
+
+  EXPECT_CALL(*mock_private_aggregation_host_, SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [](Vector<blink::mojom::blink::
+                        AggregatableReportHistogramContributionPtr>
+                 contributions,
+             blink::mojom::blink::AggregationServiceMode aggregation_mode,
+             mojom::blink::DebugModeDetailsPtr debug_mode_details) {
+            ASSERT_EQ(contributions.size(), 2u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(contributions[1]->bucket, 3);
+            EXPECT_EQ(contributions[1]->value, 4);
+            EXPECT_EQ(aggregation_mode,
+                      blink::mojom::blink::AggregationServiceMode::kDefault);
+            EXPECT_EQ(debug_mode_details,
+                      mojom::blink::DebugModeDetails::New(
+                          /*is_enabled=*/true,
+                          /*debug_key=*/mojom::blink::DebugKey::New(1234u)));
+          }));
+
+  RunResult run_result = Run("test-operation", /*serialized_data=*/{});
+  EXPECT_TRUE(run_result.success);
+
+  mock_private_aggregation_host_->FlushForTesting();
+}
+
+// Regression test for crbug.com/1429895.
+TEST_F(SharedStoragePrivateAggregationTest,
+       GlobalScopeDeletedBeforeOperationCompletes_ContributionsStillFlushed) {
+  AddModuleResult add_module_result = AddModule(/*script_content=*/R"(
+      class TestClass {
+        async run() {
+          privateAggregation.sendHistogramReport({bucket: 1n, value: 2});
+          await new Promise(() => {});
+        }
+      }
+
+      register("test-operation", TestClass);
+  )");
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(*mock_private_aggregation_host_, SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [&](Vector<blink::mojom::blink::
+                         AggregatableReportHistogramContributionPtr>
+                  contributions,
+              blink::mojom::blink::AggregationServiceMode aggregation_mode,
+              mojom::blink::DebugModeDetailsPtr debug_mode_details) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(aggregation_mode,
+                      blink::mojom::blink::AggregationServiceMode::kDefault);
+            EXPECT_FALSE(debug_mode_details->is_enabled);
+
+            run_loop.Quit();
+          }));
+
+  shared_storage_worklet_service_->RunOperation(
+      "test-operation", /*serialized_data=*/{}, base::DoNothing());
+
+  // Trigger the disconnect handler.
+  shared_storage_worklet_service_.reset();
+
+  // Callback called means the worklet has terminated successfully.
+  EXPECT_TRUE(worklet_terminated_future_.Wait());
+
+  run_loop.Run();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/shared_storage/util.cc b/third_party/blink/renderer/modules/shared_storage/util.cc
index 6b9c5e4..6197265f 100644
--- a/third_party/blink/renderer/modules/shared_storage/util.cc
+++ b/third_party/blink/renderer/modules/shared_storage/util.cc
@@ -17,7 +17,14 @@
                                  ExceptionState& exception_state) {
   if (!script_state.ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+                                      "context is not valid");
+    return false;
+  }
+
+  ExecutionContext* execution_context = ExecutionContext::From(&script_state);
+  if (execution_context->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
+                                      "context has been destroyed");
     return false;
   }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index cce8a6ec..a2ba506 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -62,6 +62,7 @@
 #include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_operators.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -89,9 +90,11 @@
 const char kInlineVerticalFOVNotSupported[] =
     "This session does not support inlineVerticalFieldOfView";
 
-const char kAnchorsNotSupportedByDevice[] = "Device does not support anchors!";
+const char kFeatureNotSupportedByDevicePrefix[] =
+    "Device does not support feature ";
 
-const char kHitTestNotSupportedByDevice[] = "Device does not support hit test!";
+const char kFeatureNotSupportedBySessionPrefix[] =
+    "Session does not support feature ";
 
 const char kDeviceDisconnected[] = "The XR device has been disconnected.";
 
@@ -103,19 +106,10 @@
     "The operation was unable to retrieve the native origin from XRSpace and "
     "could not be completed.";
 
-const char kHitTestFeatureNotSupported[] =
-    "Hit test feature is not supported by the session.";
-
 const char kHitTestSubscriptionFailed[] = "Hit test subscription failed.";
 
 const char kAnchorCreationFailed[] = "Anchor creation failed.";
 
-const char kLightEstimationFeatureNotSupported[] =
-    "Light estimation feature is not supported.";
-
-const char kImageTrackingFeatureNotSupported[] =
-    "Image tracking feature is not supported.";
-
 const char kEntityTypesNotSpecified[] =
     "No entityTypes specified: the array cannot be empty!";
 
@@ -702,8 +696,10 @@
 
   // Reject the promise if device doesn't support the anchors API.
   if (!xr_->xrEnvironmentProviderRemote()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      kAnchorsNotSupportedByDevice);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        kFeatureNotSupportedByDevicePrefix +
+            XRSessionFeatureToString(device::mojom::XRSessionFeature::ANCHORS));
     return ScriptPromise();
   }
 
@@ -824,8 +820,11 @@
   DCHECK(options_init);
 
   if (!IsFeatureEnabled(device::mojom::XRSessionFeature::HIT_TEST)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      kHitTestFeatureNotSupported);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        kFeatureNotSupportedBySessionPrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::HIT_TEST));
     return {};
   }
 
@@ -836,8 +835,11 @@
   }
 
   if (!xr_->xrEnvironmentProviderRemote()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      kHitTestNotSupportedByDevice);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        kFeatureNotSupportedByDevicePrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::HIT_TEST));
     return {};
   }
 
@@ -917,8 +919,11 @@
   DCHECK(options_init);
 
   if (!IsFeatureEnabled(device::mojom::XRSessionFeature::HIT_TEST)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      kHitTestFeatureNotSupported);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        kFeatureNotSupportedBySessionPrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::HIT_TEST));
     return {};
   }
 
@@ -929,8 +934,11 @@
   }
 
   if (!xr_->xrEnvironmentProviderRemote()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      kHitTestNotSupportedByDevice);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        kFeatureNotSupportedByDevicePrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::HIT_TEST));
     return {};
   }
 
@@ -1302,8 +1310,11 @@
   }
 
   if (!IsFeatureEnabled(device::mojom::XRSessionFeature::LIGHT_ESTIMATION)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      kLightEstimationFeatureNotSupported);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        kFeatureNotSupportedBySessionPrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::LIGHT_ESTIMATION));
     return ScriptPromise();
   }
 
@@ -1749,8 +1760,11 @@
   }
 
   if (!IsFeatureEnabled(device::mojom::XRSessionFeature::IMAGE_TRACKING)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      kImageTrackingFeatureNotSupported);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        kFeatureNotSupportedBySessionPrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::IMAGE_TRACKING));
     return ScriptPromise();
   }
 
@@ -1808,8 +1822,11 @@
 HeapVector<Member<XRImageTrackingResult>> XRSession::ImageTrackingResults(
     ExceptionState& exception_state) {
   if (!IsFeatureEnabled(device::mojom::XRSessionFeature::IMAGE_TRACKING)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      kImageTrackingFeatureNotSupported);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        kFeatureNotSupportedBySessionPrefix +
+            XRSessionFeatureToString(
+                device::mojom::XRSessionFeature::IMAGE_TRACKING));
     return {};
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 3b201cc1..e6c0d70 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -214,10 +214,15 @@
       !scroll_translation.ScrollNode()->UserScrollableVertical()) {
     return false;
   }
-  if (lcd_text_preference_ != LCDTextPreference::kStronglyPreferred) {
+  auto preference =
+      scroll_translation.ScrollNode()->GetCompositedScrollingPreference();
+  if (preference == CompositedScrollingPreference::kNotPreferred) {
+    return false;
+  }
+  if (preference == CompositedScrollingPreference::kPreferred) {
     return true;
   }
-  if (scroll_translation.ScrollNode()->PrefersCompositedScrolling()) {
+  if (lcd_text_preference_ != LCDTextPreference::kStronglyPreferred) {
     return true;
   }
   // Find the chunk containing the scrolling background which normally defines
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
index 9fd36bd..c7b0273 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
@@ -36,7 +36,8 @@
           other.prevent_viewport_scrolling_from_inner ||
       max_scroll_offset_affected_by_page_scale !=
           other.max_scroll_offset_affected_by_page_scale ||
-      prefers_composited_scrolling != other.prefers_composited_scrolling ||
+      composited_scrolling_preference !=
+          other.composited_scrolling_preference ||
       main_thread_scrolling_reasons != other.main_thread_scrolling_reasons ||
       compositor_element_id != other.compositor_element_id ||
       overscroll_behavior != other.overscroll_behavior ||
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
index fbbfa1bf..1142a7c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -27,6 +27,13 @@
 
 using MainThreadScrollingReasons = uint32_t;
 
+// For CompositeScrollAfterPaint.
+enum class CompositedScrollingPreference : uint8_t {
+  kDefault,
+  kPreferred,
+  kNotPreferred,
+};
+
 // A scroll node contains auxiliary scrolling information which includes how far
 // an area can be scrolled, main thread scrolling reasons, etc. Scroll nodes
 // are referenced by TransformPaintPropertyNodes that are used for the scroll
@@ -65,7 +72,8 @@
 
     bool max_scroll_offset_affected_by_page_scale = false;
     // Used in CompositeScrollAfterPaint.
-    bool prefers_composited_scrolling = false;
+    CompositedScrollingPreference composited_scrolling_preference =
+        CompositedScrollingPreference::kDefault;
     MainThreadScrollingReasons main_thread_scrolling_reasons =
         cc::MainThreadScrollingReason::kNotScrollingOnMain;
     // The scrolling element id is stored directly on the scroll node and not
@@ -155,9 +163,9 @@
   bool MaxScrollOffsetAffectedByPageScale() const {
     return state_.max_scroll_offset_affected_by_page_scale;
   }
-  bool PrefersCompositedScrolling() const {
+  CompositedScrollingPreference GetCompositedScrollingPreference() const {
     DCHECK(RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled());
-    return state_.prefers_composited_scrolling;
+    return state_.composited_scrolling_preference;
   }
 
   // Return reason bitfield with values from cc::MainThreadScrollingReason.
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 01ace06..50cfd26 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3621,6 +3621,13 @@
       status: "experimental",
       base_feature: "none",
     },
+    // https://w3c.github.io/webauthn/#dom-publickeycredential-tojson
+    // https://w3c.github.io/webauthn/#sctn-parseCreationOptionsFromJSON
+    // https://w3c.github.io/webauthn/#sctn-parseRequestOptionsFromJSON
+    {
+      name: "WebAuthenticationJSONSerialization",
+      status: "experimental",
+    },
     {
       name: "WebAuthenticationLargeBlobExtension",
       status: "stable",
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 00bbea02..11e558a 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -544,6 +544,7 @@
             'compositor_target_property::.+',
             'cors::.+',
             'css_parsing_utils::.+',
+            'css_toggle_key_handling::.+',
             'cssvalue::.+',
             'encoding::.+',
             'encoding_enum::.+',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 24e0840..4e05a7e 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6467,6 +6467,8 @@
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_loadeddata.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_playing.html [ Failure Skip ]
 
+crbug.com/1431228 media/video-delay-load-event.html [ Failure Pass ]
+
 # WebAssembly ESM integration is not yet implemented.
 crbug.com/1380485 external/wpt/wasm/webapi/esm-integration/exported-names.tentative.html [ Crash Failure Pass Timeout ]
 crbug.com/1380485 external/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html [ Crash Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/compositing/select-element-expected.txt b/third_party/blink/web_tests/compositing/select-element-expected.txt
new file mode 100644
index 0000000..fd5237c
--- /dev/null
+++ b/third_party/blink/web_tests/compositing/select-element-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL scrollable assert_equals: [object Object] expected 4 but got 1
+PASS not scrollable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/compositing/text-input-element-expected.txt b/third_party/blink/web_tests/compositing/text-input-element-expected.txt
new file mode 100644
index 0000000..9892e26
--- /dev/null
+++ b/third_party/blink/web_tests/compositing/text-input-element-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL scrollable assert_equals: [object Object] expected 3 but got 1
+PASS not scrollable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 459e01a..b05bc2fa 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -1759,6 +1759,13 @@
        {}
       ]
      ],
+     "inline-flexbox-absurd-block-size-crash.html": [
+      "57b119a65be6a47bff4a8165955fd855b5797900",
+      [
+       null,
+       {}
+      ]
+     ],
      "negative-available-size-crash.html": [
       "837fdaeba9ef8baeb4f2bad281bb13551d6d8ff9",
       [
@@ -268917,16 +268924,6 @@
    }
   },
   "support": {
-   ".cache": {
-    "gitignore2.json": [
-     "6a218667848bd203a6a18b493c58a23b487cdf1d",
-     []
-    ],
-    "mtime.json": [
-     "294948de26d5737245726746eb3ca0603345d08e",
-     []
-    ]
-   },
    ".gitignore": [
     "d93e645d547894b50149d3726de2654957b6e06f",
     []
@@ -382187,7 +382184,7 @@
        []
       ],
       "exec.py": [
-       "835a3f07aafa9664f763c5ebd9f10209dc8579d9",
+       "de0636117db6ef6bc7069c6afbae6a99c85eaf8b",
        []
       ],
       "fetch-intercept-worker.js": [
@@ -382375,7 +382372,7 @@
        []
       ],
       "utils.js": [
-       "f3a49f1deb9e92e824ae4103cda189170b82acec",
+       "99c26137886fc0aa573ef40634a762da88b434d7",
        []
       ],
       "wake-lock.https.html": [
@@ -388423,7 +388420,7 @@
      []
     ],
     "idlharness.https.window-expected.txt": [
-     "475986c5e9bf0e0633b5f5d9b5dae13cb68d8c1f",
+     "08236102c52ac47d21258aec7a4bd5f81eb52ac7",
      []
     ],
     "idlharness.https.window.js.ini": [
@@ -455608,10 +455605,12 @@
       ]
      ],
      "toggle-aria-roles.tentative.html": [
-      "7c500fde3cbba749442aec9dc197e464c4affc3c",
+      "9ba545f21a770473e34350e0129e6433f7cf680f",
       [
        null,
-       {}
+       {
+        "testdriver": true
+       }
       ]
      ],
      "toggle-creation.tentative.html": [
@@ -601950,6 +601949,15 @@
        }
       ]
      ],
+     "prefetch.https.html": [
+      "48de5adca13e0aa64312f006531bcb8633aba255",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "referrer-policy-from-rules.html": [
       "b19c5dddacb4b17d99d780231ae38311c39a4062",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/inline-flexbox-absurd-block-size-crash.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/inline-flexbox-absurd-block-size-crash.html
new file mode 100644
index 0000000..57b119a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/inline-flexbox-absurd-block-size-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1826635">
+<style>
+* {
+  flex-flow: wrap-reverse;
+  display: inline-flex;
+  visibility: collapse;
+  border-style: inset;
+}
+</style>
+<div style="padding-bottom: 3959291058%"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html
index 7c500fd..9ba545f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html
@@ -1,14 +1,21 @@
 <!DOCTYPE HTML>
 <meta charset="UTF-8">
-<title>CSS Toggles: ARIA roles</title>
+<title>CSS Toggles: ARIA roles and inferred keyboard handling</title>
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Google" href="http://www.google.com/">
 <link rel="help" href="https://tabatkins.github.io/css-toggle/">
 <link rel="help" href="https://github.com/tabatkins/css-toggle/issues/41">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
 <script src="support/toggle-helpers.js"></script>
-<style id="style"></style>
+<style>
+
+/* for send_keys */
+div { min-height: 10px }
+
+</style>
 
 <body>
 
@@ -18,6 +25,12 @@
 let aria_role_tests = [
   // Markup to create the test assertions:
   //   data-expected-role:  The expected aria role for this element.
+  //   data-expected-trigger-keys:  When present, indicates that keyboard events
+  //     should be tested on this element, and indicates the keys
+  //     (space-separated) that are expected to toggle the toggle.
+  //   data-expected-arrows-between-children:  When present, indicates
+  //     that arrow keys should navigate between the children of this
+  //     element that have the given role.
   //
   // Helper markup to create more markup:
   //   class=group: group the group with the toggle-group property
@@ -51,13 +64,13 @@
   // Radios and radio groups:
   `
     <div class="group" data-expected-role="radiogroup">
-      <div class="root-group trigger" data-expected-role="radio"></div>
+      <div class="root-group trigger" data-expected-role="radio" data-expected-trigger-keys="Space"></div>
     </div>
   `,
   `
-    <div class="group" data-expected-role="radiogroup">
-      <div class="root-group trigger" data-expected-role="radio"></div>
-      <div class="root-group trigger" data-expected-role="radio"></div>
+    <div class="group" data-expected-role="radiogroup" data-expected-arrows-between-children="radio">
+      <div class="root-group trigger" data-expected-role="radio" data-expected-trigger-keys="Space"></div>
+      <div class="root-group trigger" data-expected-role="radio" data-expected-trigger-keys="Space"></div>
     </div>
   `,
   `
@@ -80,38 +93,38 @@
   // Checkboxes and checkbox groups:
   `
     <div>
-      <div class="root trigger" data-expected-role="checkbox"></div>
+      <div class="root trigger" data-expected-role="checkbox" data-expected-trigger-keys="Space"></div>
     </div>
   `,
-  // TODO(dbaron): This is a checkbox group... but we can't distinguish
-  // that with current ARIA roles.
+  // TODO(https://crbug.com/1250716): This is a checkbox group... but we
+  // can't distinguish that with current ARIA roles.
   `
-    <div>
-      <div class="root trigger" data-expected-role="checkbox"></div>
-      <div class="root trigger" data-expected-role="checkbox"></div>
+    <div data-expected-arrows-between-children="checkbox">
+      <div class="root trigger" data-expected-role="checkbox" data-expected-trigger-keys="Space"></div>
+      <div class="root trigger" data-expected-role="checkbox" data-expected-trigger-keys="Space"></div>
     </div>
   `,
 
   // Disclosure:
-  // TODO(dbaron): This is a disclosure... but how is it possible to
-  // distinguish with ARIA roles (compare to next test!)?
+  // TODO(https://crbug.com/1250716): This is a disclosure... but how is
+  // it possible to distinguish with ARIA roles (compare to next test!)?
   `
     <div class="root">
-      <div class="trigger" data-expected-role="button"></div>
+      <div class="trigger" data-expected-role="button" data-expected-trigger-keys="Space Enter"></div>
       <div class="visibility"></div>
     </div>
   `,
   // This is not a disclosure because it has a toggle-group.
   `
     <div class="root-group">
-      <div class="trigger" data-expected-role="button"></div>
+      <div class="trigger" data-expected-role="button" data-expected-trigger-keys="Space Enter"></div>
       <div class="visibility"></div>
     </div>
   `,
   // This is button with popup (absolute positioning)
-  // TODO(dbaron): This test doesn't actually distinguish this from
-  // disclosure because the internal kPopUpButton role maps to "button"
-  // in kReverseRoles in ax_object.cc.
+  // TODO(https://crbug.com/1250716): This test doesn't actually
+  // distinguish this from disclosure because the internal kPopUpButton
+  // role maps to "button" in kReverseRoles in ax_object.cc.
   `
     <div class="root">
       <div class="trigger" data-expected-role="button"></div>
@@ -119,29 +132,29 @@
     </div>
   `,
   // This is button with popup (fixed positioning)
-  // TODO(dbaron): This test doesn't actually distinguish this from
-  // disclosure because the internal kPopUpButton role maps to "button"
-  // in kReverseRoles in ax_object.cc.
+  // TODO(https://crbug.com/1250716): This test doesn't actually
+  // distinguish this from disclosure because the internal kPopUpButton
+  // role maps to "button" in kReverseRoles in ax_object.cc.
   `
     <div class="root">
-      <div class="trigger" data-expected-role="button"></div>
+      <div class="trigger" data-expected-role="button" data-expected-trigger-keys="Space Enter"></div>
       <div class="visibility" style="position: fixed"></div>
     </div>
   `,
   // This is button with popup (popover)
-  // TODO(dbaron): This test doesn't actually distinguish this from
-  // disclosure because the internal kPopUpButton role maps to "button"
-  // in kReverseRoles in ax_object.cc.
+  // TODO(https://crbug.com/1250716): This test doesn't actually
+  // distinguish this from disclosure because the internal kPopUpButton
+  // role maps to "button" in kReverseRoles in ax_object.cc.
   `
     <div class="root">
-      <div class="trigger" data-expected-role="button"></div>
+      <div class="trigger" data-expected-role="button" data-expected-trigger-keys="Space Enter"></div>
       <div class="visibility" popover="auto"></div>
     </div>
   `,
   // This is disclosure (NOT button with popup) (sticky positioning)
   `
     <div class="root">
-      <div class="trigger" data-expected-role="button"></div>
+      <div class="trigger" data-expected-role="button" data-expected-trigger-keys="Space Enter"></div>
       <div class="visibility" style="position: sticky"></div>
     </div>
   `,
@@ -177,17 +190,19 @@
   `,
 
   // Tree:
-  // TODO(dbaron): This should probably also work with the toggles on
-  // the <button>!
-  // TODO(dbaron): This should probably mark the non-interactive items
-  // as treeitem as well!
-  // TODO(dbaron): Do the elements getting the roles here make sense?
-  // TODO(dbaron): The requirement for having multiple disclosure-ish
-  // children to qualify as accordion-ish probably doesn't make sense
-  // here.  The test below is basically the minimal example that gets
-  // detected as a tree, but simpler things definitely should be!
-  // TODO(dbaron): The inner parts of the tree should also be getting
-  // tree roles!
+  // TODO(https://crbug.com/1250716): This should probably also work
+  // with the toggles on the <button>!
+  // TODO(https://crbug.com/1250716): This should probably mark the
+  // non-interactive items as treeitem as well!
+  // TODO(https://crbug.com/1250716): Do the elements getting the roles
+  // here make sense?
+  // TODO(https://crbug.com/1250716): The requirement for having
+  // multiple disclosure-ish children to qualify as accordion-ish
+  // probably doesn't make sense here.  The test below is basically the
+  // minimal example that gets detected as a tree, but simpler things
+  // definitely should be!
+  // TODO(https://crbug.com/1250716): The inner parts of the tree should
+  // also be getting tree roles!
   `
     <ul data-expected-role="tree">
       <li class="root-self" data-expected-role="group">
@@ -234,12 +249,12 @@
 
   // Tabs:
   `
-    <section class="group" data-expected-role="tablist">
-      <h1 class="root-group trigger" data-expected-role="tab"></h1>
+    <section class="group" data-expected-role="tablist" data-expected-arrows-between-children="tab">
+      <h1 class="root-group trigger" data-expected-role="tab" data-expected-trigger-keys="Space Enter"></h1>
       <div class="visibility" data-expected-role="tabpanel"></div>
-      <h1 class="root-group trigger" data-expected-role="tab"></h1>
+      <h1 class="root-group trigger" data-expected-role="tab" data-expected-trigger-keys="Space Enter"></h1>
       <div class="visibility" data-expected-role="tabpanel"></div>
-      <h1 class="root-group trigger" data-expected-role="tab"></h1>
+      <h1 class="root-group trigger" data-expected-role="tab" data-expected-trigger-keys="Space Enter"></h1>
       <div class="visibility" data-expected-role="tabpanel"></div>
     </section>
   `,
@@ -297,6 +312,23 @@
   `,
 ];
 
+function find_toggle_in_scope(e) {
+  let allow_self = true;
+  while (e) {
+    let toggle = e.toggles.get("test-role");
+    if (toggle && (allow_self || toggle.scope == "wide"))
+      return toggle;
+    let sibling = e.previousElementSibling;
+    if (sibling) {
+      e = sibling;
+      allow_self = false;
+    }
+    e = e.parentNode;
+    allow_self = true;
+  }
+  return null;
+}
+
 for (let t of aria_role_tests) {
   promise_test(async function() {
     container.innerHTML = t;
@@ -348,7 +380,74 @@
       assert_equals(e.computedRole, expected_role, `role on ${e.tagName} element (#${count})`);
     }
 
-  }, `aria role test: ${t}`);
+    if (container.querySelector("[data-expected-arrows-between-children], [data-expected-trigger-keys]")) {
+      // We should do keyboard tests for this test.
+      for (let e of container.querySelectorAll("*")) {
+        if (e == container)
+          continue;
+
+        let arrows = e.parentNode.hasAttribute("data-expected-arrows-between-children") && e.getAttribute("data-expected-role") == e.parentNode.getAttribute("data-expected-arrows-between-children");
+        let trigger_keys = [];
+        if (e.hasAttribute("data-expected-trigger-keys")) {
+          trigger_keys = e.getAttribute("data-expected-trigger-keys").split(" ");
+        }
+
+        if (!e.classList.contains("trigger")) {
+          // It is a bug in the test to have expected key handling for elements
+          // that are not toggle triggers.
+          assert_false(arrows);
+          assert_equals(trigger_keys.length, 0);
+          continue;
+        }
+
+        // Test handling of space, enter, and all arrows, and check that
+        // it matches expectations.
+        let keys_to_test = {
+          "Enter": "\uE007",
+          // better known as " ", but "Space" in this test.
+          // https://w3c.github.io/webdriver/#keyboard-actions says
+          // "\uE00D" but that doesn't work.
+          "Space": " ",
+          "ArrowLeft": "\uE012",
+          "ArrowUp": "\uE013",
+          "ArrowRight": "\uE014",
+          "ArrowDown": "\uE015",
+        };
+        for (let key in keys_to_test) {
+          let toggle = find_toggle_in_scope(e);
+          let expected_state = toggle.valueAsNumber;
+          e.focus();
+          let expected_focus = e;
+          assert_equals(document.activeElement, expected_focus, `focus before ${key} key`);
+          await test_driver.send_keys(document.body, keys_to_test[key]);
+          if (trigger_keys.includes(key)) {
+            expected_state = expected_state ? 0 : 1;
+          }
+          if (key.startsWith("Arrow")) {
+            if (arrows) {
+              let role = e.parentNode.getAttribute("data-expected-arrows-between-children");
+              let direction;
+              if (key == "ArrowLeft" || key == "ArrowUp") {
+                direction = "previousElementSibling";
+              } else {
+                direction = "nextElementSibling";
+              }
+              let new_element = e;
+              while ((new_element = new_element[direction])) {
+                if (new_element.getAttribute("data-expected-role") == role)
+                  break;
+              }
+              if (new_element)
+                expected_focus = new_element;
+            }
+          }
+          assert_equals(toggle.valueAsNumber, expected_state, `state after ${key} key`);
+          assert_equals(document.activeElement, expected_focus, `focus after ${key} key`);
+        }
+      }
+    }
+
+  }, `aria role and key handling test: ${t}`);
 }
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-light-dismiss-invalidation.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-light-dismiss-invalidation.tentative.html
new file mode 100644
index 0000000..dfbab93
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-light-dismiss-invalidation.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="http://crbug.com/1429839">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectmenu id=selectmenu>
+  <option id=optone>one</option>
+  <option id=opttwo>two</option>
+</selectmenu>
+<style>
+selectmenu {
+  background-color: rgb(0, 0, 255);
+}
+selectmenu:closed {
+  background-color: rgb(0, 255, 0);
+}
+selectmenu:open {
+  background-color: rgb(255, 0, 0);
+}
+</style>
+<button id=button>hello world</button>
+
+<script>
+promise_test(async () => {
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(0, 255, 0)',
+    'The selectmenu should match :closed at the start of the test.');
+
+  await test_driver.click(selectmenu);
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(255, 0, 0)',
+    'The selectmenu should match :open when opened.');
+
+  await test_driver.click(opttwo);
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(0, 255, 0)',
+    'The selectmenu should match :closed after clicking an option.');
+
+  await test_driver.click(selectmenu);
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(255, 0, 0)',
+    'The selectmenu should match :open when reopened.');
+
+  await test_driver.click(button);
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(0, 255, 0)',
+    'The selectmenu should match :closed after light dismiss.');
+}, 'selectmenu should not match :open when light dismissed.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scheduler/task-signal-any-priority.tentative.any.js b/third_party/blink/web_tests/external/wpt/scheduler/task-signal-any-priority.tentative.any.js
index 99a7dd6..46af80b 100644
--- a/third_party/blink/web_tests/external/wpt/scheduler/task-signal-any-priority.tentative.any.js
+++ b/third_party/blink/web_tests/external/wpt/scheduler/task-signal-any-priority.tentative.any.js
@@ -184,3 +184,30 @@
   controller.setPriority('background');
   assert_true(fired);
 }, "TaskSignal.any() propagates priority after returning an aborted signal");
+
+test((t) => {
+  // Add a dependent in the initial event dispatch stage.
+  let controller = new TaskController();
+  let fired = false;
+  controller.signal.onprioritychange = t.step_func(() => {
+    fired = true;
+    const newSignal = TaskSignal.any([], {priority: controller.signal});
+    assert_equals(newSignal.priority, 'background');
+    newSignal.onprioritychange = t.unreached_func('onprioritychange called');
+  });
+  controller.setPriority('background');
+  assert_true(fired);
+
+  // Add a dependent while signaling prioritychange on dependents.
+  fired = false;
+  controller = new TaskController();
+  const signal = TaskSignal.any([], {priority: controller.signal});
+  signal.onprioritychange = t.step_func(() => {
+    fired = true;
+    const newSignal = TaskSignal.any([], {priority: signal});
+    assert_equals(newSignal.priority, 'background');
+    newSignal.onprioritychange = t.unreached_func('onprioritychange called');
+  });
+  controller.setPriority('background');
+  assert_true(fired);
+}, "TaskSignal.any() does not fire prioritychange for dependents added during prioritychange");
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/idlharness.https.window-expected.txt
index 475986c5..08236102 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 87 tests; 83 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 87 tests; 84 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Partial dictionary CredentialCreationOptions: original dictionary defined
@@ -55,7 +55,7 @@
 PASS PublicKeyCredential interface: attribute authenticatorAttachment
 PASS PublicKeyCredential interface: operation getClientExtensionResults()
 PASS PublicKeyCredential interface: operation isConditionalMediationAvailable()
-FAIL PublicKeyCredential interface: operation toJSON() assert_own_property: interface prototype object missing non-static operation expected property "toJSON" missing
+PASS PublicKeyCredential interface: operation toJSON()
 PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
 FAIL PublicKeyCredential interface: operation parseCreationOptionsFromJSON(PublicKeyCredentialCreationOptionsJSON) assert_own_property: interface object missing static operation expected property "parseCreationOptionsFromJSON" missing
 FAIL PublicKeyCredential interface: operation parseRequestOptionsFromJSON(PublicKeyCredentialRequestOptionsJSON) assert_own_property: interface object missing static operation expected property "parseRequestOptionsFromJSON" missing
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png
index 2583671..05249f1e 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png
index 06acb4e..906ae22 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..465c136
--- /dev/null
+++ b/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -0,0 +1,57 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutNGView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [7, 7, 67, 24]
+      ]
+    },
+    {
+      "name": "LayoutNGTextControlInnerEditor DIV",
+      "bounds": [57, 16],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGTextControlInnerEditor DIV",
+      "bounds": [74, 16],
+      "invalidations": [
+        [0, 0, 74, 16]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
+      "position": [7, 7],
+      "bounds": [67, 24],
+      "invalidations": [
+        [0, 0, 67, 24]
+      ]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 11, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-17, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 8e7f1da..cb66a88a 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutNGTextControlSingleLine INPUT id='root'",
       "position": [-1, -1],
       "bounds": [67, 24],
+      "contentsOpaqueForText": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
         [0, 0, 67, 24]
@@ -17,20 +18,6 @@
       "transform": 1
     },
     {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [57, 16],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [74, 16],
-      "invalidations": [
-        [0, 0, 74, 16]
-      ],
-      "transform": 3
-    },
-    {
       "name": "Caret",
       "position": [56, 0],
       "bounds": [1, 16],
@@ -69,16 +56,6 @@
         [0, 0, 1, 0],
         [4, 3, 0, 1]
       ]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-17, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
index 1127fe6..b9066c4 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 465c136..ade684d 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -8,49 +8,6 @@
       "invalidations": [
         [7, 7, 67, 24]
       ]
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [57, 16],
-      "drawsContent": false,
-      "transform": 1
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [74, 16],
-      "invalidations": [
-        [0, 0, 74, 16]
-      ],
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
-      "position": [7, 7],
-      "bounds": [67, 24],
-      "invalidations": [
-        [0, 0, 67, 24]
-      ]
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 11, 0, 1]
-      ]
-    },
-    {
-      "id": 2,
-      "parent": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-17, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png
deleted file mode 100644
index 0ccecb0..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png
deleted file mode 100644
index 3b2cbcd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png
index ecd1c69..f5cf310 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
index 692175e..5366fc5 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png
index ecd1c69..f5cf310 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
index 692175e..5366fc5 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png
index ecd1c69..f5cf310 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
index 692175e..5366fc5 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png
index 68c1ee31..cd7ebc1 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
index 5ad0792..a6372d3 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png
index ee3ff37..3b2cbcd 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png
index eb4bb3c9..27bb60b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png
index 33893c95..753b8dc 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png
index 683e496..e79b3e3 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png
index 44d9b79..51422d6 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png
index 4699800..bbc04aa 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png
index ac74ed9c..dafa474 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png
index bbf2cae5..9d850842 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png b/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png
index da974862..55da340 100644
--- a/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
index 800b3a4b..c6d15230 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -7,17 +7,6 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [20, 15],
-      "drawsContent": false,
-      "transform": 1
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [168, 15],
-      "transform": 2
-    },
-    {
       "name": "Caret",
       "position": [16, 0],
       "bounds": [1, 15],
@@ -42,16 +31,6 @@
         [0, 0, 1, 0],
         [391, 11, 0, 1]
       ]
-    },
-    {
-      "id": 2,
-      "parent": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-148, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 89c1343..56fc61f 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutNGTextControlSingleLine INPUT id='root'",
       "position": [-1, -1],
       "bounds": [50, 23],
+      "contentsOpaqueForText": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
         [0, 0, 50, 23]
@@ -17,20 +18,6 @@
       "transform": 1
     },
     {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [40, 15],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [72, 15],
-      "invalidations": [
-        [0, 0, 72, 15]
-      ],
-      "transform": 3
-    },
-    {
       "name": "Caret",
       "position": [38, 0],
       "bounds": [1, 15],
@@ -69,16 +56,6 @@
         [0, 0, 1, 0],
         [4, 3, 0, 1]
       ]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-32, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 367b49a..0d9f17c5 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -8,49 +8,6 @@
       "invalidations": [
         [7, 7, 50, 23]
       ]
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [40, 15],
-      "drawsContent": false,
-      "transform": 1
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [72, 15],
-      "invalidations": [
-        [0, 0, 72, 15]
-      ],
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
-      "position": [7, 7],
-      "bounds": [50, 23],
-      "invalidations": [
-        [0, 0, 50, 23]
-      ]
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 11, 0, 1]
-      ]
-    },
-    {
-      "id": 2,
-      "parent": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-32, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png
index e09af38..29392b2 100644
--- a/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
index 8ab891b..b602db9 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
index db35eba..59581e1 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index c08c049..18826eb5 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutNGTextControlSingleLine INPUT id='root'",
       "position": [-1, -1],
       "bounds": [74, 24],
+      "contentsOpaqueForText": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
         [0, 0, 74, 24]
@@ -17,20 +18,6 @@
       "transform": 1
     },
     {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [64, 16],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [74, 16],
-      "invalidations": [
-        [0, 0, 74, 16]
-      ],
-      "transform": 3
-    },
-    {
       "name": "Caret",
       "position": [63, 0],
       "bounds": [1, 16],
@@ -69,16 +56,6 @@
         [0, 0, 1, 0],
         [4, 3, 0, 1]
       ]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-10, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 2b4686a0..16df580 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -8,49 +8,6 @@
       "invalidations": [
         [7, 7, 74, 24]
       ]
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [64, 16],
-      "drawsContent": false,
-      "transform": 1
-    },
-    {
-      "name": "LayoutNGTextControlInnerEditor DIV",
-      "bounds": [74, 16],
-      "invalidations": [
-        [0, 0, 74, 16]
-      ],
-      "transform": 2
-    },
-    {
-      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
-      "position": [7, 7],
-      "bounds": [74, 24],
-      "invalidations": [
-        [0, 0, 74, 24]
-      ]
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 11, 0, 1]
-      ]
-    },
-    {
-      "id": 2,
-      "parent": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [-10, 0, 0, 1]
-      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/virtual/composite-scroll-after-paint/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/virtual/composite-scroll-after-paint/scrollbars/listbox-scrollbar-combinations-expected.png
index 0ccecb0..c85c462e 100644
--- a/third_party/blink/web_tests/virtual/composite-scroll-after-paint/scrollbars/listbox-scrollbar-combinations-expected.png
+++ b/third_party/blink/web_tests/virtual/composite-scroll-after-paint/scrollbars/listbox-scrollbar-combinations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index b24c7be..fb0b920 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7190,6 +7190,7 @@
     getter response
     method constructor
     method getClientExtensionResults
+    method toJSON
 interface PushManager
     static getter supportedContentEncodings
     attribute @@toStringTag
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 395e14f..8191d9d 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-0-65-gb0a4f9927
-Revision: b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6
+Version: VER-2-13-0-67-g1a4c18f7c
+Revision: 1a4c18f7cb70a2ae4fa209bb75a6c6c5b6ace0f2
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/tools/checkteamtags/OWNERS b/tools/checkteamtags/OWNERS
index 7091274..e69de29 100644
--- a/tools/checkteamtags/OWNERS
+++ b/tools/checkteamtags/OWNERS
@@ -1 +0,0 @@
-robertocn@chromium.org
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a899d66..8d7e7efc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -59408,6 +59408,7 @@
   <int value="-1384426209" label="SharingDeviceRegistration:disabled"/>
   <int value="-1383674566" label="EnableProtoApiForClassifyUrl:enabled"/>
   <int value="-1383597259" label="SyncUserConsentSeparateType:disabled"/>
+  <int value="-1383226125" label="Cr2023MacFontSmoothing:enabled"/>
   <int value="-1383145700"
       label="AutofillDoNotMigrateUnsupportedLocalCards:enabled"/>
   <int value="-1382918690" label="VirtualKeyboardApi:enabled"/>
@@ -62966,6 +62967,7 @@
   <int value="559695461" label="UseChimeAndroidSdk:enabled"/>
   <int value="559903013"
       label="AutofillEnableMerchantOptOutErrorDialog:enabled"/>
+  <int value="560062958" label="Cr2023MacFontSmoothing:disabled"/>
   <int value="560369327" label="LauncherImageSearch:disabled"/>
   <int value="560953355"
       label="ExtractRelatedSearchesFromPrefetchedZPSResponse:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index facf6f9..1b21817 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -2011,8 +2011,8 @@
 </histogram>
 
 <histogram name="Android.ModalDialog.SecurityFilteredTouchResult"
-    enum="SecurityFilteredTouchResult" expires_after="2022-04-01">
-  <owner>pavely@chromium.org</owner>
+    enum="SecurityFilteredTouchResult" expires_after="2023-09-01">
+  <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
     Records the action taken on a security sensitive touch event in modal
diff --git a/tools/metrics/histograms/metadata/blink/OWNERS b/tools/metrics/histograms/metadata/blink/OWNERS
index d316e1a..7fb5433b 100644
--- a/tools/metrics/histograms/metadata/blink/OWNERS
+++ b/tools/metrics/histograms/metadata/blink/OWNERS
@@ -3,7 +3,6 @@
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
 iclelland@chromium.org
-schenney@chromium.org
 # For BlinkGC.* and related changes.
 mlippautz@chromium.org
 toyoshim@chromium.org
diff --git a/tools/resultdb/OWNERS b/tools/resultdb/OWNERS
index 4e64a19..f4e1711e 100644
--- a/tools/resultdb/OWNERS
+++ b/tools/resultdb/OWNERS
@@ -1,2 +1 @@
 chanli@chromium.org
-robertocn@chromium.org
diff --git a/ui/accessibility/android/OWNERS b/ui/accessibility/android/OWNERS
index a3bf78a..c2c9d20 100644
--- a/ui/accessibility/android/OWNERS
+++ b/ui/accessibility/android/OWNERS
@@ -1,4 +1,3 @@
 aldietz@google.com
 dtseng@chromium.org
-jacklynch@google.com
 mschillaci@google.com
diff --git a/ui/accessibility/ax_node_data_unittest.cc b/ui/accessibility/ax_node_data_unittest.cc
index 630950a..41610f4 100644
--- a/ui/accessibility/ax_node_data_unittest.cc
+++ b/ui/accessibility/ax_node_data_unittest.cc
@@ -452,7 +452,7 @@
   EXPECT_DCHECK_DEATH(data.SetDescription(""));
 }
 
-TEST(AXNodeDataTest, BitFieldsSanityCheck) {
+TEST(AXNodeDataTest, BitFieldsConfidenceCheck) {
   EXPECT_LT(static_cast<size_t>(ax::mojom::State::kMaxValue),
             sizeof(AXNodeData::state) * 8);
   EXPECT_LT(static_cast<size_t>(ax::mojom::Action::kMaxValue),
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 328d7e4e..d745d25 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -81,7 +81,7 @@
   DCHECK(table_node);
 
 #if DCHECK_IS_ON()
-  // Sanity check, make sure the node is in the tree.
+  // Confidence check, make sure the node is in the tree.
   AXNode* node = table_node;
   while (node && node != tree->root())
     node = node->GetParent();
diff --git a/ui/accessibility/ax_tree_combiner.cc b/ui/accessibility/ax_tree_combiner.cc
index bac75f0..b1a9a71 100644
--- a/ui/accessibility/ax_tree_combiner.cc
+++ b/ui/accessibility/ax_tree_combiner.cc
@@ -129,7 +129,7 @@
       }
     }
 
-    // See if this node has a child tree. As a sanity check make sure the
+    // See if this node has a child tree. As a confidence check, make sure the
     // child tree lists this tree as its parent tree id.
     const AXTreeUpdate* child_tree = nullptr;
     if (tree_id_map_.find(child_tree_id) != tree_id_map_.end()) {
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index 513f050..c810afc7 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -379,10 +379,7 @@
   auto num_children = tree_->GetChildCount(node);
   for (size_t i = 0; i < num_children; ++i) {
     AXSourceNode child = tree_->ChildAt(node, i);
-    if (!child) {
-      continue;
-    }
-
+    DCHECK(child);
     DCHECK(tree_->IsValid(child));
     int child_id = tree_->GetId(child);
     ClientTreeNode* client_child = ClientTreeNodeById(child_id);
@@ -695,13 +692,9 @@
     tree_->CacheChildrenIfNeeded(node);
     num_children = tree_->GetChildCount(node);
   }
-  size_t actual_num_children = 0;
   for (size_t i = 0; i < num_children; ++i) {
     AXSourceNode child = tree_->ChildAt(node, i);
-    if (!child) {
-      continue;
-    }
-    actual_num_children++;
+    DCHECK(child);
 
     int new_child_id = tree_->GetId(child);
     new_child_ids.insert(new_child_id);
@@ -790,12 +783,10 @@
   // Iterate over the children, serialize them, and update the ClientTreeNode
   // data structure to reflect the new tree.
   std::vector<AXNodeID> actual_serialized_node_child_ids;
-  client_node->children.reserve(actual_num_children);
+  client_node->children.reserve(num_children);
   for (size_t i = 0; i < num_children; ++i) {
     AXSourceNode child = tree_->ChildAt(node, i);
-    if (!child) {
-      continue;
-    }
+    DCHECK(child);
 
     int child_id = tree_->GetId(child);
 
diff --git a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher.js b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher.js
index 198c5bec..2975acd2 100644
--- a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher.js
+++ b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher.js
@@ -628,10 +628,10 @@
  * @return {boolean} True if the default action should be performed.
  */
 cvox.ChromeVoxEventWatcher.focusEventWatcher = function(evt) {
-  // First remove any dummy spans. We create dummy spans in UserCommands in
+  // First remove any empty spans. We create empty spans in UserCommands in
   // order to sync the browser's default tab action with the user's current
   // navigation position.
-  cvox.ChromeVoxUserCommands.removeTabDummySpan();
+  cvox.ChromeVoxUserCommands.removeTabEmptySpan();
 
   if (!cvox.ChromeVoxEventSuspender.areEventsSuspended()) {
     cvox.ChromeVoxEventWatcher.addEvent(evt);
diff --git a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/navigation_manager.js b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/navigation_manager.js
index be4d90f..831bd1c2 100644
--- a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/navigation_manager.js
+++ b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/navigation_manager.js
@@ -315,12 +315,12 @@
   if (this.shifterStack_.length > 0) {
     return true;
   }
-  var dummySel = this.curSel_.clone();
+  var emptySel = this.curSel_.clone();
   var result = false;
-  var dummyNavShifter = new cvox.NavigationShifter();
-  dummyNavShifter.setGranularity(this.shifter_.getGranularity());
-  dummyNavShifter.sync(dummySel);
-  if (dummyNavShifter.next(dummySel)) {
+  var emptyNavShifter = new cvox.NavigationShifter();
+  emptyNavShifter.setGranularity(this.shifter_.getGranularity());
+  emptyNavShifter.sync(emptySel);
+  if (emptyNavShifter.next(emptySel)) {
     result = true;
   }
   return result;
diff --git a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
index 3ccd5f3..90b8fa4 100644
--- a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
+++ b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
@@ -97,7 +97,7 @@
 
 /**
  * Handles any tab navigation by putting focus at the user's position.
- * This function will create dummy nodes if there is nothing that is focusable
+ * This function will create empty nodes if there is nothing that is focusable
  * at the current position.
  * TODO (adu): This function is too long. We need to break it up into smaller
  * helper functions.
@@ -142,7 +142,7 @@
   }
 
   // See if we can set focus to either anchorNode or focusNode.
-  // If not, try the parents. Otherwise give up and create a dummy span.
+  // If not, try the parents. Otherwise give up and create an empty span.
   if (anchorNode == null || focusNode == null) {
     return true;
   }
@@ -163,39 +163,38 @@
     return true;
   }
 
-  // Insert and focus a dummy span immediately before the current position
+  // Insert and focus an empty span immediately before the current position
   // so that the default tab action will start off as close to the user's
   // current position as possible.
   var bestGuess = anchorNode;
-  var dummySpan = cvox.ChromeVoxUserCommands.createTabDummySpan_();
-  bestGuess.parentNode.insertBefore(dummySpan, bestGuess);
-  dummySpan.focus();
+  var emptySpan = cvox.ChromeVoxUserCommands.createTabEmptySpan_();
+  bestGuess.parentNode.insertBefore(emptySpan, bestGuess);
+  emptySpan.focus();
   return true;
 };
 
 
 /**
- * If a lingering tab dummy span exists, remove it.
+ * If a lingering tab empty span exists, remove it.
  */
-cvox.ChromeVoxUserCommands.removeTabDummySpan = function() {
+cvox.ChromeVoxUserCommands.removeTabEmptySpan = function() {
   // Break the following line to get around a Chromium js linter warning.
   // TODO(plundblad): Find a better solution.
-  var previousDummySpan = document.
-      getElementById('ChromeVoxTabDummySpan');
-  if (previousDummySpan && document.activeElement != previousDummySpan) {
-    previousDummySpan.parentNode.removeChild(previousDummySpan);
+  var previousEmptySpan = document.getElementById('ChromeVoxTabEmptySpan');
+  if (previousEmptySpan && document.activeElement != previousEmptySpan) {
+    previousEmptySpan.parentNode.removeChild(previousEmptySpan);
   }
 };
 
 
 /**
- * Create a new tab dummy span.
- * @return {Element} The dummy span element to be inserted.
+ * Create a new tab empty span.
+ * @return {Element} The empty span element to be inserted.
  * @private
  */
-cvox.ChromeVoxUserCommands.createTabDummySpan_ = function() {
+cvox.ChromeVoxUserCommands.createTabEmptySpan_ = function() {
   var span = document.createElement('span');
-  span.id = 'ChromeVoxTabDummySpan';
+  span.id = 'ChromeVoxTabEmptySpan';
   span.tabIndex = -1;
   return span;
 };
diff --git a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
index 423008f..19f36b8e 100644
--- a/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
+++ b/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
@@ -54,7 +54,7 @@
   this.waitForCalm(this.userCommand, 'forward');
   this.waitForCalm(this.userCommand, 'handleTab');
   this.waitForCalm(function() {
-    // handleTab should place focus on a dummy node immediately in front of
+    // handleTab should place focus on a test node immediately in front of
     // current position so that tab will jump reasonably.
     var id = document.activeElement.nextSibling.id;
     assertEquals('foo', id);
diff --git a/ui/accessibility/extensions/chromevoxclassic/common/braille_util.js b/ui/accessibility/extensions/chromevoxclassic/common/braille_util.js
index fba05ee..b65e8c7 100644
--- a/ui/accessibility/extensions/chromevoxclassic/common/braille_util.js
+++ b/ui/accessibility/extensions/chromevoxclassic/common/braille_util.js
@@ -129,10 +129,10 @@
     //
     // Note: many messages are templatized, and if we don't pass any
     // argument to substitute, getMsg might throw an error if the
-    // resulting string is empty. To avoid this, we pass a dummy
+    // resulting string is empty. To avoid this, we pass a test
     // substitution string array here.
-    var dummySubs = ['dummy', 'dummy', 'dummy'];
-    if (Msgs.getMsg(state[0] + '_brl', dummySubs)) {
+    var testSubs = ['test', 'test', 'test'];
+    if (Msgs.getMsg(state[0] + '_brl', testSubs)) {
       state[0] += '_brl';
     }
   });
diff --git a/ui/accessibility/extensions/chromevoxclassic/common/dom_util.js b/ui/accessibility/extensions/chromevoxclassic/common/dom_util.js
index a25bca65..59cb02d 100644
--- a/ui/accessibility/extensions/chromevoxclassic/common/dom_util.js
+++ b/ui/accessibility/extensions/chromevoxclassic/common/dom_util.js
@@ -1608,13 +1608,13 @@
     }
   }
   if (targetNode) {
-    // Insert a dummy node to adjust next Tab focus location.
+    // Insert a test node to adjust next Tab focus location.
     var parent = targetNode.parentNode;
-    var dummyNode = document.createElement('div');
-    dummyNode.setAttribute('tabindex', '-1');
-    parent.insertBefore(dummyNode, targetNode);
-    dummyNode.setAttribute('chromevoxignoreariahidden', 1);
-    dummyNode.focus();
+    var testNode = document.createElement('div');
+    testNode.setAttribute('tabindex', '-1');
+    parent.insertBefore(testNode, targetNode);
+    testNode.setAttribute('chromevoxignoreariahidden', 1);
+    testNode.focus();
     cvox.ChromeVox.syncToNode(targetNode, false);
   }
 };
diff --git a/ui/accessibility/extensions/chromevoxclassic/common/dom_util_test.unitjs b/ui/accessibility/extensions/chromevoxclassic/common/dom_util_test.unitjs
index 49e5799..e6fbe33 100644
--- a/ui/accessibility/extensions/chromevoxclassic/common/dom_util_test.unitjs
+++ b/ui/accessibility/extensions/chromevoxclassic/common/dom_util_test.unitjs
@@ -309,8 +309,8 @@
     Stay signed in
     </label>
     <input name="signIn" id="signIn" value="Sign in" type="submit">
-    <input id="dummyA" size="18" value="" type="text" title="">
-    <input id="dummyB" size="18" value="" type="text" aria-label="">
+    <input id="idA" size="18" value="" type="text" title="">
+    <input id="idB" size="18" value="" type="text" aria-label="">
     </fieldset>
   */});
 
@@ -344,10 +344,10 @@
   var signinButton = $('signIn');
   assertEquals('Sign in', cvox.DomUtil.getName(signinButton));
   assertEquals('Sign in Button', getControlText(signinButton));
-  var dummyInputA = $('dummyA');
-  assertEquals('', cvox.DomUtil.getName(dummyInputA));
-  var dummyInputB = $('dummyB');
-  assertEquals('', cvox.DomUtil.getName(dummyInputB));
+  var testInputA = $('idA');
+  assertEquals('', cvox.DomUtil.getName(testInputA));
+  var testInputB = $('idB');
+  assertEquals('', cvox.DomUtil.getName(testInputB));
 
   // The heuristic no longer returns 'Stay signed in' as the label for
   // the signIn button because 'Stay signed in' is in a label that's
@@ -1563,7 +1563,7 @@
     });
   });
 
-  // Finally as a sanity check that things are being memoized, turn on
+  // Finally as a confidence check that things are being memoized, turn on
   // memoization and show that we get the wrong result if we change the
   // DOM and call isVisible again.
   cvox.Memoize.scope(function() {
diff --git a/ui/accessibility/extensions/chromevoxclassic/host/testing/earcons.js b/ui/accessibility/extensions/chromevoxclassic/host/testing/earcons.js
index 20276d3b..ece714d 100644
--- a/ui/accessibility/extensions/chromevoxclassic/host/testing/earcons.js
+++ b/ui/accessibility/extensions/chromevoxclassic/host/testing/earcons.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Dummy earcons implementation for testing.
+ * @fileoverview Testing implementation for earcons.
  *
  */
 
diff --git a/ui/accessibility/extensions/chromevoxclassic/host/testing/host.js b/ui/accessibility/extensions/chromevoxclassic/host/testing/host.js
index eed258a7..cffaa00 100644
--- a/ui/accessibility/extensions/chromevoxclassic/host/testing/host.js
+++ b/ui/accessibility/extensions/chromevoxclassic/host/testing/host.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Dummy implementation of host.js for testing.
+ * @fileoverview Testing implementation of host.js.
  *
  */
 
diff --git a/ui/accessibility/extensions/chromevoxclassic/host/testing/tts.js b/ui/accessibility/extensions/chromevoxclassic/host/testing/tts.js
index 2a45eeb..fef1538 100644
--- a/ui/accessibility/extensions/chromevoxclassic/host/testing/tts.js
+++ b/ui/accessibility/extensions/chromevoxclassic/host/testing/tts.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Dummy implementation of TTS for testing.
+ * @fileoverview Testing implementation of TTS.
  *
  */
 
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.cc b/ui/accessibility/platform/ax_platform_node_delegate.cc
index 7eb4a43..17a2b389 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate.cc
@@ -377,10 +377,10 @@
   if (node_)
     return node_->GetHypertextOffsetToHyperlinkChildIndex();
 
-  // TODO(nektar): Remove this dummy method once hypertext computation and
+  // TODO(nektar): Remove this method once hypertext computation and
   // selection handling has moved entirely to AXNode / AXPosition.
-  static base::NoDestructor<std::map<int, int>> dummy_map;
-  return *dummy_map;
+  static base::NoDestructor<std::map<int, int>> empty_map;
+  return *empty_map;
 }
 
 bool AXPlatformNodeDelegate::SetHypertextSelection(int start_offset,
@@ -538,8 +538,8 @@
 }
 
 const AXUniqueId& AXPlatformNodeDelegate::GetUniqueId() const {
-  static base::NoDestructor<AXUniqueId> dummy_unique_id;
-  return *dummy_unique_id;
+  static base::NoDestructor<AXUniqueId> empty_unique_id;
+  return *empty_unique_id;
 }
 
 AXPlatformNodeDelegate* AXPlatformNodeDelegate::GetParentDelegate() const {
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider.h b/ui/accessibility/platform/fuchsia/semantic_provider.h
index 24fbbdf..8f48dfe4 100644
--- a/ui/accessibility/platform/fuchsia/semantic_provider.h
+++ b/ui/accessibility/platform/fuchsia/semantic_provider.h
@@ -70,7 +70,7 @@
   virtual bool Clear() = 0;
 
   // Sends an accessibility event to Fuchsia. Please consult
-  // https://cs.opensource.google/fuchsia/fuchsia/+/master:sdk/fidl/fuchsia.accessibility.semantics/semantics_manager.fidl
+  // https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.accessibility.semantics/semantics_manager.fidl
   // for documentation on events.
   virtual void SendEvent(
       fuchsia::accessibility::semantics::SemanticEvent event) = 0;
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 3d934b14..6ce3007 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -479,6 +479,16 @@
              "WebUiSystemFont",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_MAC)
+// Font Smoothing was enabled by default prior to introducing this feature.
+// We want to experiment with disabling it to align with CR2023 designs,
+// and we also want to disable it *by default* so that any abnormalities or
+// inconsistencies can be noticed as soon as possible.
+BASE_FEATURE(kCr2023MacFontSmoothing,
+             "Cr2023MacFontSmoothing",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
 BASE_FEATURE(kUseNanosecondsForMotionEvent,
              "UseNanosecondsForMotionEvent",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 5a2c6a5..f92325c3 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -236,6 +236,16 @@
 
 COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kWebUiSystemFont);
 
+#if BUILDFLAG(IS_MAC)
+// Font Smoothing, a CoreText technique, simulates optical sizes to enhance text
+// readability at smaller scales. In practice, it leads to an increased
+// perception of text weight, creating discrepancies between renderings in UX
+// design tools and actual macOS displays. This feature is only effective when
+// ChromeRefresh2023 is enabled.
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+BASE_DECLARE_FEATURE(kCr2023MacFontSmoothing);
+#endif
+
 // Сreating a MotionEvent from Java MotionEvent use the event time in
 // nanoseconds instead of milliseconds.
 COMPONENT_EXPORT(UI_BASE_FEATURES)
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 00f83601..171bc18 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -415,6 +415,8 @@
       "CoreGraphics.framework",
       "CoreText.framework",
     ]
+
+    deps += [ "//ui/base:features" ]
   }
 
   if ((!use_aura && !toolkit_views) || is_ios) {
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS
index 73262fa7..75bd0583 100644
--- a/ui/gfx/DEPS
+++ b/ui/gfx/DEPS
@@ -7,6 +7,7 @@
   "+third_party/harfbuzz-ng",
   "+third_party/skia",
   "+third_party/test_fonts",
+  "+ui/base/ui_base_features.h",
   "+ui/ios",
   "+ui/linux",
   "+ui/ozone/buildflags.h",
diff --git a/ui/gfx/font_render_params_mac.cc b/ui/gfx/font_render_params_mac.cc
index a0aa2f7..86111a9 100644
--- a/ui/gfx/font_render_params_mac.cc
+++ b/ui/gfx/font_render_params_mac.cc
@@ -4,7 +4,9 @@
 
 #include "ui/gfx/font_render_params.h"
 
+#include "base/feature_list.h"
 #include "base/notreached.h"
+#include "ui/base/ui_base_features.h"
 
 namespace gfx {
 
@@ -16,9 +18,16 @@
   params.antialiasing = true;
   params.autohinter = false;
   params.use_bitmaps = true;
-  params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
   params.subpixel_positioning = true;
-  params.hinting = FontRenderParams::HINTING_MEDIUM;
+
+  if (features::IsChromeRefresh2023() &&
+      !base::FeatureList::IsEnabled(features::kCr2023MacFontSmoothing)) {
+    params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
+    params.hinting = FontRenderParams::HINTING_NONE;
+  } else {
+    params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
+    params.hinting = FontRenderParams::HINTING_MEDIUM;
+  }
 
   return params;
 }
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 8535a0a..b797278 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -37,6 +37,7 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkTypeface.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/break_list.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -7600,7 +7601,18 @@
       FontRenderParams::SUBPIXEL_RENDERING_RGB;
   DrawVisualText();
 #endif
+
+#if !BUILDFLAG(IS_MAC)
   EXPECT_EQ(GetRendererFont().getEdging(), SkFont::Edging::kSubpixelAntiAlias);
+#else
+  if (features::IsChromeRefresh2023() &&
+      !base::FeatureList::IsEnabled(features::kCr2023MacFontSmoothing)) {
+    EXPECT_EQ(GetRendererFont().getEdging(), SkFont::Edging::kAntiAlias);
+  } else {
+    EXPECT_EQ(GetRendererFont().getEdging(),
+              SkFont::Edging::kSubpixelAntiAlias);
+  }
+#endif
 
   render_text->set_subpixel_rendering_suppressed(true);
   DrawVisualText();
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index b188ad09..6c0d2cd 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -98,6 +98,13 @@
   QtNativeTheme& operator=(const QtNativeTheme&) = delete;
   ~QtNativeTheme() override = default;
 
+  void ThemeChanged(bool prefer_dark_theme) {
+    set_use_dark_colors(IsForcedDarkMode() || prefer_dark_theme);
+    set_preferred_color_scheme(CalculatePreferredColorScheme());
+
+    NotifyOnNativeThemeUpdated();
+  }
+
   // ui::NativeTheme:
   DISABLE_CFI_VCALL
   void PaintFrameTopArea(cc::PaintCanvas* canvas,
@@ -387,7 +394,7 @@
 }
 
 void QtUi::ThemeChanged() {
-  native_theme_->NotifyOnNativeThemeUpdated();
+  native_theme_->ThemeChanged(PreferDarkTheme());
 }
 
 DISABLE_CFI_VCALL
diff --git a/ui/surface/OWNERS b/ui/surface/OWNERS
index 66f16abf..d591a3e 100644
--- a/ui/surface/OWNERS
+++ b/ui/surface/OWNERS
@@ -1,2 +1 @@
 kbr@chromium.org
-pinkerton@chromium.org
diff --git a/ui/views/test/desktop_window_tree_host_win_test_api.cc b/ui/views/test/desktop_window_tree_host_win_test_api.cc
index 2546016..eb99953e2 100644
--- a/ui/views/test/desktop_window_tree_host_win_test_api.cc
+++ b/ui/views/test/desktop_window_tree_host_win_test_api.cc
@@ -36,5 +36,13 @@
   GetHwndMessageHandler()->mock_cursor_position_ = position;
 }
 
+LRESULT DesktopWindowTreeHostWinTestApi::SimulatePenEventForTesting(
+    UINT message,
+    UINT32 pointer_id,
+    POINTER_PEN_INFO pointer_pen_info) {
+  return host_->message_handler_->HandlePointerEventTypePen(message, pointer_id,
+                                                            pointer_pen_info);
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/test/desktop_window_tree_host_win_test_api.h b/ui/views/test/desktop_window_tree_host_win_test_api.h
index f8b7161..2233da6 100644
--- a/ui/views/test/desktop_window_tree_host_win_test_api.h
+++ b/ui/views/test/desktop_window_tree_host_win_test_api.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_TEST_DESKTOP_WINDOW_TREE_HOST_WIN_TEST_API_H_
 #define UI_VIEWS_TEST_DESKTOP_WINDOW_TREE_HOST_WIN_TEST_API_H_
 
+#include <windows.h>
+
 #include "base/memory/raw_ptr.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/native_widget_types.h"
@@ -36,6 +38,10 @@
 
   HWNDMessageHandler* GetHwndMessageHandler();
 
+  LRESULT SimulatePenEventForTesting(UINT message,
+                                     UINT32 pointer_id,
+                                     POINTER_PEN_INFO pointer_pen_info);
+
   void SetMockCursorPositionForTesting(const gfx::Point& position);
 
  private:
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 5de104543..5603bd9 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -3496,21 +3496,10 @@
   return 0;
 }
 
-LRESULT HWNDMessageHandler::HandlePointerEventTypePenClient(UINT message,
-                                                            WPARAM w_param,
-                                                            LPARAM l_param) {
-  UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
-  using GetPointerPenInfoFn = BOOL(WINAPI*)(UINT32, POINTER_PEN_INFO*);
-  POINTER_PEN_INFO pointer_pen_info;
-  static const auto get_pointer_pen_info =
-      reinterpret_cast<GetPointerPenInfoFn>(
-          base::win::GetUser32FunctionPointer("GetPointerPenInfo"));
-  if (!get_pointer_pen_info ||
-      !get_pointer_pen_info(pointer_id, &pointer_pen_info)) {
-    SetMsgHandled(FALSE);
-    return -1;
-  }
-
+LRESULT HWNDMessageHandler::HandlePointerEventTypePen(
+    UINT message,
+    UINT32 pointer_id,
+    POINTER_PEN_INFO pointer_pen_info) {
   POINT client_point = pointer_pen_info.pointerInfo.ptPixelLocationRaw;
   ScreenToClient(hwnd(), &client_point);
   gfx::Point point = gfx::Point(client_point.x, client_point.y);
@@ -3540,6 +3529,19 @@
   return 0;
 }
 
+LRESULT HWNDMessageHandler::HandlePointerEventTypePenClient(UINT message,
+                                                            WPARAM w_param,
+                                                            LPARAM l_param) {
+  UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
+  POINTER_PEN_INFO pointer_pen_info;
+  if (!GetPointerPenInfo(pointer_id, &pointer_pen_info)) {
+    SetMsgHandled(FALSE);
+    return -1;
+  }
+
+  return HandlePointerEventTypePen(message, pointer_id, pointer_pen_info);
+}
+
 bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message,
                                                    int message_time,
                                                    LPARAM l_param) {
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index e48bf961..a8c9c12e 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -557,6 +557,10 @@
                                           WPARAM w_param,
                                           LPARAM l_param);
 
+  // Helper to handle client area events of PT_PEN.
+  LRESULT HandlePointerEventTypePen(UINT message,
+                                    UINT32 pointer_id,
+                                    POINTER_PEN_INFO pointer_pen_info);
   // Returns true if the mouse message passed in is an OS synthesized mouse
   // message.
   // |message| identifies the mouse message.
diff --git a/ui/views/win/pen_id_handler.cc b/ui/views/win/pen_id_handler.cc
index 0da24e03..82451439 100644
--- a/ui/views/win/pen_id_handler.cc
+++ b/ui/views/win/pen_id_handler.cc
@@ -24,6 +24,14 @@
 
 }  // namespace
 
+PenIdHandler::GetPenDeviceStatics get_pen_device_statics = nullptr;
+
+PenIdHandler::ScopedPenIdStaticsForTesting::ScopedPenIdStaticsForTesting(
+    PenIdHandler::GetPenDeviceStatics statics)
+    : resetter_(&get_pen_device_statics, statics) {}
+PenIdHandler::ScopedPenIdStaticsForTesting::~ScopedPenIdStaticsForTesting() =
+    default;
+
 PenIdHandler::PenIdHandler() {
   base::win::AssertComInitialized();
   HRESULT hr = base::win::RoGetActivationFactory(
@@ -55,12 +63,17 @@
 }
 
 absl::optional<std::string> PenIdHandler::TryGetGuid(UINT32 pointer_id) const {
-  if (!pen_device_statics_) {
+  // Override pen device statics if in a test.
+  const Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IPenDeviceStatics>
+      pen_device_statics = get_pen_device_statics ? (*get_pen_device_statics)()
+                                                  : pen_device_statics_;
+
+  if (!pen_device_statics) {
     return absl::nullopt;
   }
 
   Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IPenDevice> pen_device;
-  HRESULT hr = pen_device_statics_->GetFromPointerId(pointer_id, &pen_device);
+  HRESULT hr = pen_device_statics->GetFromPointerId(pointer_id, &pen_device);
   // `pen_device` is null if the pen does not support a unique ID.
   if (FAILED(hr) || !pen_device) {
     return absl::nullopt;
diff --git a/ui/views/win/pen_id_handler.h b/ui/views/win/pen_id_handler.h
index ab79ce9a..e68e899 100644
--- a/ui/views/win/pen_id_handler.h
+++ b/ui/views/win/pen_id_handler.h
@@ -13,6 +13,7 @@
 
 #include <string>
 
+#include "base/auto_reset.h"
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -28,10 +29,21 @@
 // which is ultimately returned.
 class VIEWS_EXPORT PenIdHandler {
  public:
+  using GetPenDeviceStatics = Microsoft::WRL::ComPtr<
+      ABI::Windows::Devices::Input::IPenDeviceStatics> (*)();
+  class VIEWS_EXPORT [[maybe_unused, nodiscard]] ScopedPenIdStaticsForTesting {
+   public:
+    explicit ScopedPenIdStaticsForTesting(
+        GetPenDeviceStatics pen_device_statics);
+    ~ScopedPenIdStaticsForTesting();
+
+   private:
+    base::AutoReset<GetPenDeviceStatics> resetter_;
+  };
+
   PenIdHandler();
   virtual ~PenIdHandler();
   absl::optional<int32_t> TryGetPenUniqueId(UINT32 pointer_id);
-  static void OverrideStaticsForTesting(bool override);
 
  private:
   friend class FakePenIdHandler;
diff --git a/ui/views/win/test_support/fake_ipen_device_statics.cc b/ui/views/win/test_support/fake_ipen_device_statics.cc
index 0f8469be..5a56fd6 100644
--- a/ui/views/win/test_support/fake_ipen_device_statics.cc
+++ b/ui/views/win/test_support/fake_ipen_device_statics.cc
@@ -19,6 +19,14 @@
   return instance.get();
 }
 
+Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IPenDeviceStatics>
+FakeIPenDeviceStatics::FakeIPenDeviceStaticsComPtr() {
+  FakeIPenDeviceStatics* instance = GetInstance();
+  return static_cast<
+      Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IPenDeviceStatics>>(
+      instance);
+}
+
 HRESULT FakeIPenDeviceStatics::GetFromPointerId(UINT32 pointer_id,
                                                 IPenDevice** result) {
   auto pen_device = pen_device_map_.find(pointer_id);
diff --git a/ui/views/win/test_support/fake_ipen_device_statics.h b/ui/views/win/test_support/fake_ipen_device_statics.h
index b404dede..3a21064 100644
--- a/ui/views/win/test_support/fake_ipen_device_statics.h
+++ b/ui/views/win/test_support/fake_ipen_device_statics.h
@@ -26,6 +26,8 @@
   ~FakeIPenDeviceStatics() final;
 
   static FakeIPenDeviceStatics* GetInstance();
+  static Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IPenDeviceStatics>
+  FakeIPenDeviceStaticsComPtr();
 
   // ABI::Windows::Devices::Input::IPenDeviceStatics:
   IFACEMETHODIMP GetFromPointerId(
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.html b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.html
index 3cba6fa..c79ff3b 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.html
+++ b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.html
@@ -101,18 +101,13 @@
             </cr-icon-button>
           </div>
         </template>
-        <template is="dom-if" if="[[!groupIsHidden_(groupId, hiddenGroupIds_.*)]]"
-            restamp>
-          <template is="dom-repeat"
-              items="[[matchesForSide_(side, result.matches.*)]]" as="match"
-              filter="[[matchIsInGroupFilter_(groupId)]]"
-              on-dom-change="onResultRepaint_">
-            <cr-realbox-match tabindex="0" role="option" match="[[match]]"
-                match-index="[[matchIndex_(match)]]"
-                side-type="[[sideTypeForGroup_(groupId)]]"
-                selected$="[[isSelected_(match, selectedMatchIndex)]]">
-            </cr-realbox-match>
-          </template>
+        <template is="dom-repeat"
+            items="[[matchesForGroup_(groupId, result.matches.*, hiddenGroupIds_.*)]]"
+            as="match" on-dom-change="onResultRepaint_">
+          <cr-realbox-match tabindex="0" role="option" match="[[match]]"
+              match-index="[[matchIndex_(match)]]" side-type="[[side]]"
+              selected$="[[isSelected_(match, selectedMatchIndex)]]">
+          </cr-realbox-match>
         </template>
       </template>
     </div>
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
index c6d4cc6..74122d7 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
@@ -263,7 +263,7 @@
   }
 
   /**
-   * @returns The unique suggestion group IDs that belong to given side type
+   * @returns The unique suggestion group IDs that belong to the given side type
    *     while preserving the order in which they appear in the list of matches.
    */
   private groupIdsForSide_(side: SideType): number[] {
@@ -297,15 +297,6 @@
   }
 
   /**
-   * @returns The filter function to filter matches that belong to the given
-   *     suggestion group ID.
-   */
-  private matchIsInGroupFilter_(groupId: number):
-      (match: AutocompleteMatch) => boolean {
-    return match => match.suggestionGroupId === groupId;
-  }
-
-  /**
    * @returns Index of the match in the autocomplete result. Passed to the match
    *     so it knows its position in the list of matches.
    */
@@ -314,13 +305,14 @@
   }
 
   /**
-   * @returns The list of matches that belong to given side type. Updates if
-   *     secondary matches are currently or were at any point available to show.
+   * @returns The list of visible matches that belong to the given suggestion
+   *     group ID.
    */
-  private matchesForSide_(side: SideType): AutocompleteMatch[] {
-    return (this.result?.matches ?? [])
-        .filter(
-            match => this.sideTypeForGroup_(match.suggestionGroupId) === side);
+  private matchesForGroup_(groupId: number): AutocompleteMatch[] {
+    return this.groupIsHidden_(groupId) ?
+        [] :
+        (this.result?.matches ??
+         []).filter(match => match.suggestionGroupId === groupId);
   }
 
   /**
diff --git a/ui/wm/core/cursors_aura.cc b/ui/wm/core/cursors_aura.cc
deleted file mode 100644
index 1623284..0000000
--- a/ui/wm/core/cursors_aura.cc
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/wm/core/cursors_aura.h"
-
-#include <stddef.h>
-
-#include "base/memory/raw_ptr_exclusion.h"
-#include "build/build_config.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/cursor_size.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/image/image_skia_rep.h"
-#include "ui/resources/grit/ui_resources.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "ui/base/win/win_cursor.h"
-#include "ui/gfx/icon_util.h"
-#include "ui/wm/core/cursor_loader.h"
-#endif
-
-namespace wm {
-
-namespace {
-
-namespace mojom = ::ui::mojom;
-
-struct HotPoint {
-  int x;
-  int y;
-};
-
-struct CursorData {
-  mojom::CursorType id;
-  int resource_id;
-  HotPoint hot_1x;
-  HotPoint hot_2x;
-};
-
-struct CursorSizeData {
-  const ui::CursorSize id;
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #global-scope
-  RAW_PTR_EXCLUSION const CursorData* cursors;
-  const int length;
-};
-
-const CursorData kNormalCursors[] = {
-    {mojom::CursorType::kNull, IDR_AURA_CURSOR_PTR, {4, 4}, {7, 7}},
-    {mojom::CursorType::kPointer, IDR_AURA_CURSOR_PTR, {4, 4}, {7, 7}},
-    {mojom::CursorType::kNoDrop, IDR_AURA_CURSOR_NO_DROP, {9, 9}, {18, 18}},
-    {mojom::CursorType::kNotAllowed, IDR_AURA_CURSOR_NO_DROP, {9, 9}, {18, 18}},
-    {mojom::CursorType::kCopy, IDR_AURA_CURSOR_COPY, {9, 9}, {18, 18}},
-    {mojom::CursorType::kHand, IDR_AURA_CURSOR_HAND, {9, 4}, {19, 8}},
-    {mojom::CursorType::kMove, IDR_AURA_CURSOR_MOVE, {11, 11}, {23, 23}},
-    {mojom::CursorType::kNorthEastResize,
-     IDR_AURA_CURSOR_NORTH_EAST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kSouthWestResize,
-     IDR_AURA_CURSOR_SOUTH_WEST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kSouthEastResize,
-     IDR_AURA_CURSOR_SOUTH_EAST_RESIZE,
-     {11, 11},
-     {24, 23}},
-    {mojom::CursorType::kNorthWestResize,
-     IDR_AURA_CURSOR_NORTH_WEST_RESIZE,
-     {11, 11},
-     {24, 23}},
-    {mojom::CursorType::kNorthResize,
-     IDR_AURA_CURSOR_NORTH_RESIZE,
-     {11, 12},
-     {23, 23}},
-    {mojom::CursorType::kSouthResize,
-     IDR_AURA_CURSOR_SOUTH_RESIZE,
-     {11, 12},
-     {23, 23}},
-    {mojom::CursorType::kEastResize,
-     IDR_AURA_CURSOR_EAST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kWestResize,
-     IDR_AURA_CURSOR_WEST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kIBeam, IDR_AURA_CURSOR_IBEAM, {12, 12}, {24, 25}},
-    {mojom::CursorType::kAlias, IDR_AURA_CURSOR_ALIAS, {8, 6}, {15, 11}},
-    {mojom::CursorType::kCell, IDR_AURA_CURSOR_CELL, {11, 11}, {24, 23}},
-    {mojom::CursorType::kContextMenu,
-     IDR_AURA_CURSOR_CONTEXT_MENU,
-     {4, 4},
-     {8, 9}},
-    {mojom::CursorType::kCross, IDR_AURA_CURSOR_CROSSHAIR, {12, 12}, {24, 24}},
-    {mojom::CursorType::kHelp, IDR_AURA_CURSOR_HELP, {4, 4}, {8, 9}},
-    {mojom::CursorType::kVerticalText,
-     IDR_AURA_CURSOR_XTERM_HORIZ,
-     {12, 11},
-     {26, 23}},
-    {mojom::CursorType::kZoomIn, IDR_AURA_CURSOR_ZOOM_IN, {10, 10}, {20, 20}},
-    {mojom::CursorType::kZoomOut, IDR_AURA_CURSOR_ZOOM_OUT, {10, 10}, {20, 20}},
-    {mojom::CursorType::kRowResize,
-     IDR_AURA_CURSOR_ROW_RESIZE,
-     {11, 12},
-     {23, 23}},
-    {mojom::CursorType::kColumnResize,
-     IDR_AURA_CURSOR_COL_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kEastWestNoResize,
-     IDR_AURA_CURSOR_EAST_WEST_NO_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kEastWestResize,
-     IDR_AURA_CURSOR_EAST_WEST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kNorthSouthNoResize,
-     IDR_AURA_CURSOR_NORTH_SOUTH_NO_RESIZE,
-     {11, 12},
-     {23, 23}},
-    {mojom::CursorType::kNorthSouthResize,
-     IDR_AURA_CURSOR_NORTH_SOUTH_RESIZE,
-     {11, 12},
-     {23, 23}},
-    {mojom::CursorType::kNorthEastSouthWestNoResize,
-     IDR_AURA_CURSOR_NORTH_EAST_SOUTH_WEST_NO_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kNorthEastSouthWestResize,
-     IDR_AURA_CURSOR_NORTH_EAST_SOUTH_WEST_RESIZE,
-     {12, 11},
-     {25, 23}},
-    {mojom::CursorType::kNorthWestSouthEastNoResize,
-     IDR_AURA_CURSOR_NORTH_WEST_SOUTH_EAST_NO_RESIZE,
-     {11, 11},
-     {24, 23}},
-    {mojom::CursorType::kNorthWestSouthEastResize,
-     IDR_AURA_CURSOR_NORTH_WEST_SOUTH_EAST_RESIZE,
-     {11, 11},
-     {24, 23}},
-    {mojom::CursorType::kGrab, IDR_AURA_CURSOR_GRAB, {8, 5}, {16, 10}},
-    {mojom::CursorType::kGrabbing, IDR_AURA_CURSOR_GRABBING, {9, 9}, {18, 18}},
-    {mojom::CursorType::kWait, IDR_AURA_CURSOR_THROBBER, {7, 7}, {14, 14}},
-    {mojom::CursorType::kProgress, IDR_AURA_CURSOR_THROBBER, {7, 7}, {14, 14}},
-};
-
-const CursorData kLargeCursors[] = {
-    // The 2x hotspots should be double of the 1x, even though the cursors are
-    // shown as same size as 1x (64x64), because in 2x dpi screen, the 1x large
-    // cursor assets (64x64) are internally enlarged to the double size
-    // (128x128)
-    // by ResourceBundleImageSource.
-    {mojom::CursorType::kNull, IDR_AURA_CURSOR_BIG_PTR, {10, 10}, {20, 20}},
-    {mojom::CursorType::kPointer, IDR_AURA_CURSOR_BIG_PTR, {10, 10}, {20, 20}},
-    {mojom::CursorType::kNoDrop,
-     IDR_AURA_CURSOR_BIG_NO_DROP,
-     {10, 10},
-     {20, 20}},
-    {mojom::CursorType::kNotAllowed,
-     IDR_AURA_CURSOR_BIG_NO_DROP,
-     {10, 10},
-     {20, 20}},
-    {mojom::CursorType::kCopy, IDR_AURA_CURSOR_BIG_COPY, {21, 11}, {42, 22}},
-    {mojom::CursorType::kHand, IDR_AURA_CURSOR_BIG_HAND, {25, 7}, {50, 14}},
-    {mojom::CursorType::kMove, IDR_AURA_CURSOR_BIG_MOVE, {32, 31}, {64, 62}},
-    {mojom::CursorType::kNorthEastResize,
-     IDR_AURA_CURSOR_BIG_NORTH_EAST_RESIZE,
-     {31, 28},
-     {62, 56}},
-    {mojom::CursorType::kSouthWestResize,
-     IDR_AURA_CURSOR_BIG_SOUTH_WEST_RESIZE,
-     {31, 28},
-     {62, 56}},
-    {mojom::CursorType::kSouthEastResize,
-     IDR_AURA_CURSOR_BIG_SOUTH_EAST_RESIZE,
-     {28, 28},
-     {56, 56}},
-    {mojom::CursorType::kNorthWestResize,
-     IDR_AURA_CURSOR_BIG_NORTH_WEST_RESIZE,
-     {28, 28},
-     {56, 56}},
-    {mojom::CursorType::kNorthResize,
-     IDR_AURA_CURSOR_BIG_NORTH_RESIZE,
-     {29, 32},
-     {58, 64}},
-    {mojom::CursorType::kSouthResize,
-     IDR_AURA_CURSOR_BIG_SOUTH_RESIZE,
-     {29, 32},
-     {58, 64}},
-    {mojom::CursorType::kEastResize,
-     IDR_AURA_CURSOR_BIG_EAST_RESIZE,
-     {35, 29},
-     {70, 58}},
-    {mojom::CursorType::kWestResize,
-     IDR_AURA_CURSOR_BIG_WEST_RESIZE,
-     {35, 29},
-     {70, 58}},
-    {mojom::CursorType::kIBeam, IDR_AURA_CURSOR_BIG_IBEAM, {30, 32}, {60, 64}},
-    {mojom::CursorType::kAlias, IDR_AURA_CURSOR_BIG_ALIAS, {19, 11}, {38, 22}},
-    {mojom::CursorType::kCell, IDR_AURA_CURSOR_BIG_CELL, {30, 30}, {60, 60}},
-    {mojom::CursorType::kContextMenu,
-     IDR_AURA_CURSOR_BIG_CONTEXT_MENU,
-     {11, 11},
-     {22, 22}},
-    {mojom::CursorType::kCross,
-     IDR_AURA_CURSOR_BIG_CROSSHAIR,
-     {30, 32},
-     {60, 64}},
-    {mojom::CursorType::kHelp, IDR_AURA_CURSOR_BIG_HELP, {10, 11}, {20, 22}},
-    {mojom::CursorType::kVerticalText,
-     IDR_AURA_CURSOR_BIG_XTERM_HORIZ,
-     {32, 30},
-     {64, 60}},
-    {mojom::CursorType::kZoomIn,
-     IDR_AURA_CURSOR_BIG_ZOOM_IN,
-     {25, 26},
-     {50, 52}},
-    {mojom::CursorType::kZoomOut,
-     IDR_AURA_CURSOR_BIG_ZOOM_OUT,
-     {26, 26},
-     {52, 52}},
-    {mojom::CursorType::kRowResize,
-     IDR_AURA_CURSOR_BIG_ROW_RESIZE,
-     {29, 32},
-     {58, 64}},
-    {mojom::CursorType::kColumnResize,
-     IDR_AURA_CURSOR_BIG_COL_RESIZE,
-     {35, 29},
-     {70, 58}},
-    {mojom::CursorType::kEastWestNoResize,
-     IDR_AURA_CURSOR_BIG_EAST_WEST_NO_RESIZE,
-     {35, 29},
-     {70, 58}},
-    {mojom::CursorType::kEastWestResize,
-     IDR_AURA_CURSOR_BIG_EAST_WEST_RESIZE,
-     {35, 29},
-     {70, 58}},
-    {mojom::CursorType::kNorthSouthNoResize,
-     IDR_AURA_CURSOR_BIG_NORTH_SOUTH_NO_RESIZE,
-     {29, 32},
-     {58, 64}},
-    {mojom::CursorType::kNorthSouthResize,
-     IDR_AURA_CURSOR_BIG_NORTH_SOUTH_RESIZE,
-     {29, 32},
-     {58, 64}},
-    {mojom::CursorType::kNorthEastSouthWestNoResize,
-     IDR_AURA_CURSOR_BIG_NORTH_EAST_SOUTH_WEST_NO_RESIZE,
-     {32, 30},
-     {64, 60}},
-    {mojom::CursorType::kNorthEastSouthWestResize,
-     IDR_AURA_CURSOR_BIG_NORTH_EAST_SOUTH_WEST_RESIZE,
-     {32, 30},
-     {64, 60}},
-    {mojom::CursorType::kNorthWestSouthEastNoResize,
-     IDR_AURA_CURSOR_BIG_NORTH_WEST_SOUTH_EAST_NO_RESIZE,
-     {32, 31},
-     {64, 62}},
-    {mojom::CursorType::kNorthWestSouthEastResize,
-     IDR_AURA_CURSOR_BIG_NORTH_WEST_SOUTH_EAST_RESIZE,
-     {32, 31},
-     {64, 62}},
-    {mojom::CursorType::kGrab, IDR_AURA_CURSOR_BIG_GRAB, {21, 11}, {42, 22}},
-    {mojom::CursorType::kGrabbing,
-     IDR_AURA_CURSOR_BIG_GRABBING,
-     {20, 12},
-     {40, 24}},
-    // TODO(https://crbug.com/336867): create IDR_AURA_CURSOR_BIG_THROBBER.
-};
-
-const CursorSizeData kCursorSizes[] = {
-    {ui::CursorSize::kNormal, kNormalCursors, std::size(kNormalCursors)},
-    {ui::CursorSize::kLarge, kLargeCursors, std::size(kLargeCursors)},
-};
-
-const CursorSizeData* GetCursorSizeByType(ui::CursorSize cursor_size) {
-  for (size_t i = 0; i < std::size(kCursorSizes); ++i) {
-    if (kCursorSizes[i].id == cursor_size) {
-      return &kCursorSizes[i];
-    }
-  }
-
-  return NULL;
-}
-
-bool SearchTable(const CursorData* table,
-                 size_t table_length,
-                 mojom::CursorType id,
-                 float scale_factor,
-                 int* resource_id,
-                 gfx::Point* point) {
-  DCHECK_NE(scale_factor, 0);
-
-  bool resource_2x_available =
-      ui::ResourceBundle::GetSharedInstance().GetMaxResourceScaleFactor() ==
-      ui::k200Percent;
-  for (size_t i = 0; i < table_length; ++i) {
-    if (table[i].id == id) {
-      *resource_id = table[i].resource_id;
-      *point = scale_factor == 1.0f || !resource_2x_available
-                   ? gfx::Point(table[i].hot_1x.x, table[i].hot_1x.y)
-                   : gfx::Point(table[i].hot_2x.x, table[i].hot_2x.y);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-}  // namespace
-
-bool GetCursorDataFor(ui::CursorSize cursor_size,
-                      mojom::CursorType id,
-                      float scale_factor,
-                      int* resource_id,
-                      gfx::Point* point) {
-  const CursorSizeData* cursor_set = GetCursorSizeByType(cursor_size);
-  if (cursor_set && SearchTable(cursor_set->cursors, cursor_set->length, id,
-                                scale_factor, resource_id, point)) {
-    return true;
-  }
-
-  // Falls back to the default cursor set.
-  cursor_set = GetCursorSizeByType(ui::CursorSize::kNormal);
-  DCHECK(cursor_set);
-  return SearchTable(cursor_set->cursors, cursor_set->length, id, scale_factor,
-                     resource_id, point);
-}
-
-SkBitmap GetDefaultBitmap(const ui::Cursor& cursor) {
-#if BUILDFLAG(IS_WIN)
-  ui::Cursor cursor_copy = cursor;
-  CursorLoader cursor_loader;
-  cursor_loader.SetPlatformCursor(&cursor_copy);
-  return IconUtil::CreateSkBitmapFromHICON(
-      ui::WinCursor::FromPlatformCursor(cursor_copy.platform())->hcursor());
-#else
-  int resource_id;
-  gfx::Point hotspot;
-  if (!GetCursorDataFor(ui::CursorSize::kNormal, cursor.type(),
-                        cursor.image_scale_factor(), &resource_id, &hotspot)) {
-    return SkBitmap();
-  }
-  return ui::ResourceBundle::GetSharedInstance()
-      .GetImageSkiaNamed(resource_id)
-      ->GetRepresentation(cursor.image_scale_factor())
-      .GetBitmap();
-#endif
-}
-
-gfx::Point GetDefaultHotspot(const ui::Cursor& cursor) {
-#if BUILDFLAG(IS_WIN)
-  ui::Cursor cursor_copy = cursor;
-  CursorLoader cursor_loader;
-  cursor_loader.SetPlatformCursor(&cursor_copy);
-  return IconUtil::GetHotSpotFromHICON(
-      ui::WinCursor::FromPlatformCursor(cursor_copy.platform())->hcursor());
-#else
-  int resource_id;
-  gfx::Point hotspot;
-  if (!GetCursorDataFor(ui::CursorSize::kNormal, cursor.type(),
-                        cursor.image_scale_factor(), &resource_id, &hotspot)) {
-    return gfx::Point();
-  }
-  return hotspot;
-#endif
-}
-
-}  // namespace wm