diff --git a/AUTHORS b/AUTHORS
index dfe36977..b708de5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -984,6 +984,7 @@
 Rene Ladan <r.c.ladan@gmail.com>
 Richard Baranyi <lordprotector@gmail.com>
 Richard Li <richard.li@intel.com>
+Richard Smith <happyercat@gmail.com>
 Rijubrata Bhaumik <rijubrata.bhaumik@intel.com>
 Riku Voipio <riku.voipio@linaro.org>
 Rob Buis <rob.buis@samsung.com>
diff --git a/BUILD.gn b/BUILD.gn
index 4efccb33..2d0aa4f2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -147,7 +147,7 @@
     ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     deps += [
       "//ppapi:ppapi_unittests",
       "//ppapi/examples",
@@ -583,7 +583,7 @@
     }
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     deps += [
       "//ppapi:pepper_hash_for_uma",
       "//ppapi:ppapi_perftests",
diff --git a/DEPS b/DEPS
index d14a0ea..f1209e6 100644
--- a/DEPS
+++ b/DEPS
@@ -297,11 +297,11 @@
   # 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': '3ee4ae9c24757b214348bd4d6490195bdb9e9a20',
+  'skia_revision': '3a6e1b892c2ffd434eea7a5d36c624120d61e9df',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '185fc09b6eee425cd499549c3d1d84a137912928',
+  'v8_revision': 'aa135bd3fb16dff383281079fb76cb22efdedff4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -324,7 +324,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:9.20220720.2.1',
+  'fuchsia_version': 'version:9.20220722.3.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.
@@ -368,7 +368,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3384fbd90357b44f9e50dd33dbabc4f1acc1e9e2',
+  'catapult_revision': '192679c0210b9529653073cc1d28fa47f444187d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -376,7 +376,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '1347dd5cd4f855b7a3dd42987a03b16cafc6b93f',
+  'devtools_frontend_revision': '30f01bf8aa0f9ffa8a9dab4b2c1b158dfeb4cf96',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -412,7 +412,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'a089376daf17ed16c2b527d26aa41338c1ad60f8',
+  'dawn_revision': 'e657fa7cb7d522a6f3fede8bf06133489017f5d8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -480,7 +480,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'eb79671bfbedd77b747d01dee8c0479ff1693f88',
+  'libcxx_revision':       'ae6c9d1fb41406da05a3b6def66151b41fff086c',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:9ef321772ecc161937db69acb346397e0ccc484d',
@@ -1184,7 +1184,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f353dc3e889584a1b0e1201fefc0464726d22917',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2cd48234f1fdef2e61a06a7a43479fe8407baaf0',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1584,7 +1584,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '209791f79d097387b721b2121ae5b54c4d4facb3',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '86c920c3179e3abff64a8ed736b11ca3ac9998a5',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1715,7 +1715,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c42337d9ef75170244486b580bad7dfe78447bfd',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@50a58c2a73cb6f66480c8c258af941e1672255dd',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1754,7 +1754,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e88fcf99da482fb22a3f90e0f74bed85d84e0f99',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '08492a199058dce016d31aa30464987198561498',
+    Var('webrtc_git') + '/src.git' + '@' + '98bfd991517c30ae883b12a131c684aec8a3b178',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1827,7 +1827,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4f158789476269b3880e8d182ee2f4da32d50742',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a8b6f16dfee6b28fccc6ce15435bcb71f4ed5027',
     'condition': 'checkout_src_internal',
   },
 
@@ -1879,7 +1879,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'geCGRZyWhKZJmosuLy3CrGPLEWpmYMQGdrmlPyHHJ4UC',
+        'version': 'gdBEjt3JjDI1P9zKXIqhsqsaxoT7ns9ZvIxuz3WcMUQC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 8b27c87..4c14eec 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1110,6 +1110,10 @@
     "system/cast/unified_cast_detailed_view_controller.h",
     "system/channel_indicator/channel_indicator.cc",
     "system/channel_indicator/channel_indicator.h",
+    "system/channel_indicator/channel_indicator_quick_settings_view.cc",
+    "system/channel_indicator/channel_indicator_quick_settings_view.h",
+    "system/channel_indicator/channel_indicator_utils.cc",
+    "system/channel_indicator/channel_indicator_utils.h",
     "system/dark_mode/dark_mode_feature_pod_controller.cc",
     "system/dark_mode/dark_mode_feature_pod_controller.h",
     "system/diagnostics/async_log.cc",
@@ -2744,7 +2748,9 @@
     "system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc",
     "system/bluetooth/unified_bluetooth_detailed_view_controller_unittest.cc",
     "system/caps_lock_notification_controller_unittest.cc",
+    "system/channel_indicator/channel_indicator_quick_settings_view_unittest.cc",
     "system/channel_indicator/channel_indicator_unittest.cc",
+    "system/channel_indicator/channel_indicator_utils_unittest.cc",
     "system/dark_mode/dark_mode_feature_pod_controller_unittest.cc",
     "system/diagnostics/async_log_unittest.cc",
     "system/diagnostics/diagnostics_log_controller_unittest.cc",
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index 89b6100..ade665a4 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -2052,7 +2052,7 @@
 
     key_codes[key_code] = std::set<std::string>();
 
-    for (const base::Value& device_type : v.second.GetListDeprecated())
+    for (const base::Value& device_type : v.second.GetList())
       key_codes[key_code].insert(device_type.GetString());
 
     DCHECK(!key_codes[key_code].empty());
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.cc b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
index 0e29a2a7..7faa8a73 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.cc
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
@@ -131,7 +131,7 @@
   }
 }
 
-absl::optional<std::string> GetStringValue(base::Value::ConstListView values,
+absl::optional<std::string> GetStringValue(const base::Value::List& values,
                                            size_t field_number) {
   if (values.empty() || values.size() < field_number)
     return absl::nullopt;
@@ -143,7 +143,7 @@
   return v.GetString();
 }
 
-absl::optional<double> GetDoubleValue(base::Value::ConstListView values,
+absl::optional<double> GetDoubleValue(const base::Value::List& values,
                                       size_t field_number) {
   if (values.empty() || values.size() < field_number)
     return absl::nullopt;
@@ -155,7 +155,7 @@
   return v.GetDouble();
 }
 
-absl::optional<bool> GetBoolValue(base::Value::ConstListView values,
+absl::optional<bool> GetBoolValue(const base::Value::List& values,
                                   size_t field_number) {
   if (values.empty() || values.size() < field_number)
     return absl::nullopt;
@@ -176,7 +176,7 @@
     return absl::nullopt;
 
   WeatherInfo weather_info;
-  const auto& list_result = result.GetListDeprecated();
+  const auto& list_result = result.GetList();
 
   weather_info.condition_icon_url = GetStringValue(
       list_result, backdrop::WeatherInfo::kConditionIconUrlFieldNumber);
diff --git a/ash/app_list/app_list_bubble_presenter.cc b/ash/app_list/app_list_bubble_presenter.cc
index 8ac795c..26a199f 100644
--- a/ash/app_list/app_list_bubble_presenter.cc
+++ b/ash/app_list/app_list_bubble_presenter.cc
@@ -230,6 +230,9 @@
         std::make_unique<AppListEventTargeter>(controller_));
     bubble_view_ = bubble_widget_->SetContentsView(
         std::make_unique<AppListBubbleView>(controller_, drag_and_drop_host));
+    // Some of Assistant UIs have to be initialized explicitly. See details in
+    // the comment of AppListBubbleView::InitializeUIForBubbleView.
+    bubble_view_->InitializeUIForBubbleView();
     // Arrow left/right and up/down triggers the same focus movement as
     // tab/shift+tab.
     bubble_widget_->widget_delegate()->SetEnableArrowKeyTraversal(true);
diff --git a/ash/app_list/app_list_bubble_presenter_unittest.cc b/ash/app_list/app_list_bubble_presenter_unittest.cc
index 5f6ccef..b7c4465 100644
--- a/ash/app_list/app_list_bubble_presenter_unittest.cc
+++ b/ash/app_list/app_list_bubble_presenter_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/app_list/views/app_list_bubble_apps_page.h"
 #include "ash/app_list/views/app_list_bubble_view.h"
 #include "ash/app_list/views/search_box_view.h"
+#include "ash/assistant/ui/assistant_view_ids.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/app_list_test_api.h"
@@ -32,6 +33,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/display/display.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -431,6 +433,16 @@
   EXPECT_FALSE(
       presenter->bubble_view_for_test()->apps_page_for_test()->GetVisible());
   EXPECT_TRUE(presenter->IsShowingEmbeddedAssistantUI());
+
+  views::View* progress_indicator =
+      presenter->bubble_view_for_test()->GetViewByID(
+          AssistantViewID::kProgressIndicator);
+  EXPECT_FLOAT_EQ(0.f, progress_indicator->layer()->opacity());
+
+  // Check target opacity as footer is animating.
+  views::View* footer = presenter->bubble_view_for_test()->GetViewByID(
+      AssistantViewID::kFooterView);
+  EXPECT_FLOAT_EQ(1.f, footer->layer()->GetTargetOpacity());
 }
 
 TEST_F(AppListBubblePresenterTest, AssistantKeyOpensAssistantPageWhenCached) {
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index e5c01ec..6ab0e36 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -714,6 +714,10 @@
   // Nothing to do.
 }
 
+void AppListBubbleView::InitializeUIForBubbleView() {
+  assistant_page_->InitializeUIForBubbleView();
+}
+
 void AppListBubbleView::DisableFocusForShowingActiveFolder(bool disabled) {
   search_box_view_->SetEnabled(!disabled);
   SetViewIgnoredForAccessibility(search_box_view_, disabled);
diff --git a/ash/app_list/views/app_list_bubble_view.h b/ash/app_list/views/app_list_bubble_view.h
index 04bb18d..9f7d5c4 100644
--- a/ash/app_list/views/app_list_bubble_view.h
+++ b/ash/app_list/views/app_list_bubble_view.h
@@ -115,6 +115,24 @@
   void ReparentFolderItemTransit(AppListFolderItem* folder_item) override;
   void ReparentDragEnded() override;
 
+  // Initialize Assistant UIs for bubble view. Assistant UIs
+  // (AppListAssistantMainStage, SuggestionContainerView) expect that their
+  // OnUiVisibilityChanged methods get called via value update in
+  // AssistantUiModel.
+  //
+  // But it does not happen for bubble view as AppListBubblePresenter have an
+  // async call for OnZeroStateSearchDone. AppListBubbleView is instantiated
+  // after the async call and those UIs will miss the event.
+  //
+  // This is a helper method to manually trigger the UI initialization.
+  //
+  // This method is designed to be explicitly called from AppListBubblePresenter
+  // (i.e. instead of doing this in the constructor of AppListBubbleView) to
+  // make the intention clear.
+  //
+  // TODO(b/239754561): Clean up: refactor Assistant UI initialization
+  void InitializeUIForBubbleView();
+
   AppListBubblePage current_page_for_test() { return current_page_; }
   ViewShadow* view_shadow_for_test() { return view_shadow_.get(); }
   SearchBoxView* search_box_view_for_test() { return search_box_view_; }
diff --git a/ash/app_list/views/assistant/app_list_bubble_assistant_page.cc b/ash/app_list/views/assistant/app_list_bubble_assistant_page.cc
index 6266ffd..900eea2 100644
--- a/ash/app_list/views/assistant/app_list_bubble_assistant_page.cc
+++ b/ash/app_list/views/assistant/app_list_bubble_assistant_page.cc
@@ -47,6 +47,10 @@
     AssistantUiController::Get()->SetAppListBubbleWidth(size().width());
 }
 
+void AppListBubbleAssistantPage::InitializeUIForBubbleView() {
+  main_stage_->InitializeUIForBubbleView();
+}
+
 BEGIN_METADATA(AppListBubbleAssistantPage, views::View)
 END_METADATA
 
diff --git a/ash/app_list/views/assistant/app_list_bubble_assistant_page.h b/ash/app_list/views/assistant/app_list_bubble_assistant_page.h
index 6da445d..5af2883 100644
--- a/ash/app_list/views/assistant/app_list_bubble_assistant_page.h
+++ b/ash/app_list/views/assistant/app_list_bubble_assistant_page.h
@@ -31,6 +31,8 @@
   void RequestFocus() override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
 
+  void InitializeUIForBubbleView();
+
  private:
   // The text and microphone input area. Owned by views hierarchy.
   AssistantDialogPlate* dialog_plate_;
diff --git a/ash/app_list/views/assistant/assistant_main_stage.cc b/ash/app_list/views/assistant/assistant_main_stage.cc
index 848eef83..28ad471 100644
--- a/ash/app_list/views/assistant/assistant_main_stage.cc
+++ b/ash/app_list/views/assistant/assistant_main_stage.cc
@@ -423,19 +423,9 @@
     absl::optional<AssistantEntryPoint> entry_point,
     absl::optional<AssistantExitPoint> exit_point) {
   if (assistant::util::IsStartingSession(new_visibility, old_visibility)) {
-    // When Assistant is starting a new session, we animate in the appearance of
-    // the zero state view and footer.
     const bool from_search =
         entry_point == AssistantEntryPoint::kLauncherSearchResult;
-    progress_indicator_->layer()->SetOpacity(0.f);
-    horizontal_separator_->layer()->SetOpacity(from_search ? 1.f : 0.f);
-
-    if (!from_search)
-      AnimateInZeroState();
-    else
-      zero_state_view_->SetVisible(false);
-
-    AnimateInFooter();
+    InitializeUIForStartingSession(from_search);
     return;
   }
 
@@ -446,6 +436,10 @@
   footer_->SetCanProcessEventsWithinSubtree(true);
 }
 
+void AppListAssistantMainStage::InitializeUIForBubbleView() {
+  InitializeUIForStartingSession(/*from_search=*/false);
+}
+
 void AppListAssistantMainStage::MaybeHideZeroState() {
   if (!IsShown(zero_state_view_))
     return;
@@ -454,6 +448,22 @@
                                   kZeroStateAnimationFadeOutDuration);
 }
 
+void AppListAssistantMainStage::InitializeUIForStartingSession(
+    bool from_search) {
+  // When Assistant is starting a new session, we animate in the appearance of
+  // the zero state view and footer.
+  progress_indicator_->layer()->SetOpacity(0.f);
+  horizontal_separator_->layer()->SetOpacity(from_search ? 1.f : 0.f);
+
+  if (!from_search)
+    AnimateInZeroState();
+  else
+    zero_state_view_->SetVisible(false);
+
+  footer_->InitializeUIForBubbleView();
+  AnimateInFooter();
+}
+
 BEGIN_METADATA(AppListAssistantMainStage, views::View)
 END_METADATA
 
diff --git a/ash/app_list/views/assistant/assistant_main_stage.h b/ash/app_list/views/assistant/assistant_main_stage.h
index 38a4470b..e4c4d2a0 100644
--- a/ash/app_list/views/assistant/assistant_main_stage.h
+++ b/ash/app_list/views/assistant/assistant_main_stage.h
@@ -69,6 +69,8 @@
       absl::optional<AssistantEntryPoint> entry_point,
       absl::optional<AssistantExitPoint> exit_point) override;
 
+  void InitializeUIForBubbleView();
+
  private:
   void InitLayout();
   std::unique_ptr<views::View> CreateContentLayoutContainer();
@@ -80,6 +82,7 @@
   void AnimateInFooter();
 
   void MaybeHideZeroState();
+  void InitializeUIForStartingSession(bool from_search);
 
   AssistantViewDelegate* const delegate_;  // Owned by Shell.
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index ce7cd78..1e22bb1 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2118,13 +2118,25 @@
         ''' - '''
       </message>
       <!-- Status tray channel indicator strings -->
-      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_BETA" desc="The string used to indicate that this device is on the beta release track">
+      <message name="IDS_ASH_STATUS_TRAY_REPORT_FEEDBACK" desc="The string used for the tooltip and speakable name of the 'report feedback' button">
+        Report feedback
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_BETA" desc="The string used to show the name of beta release track, by itself">
+        Beta
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_DEV" desc="The string used to show the name of dev release track, by itself">
+        Dev
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_CANARY" desc="The string used to show the name of canary release track, by itself">
+        Canary
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL" desc="The string used to indicate that this device is on the beta release track">
         Beta Channel
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_DEV" desc="The string used to indicate that this device is on the development release track">
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_DEV_CHANNEL" desc="The string used to indicate that this device is on the development release track">
         Dev Channel
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_CANARY" desc="The string used to indicate that this device is on the canary release track">
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_CANARY_CHANNEL" desc="The string used to indicate that this device is on the canary release track">
         Canary Channel
       </message>
       <!-- Status Tray Network strings -->
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1
index 900cb87..e9fb273 100644
--- a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1
@@ -1 +1 @@
-a23e8925d97be40954790401e84089e09af5a456
\ No newline at end of file
+9089cfd7cee8fb5053295c5639a4a9e99055d090
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL.png.sha1
new file mode 100644
index 0000000..e9fb273
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL.png.sha1
@@ -0,0 +1 @@
+9089cfd7cee8fb5053295c5639a4a9e99055d090
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1
index 29eb38a..353ac92 100644
--- a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1
@@ -1 +1 @@
-11b35d936ec8e0b18154281d9cf2392ad822bf62
\ No newline at end of file
+8041c4627127d19acb3ba3a34b59a8bd4ebd10ce
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY_CHANNEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY_CHANNEL.png.sha1
new file mode 100644
index 0000000..353ac92
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY_CHANNEL.png.sha1
@@ -0,0 +1 @@
+8041c4627127d19acb3ba3a34b59a8bd4ebd10ce
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1
index a4516f6..48644be0d 100644
--- a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1
@@ -1 +1 @@
-b7a6ff57d02323da11175ecca7c8473ceed32487
\ No newline at end of file
+68a5f4e9505763877bfdd169cdcee7a27ccb7913
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV_CHANNEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV_CHANNEL.png.sha1
new file mode 100644
index 0000000..48644be0d
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV_CHANNEL.png.sha1
@@ -0,0 +1 @@
+68a5f4e9505763877bfdd169cdcee7a27ccb7913
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_REPORT_FEEDBACK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_REPORT_FEEDBACK.png.sha1
new file mode 100644
index 0000000..5d1e81e
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_REPORT_FEEDBACK.png.sha1
@@ -0,0 +1 @@
+54c61a6a20b846a2270c4bee736c419afb70697c
\ No newline at end of file
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.cc b/ash/assistant/ui/main_stage/assistant_footer_view.cc
index 8510126..51936a8 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.cc
@@ -88,7 +88,6 @@
   suggestion_container_->layer()->SetOpacity(consent_given ? 1.f : 0.f);
   suggestion_container_->SetVisible(consent_given);
 
-
   // Opt in view.
   opt_in_view_ = AddChildView(std::make_unique<AssistantOptInView>(delegate_));
   opt_in_view_->SetCanProcessEventsWithinSubtree(!consent_given);
@@ -145,6 +144,10 @@
   animation_observer_->SetActive();
 }
 
+void AssistantFooterView::InitializeUIForBubbleView() {
+  suggestion_container_->InitializeUIForBubbleView();
+}
+
 void AssistantFooterView::OnAnimationStarted(
     const ui::CallbackLayerAnimationObserver& observer) {
   // Our views should not process events while animating.
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.h b/ash/assistant/ui/main_stage/assistant_footer_view.h
index e891ce3..b0686a9 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.h
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.h
@@ -41,6 +41,8 @@
   // AssistantStateObserver:
   void OnAssistantConsentStatusChanged(int consent_status) override;
 
+  void InitializeUIForBubbleView();
+
  private:
   void InitLayout();
 
diff --git a/ash/assistant/ui/main_stage/suggestion_container_view.cc b/ash/assistant/ui/main_stage/suggestion_container_view.cc
index 997b146..8d00e5d 100644
--- a/ash/assistant/ui/main_stage/suggestion_container_view.cc
+++ b/ash/assistant/ui/main_stage/suggestion_container_view.cc
@@ -249,6 +249,12 @@
       views::BoxLayout::MainAxisAlignment::kCenter);
 }
 
+void SuggestionContainerView::InitializeUIForBubbleView() {
+  OnConversationStartersChanged(AssistantSuggestionsController::Get()
+                                    ->GetModel()
+                                    ->GetConversationStarters());
+}
+
 void SuggestionContainerView::OnButtonPressed(SuggestionChipView* chip_view) {
   // Remember which chip was selected, so we can give it a special animation.
   selected_chip_ = chip_view;
diff --git a/ash/assistant/ui/main_stage/suggestion_container_view.h b/ash/assistant/ui/main_stage/suggestion_container_view.h
index 34c5e72..b4be0f4 100644
--- a/ash/assistant/ui/main_stage/suggestion_container_view.h
+++ b/ash/assistant/ui/main_stage/suggestion_container_view.h
@@ -61,6 +61,8 @@
       absl::optional<AssistantEntryPoint> entry_point,
       absl::optional<AssistantExitPoint> exit_point) override;
 
+  void InitializeUIForBubbleView();
+
   // The suggestion chip that was pressed by the user. May be |nullptr|.
   const SuggestionChipView* selected_chip() const { return selected_chip_; }
 
diff --git a/ash/components/arc/arc_features_parser.cc b/ash/components/arc/arc_features_parser.cc
index 762ec0c..bb55597 100644
--- a/ash/components/arc/arc_features_parser.cc
+++ b/ash/components/arc/arc_features_parser.cc
@@ -40,38 +40,37 @@
     return absl::nullopt;
   }
 
+  const base::Value::Dict& dict = parsed_json->GetDict();
+
   // Parse each item under features.
-  const base::Value* feature_list =
-      parsed_json->FindKeyOfType("features", base::Value::Type::LIST);
+  const base::Value::List* feature_list = dict.FindList("features");
   if (!feature_list) {
     LOG(ERROR) << "No feature list in JSON.";
     return absl::nullopt;
   }
-  for (auto& feature_item : feature_list->GetListDeprecated()) {
-    const base::Value* feature_name =
-        feature_item.FindKeyOfType("name", base::Value::Type::STRING);
-    const base::Value* feature_version =
-        feature_item.FindKeyOfType("version", base::Value::Type::INTEGER);
-    if (!feature_name || feature_name->GetString().empty()) {
+  for (auto& feature_item : *feature_list) {
+    const std::string* feature_name = feature_item.GetDict().FindString("name");
+    const absl::optional<int> feature_version =
+        feature_item.GetDict().FindInt("version");
+    if (!feature_name || feature_name->empty()) {
       LOG(ERROR) << "Missing name in the feature.";
       return absl::nullopt;
     }
-    if (!feature_version) {
+    if (!feature_version.has_value()) {
       LOG(ERROR) << "Missing version in the feature.";
       return absl::nullopt;
     }
-    arc_features.feature_map.emplace(feature_name->GetString(),
-                                     feature_version->GetInt());
+    arc_features.feature_map.emplace(*feature_name, *feature_version);
   }
 
   // Parse each item under unavailable_features.
-  const base::Value* unavailable_feature_list = parsed_json->FindKeyOfType(
-      "unavailable_features", base::Value::Type::LIST);
+  const base::Value::List* unavailable_feature_list =
+      dict.FindList("unavailable_features");
   if (!unavailable_feature_list) {
     LOG(ERROR) << "No unavailable feature list in JSON.";
     return absl::nullopt;
   }
-  for (auto& feature_item : unavailable_feature_list->GetListDeprecated()) {
+  for (auto& feature_item : *unavailable_feature_list) {
     if (!feature_item.is_string()) {
       LOG(ERROR) << "Item in the unavailable feature list is not a string.";
       return absl::nullopt;
@@ -85,13 +84,12 @@
   }
 
   // Parse each item under properties.
-  const base::Value* properties =
-      parsed_json->FindKeyOfType("properties", base::Value::Type::DICTIONARY);
+  const base::Value::Dict* properties = dict.FindDict("properties");
   if (!properties) {
     LOG(ERROR) << "No properties in JSON.";
     return absl::nullopt;
   }
-  for (const auto item : properties->DictItems()) {
+  for (const auto item : *properties) {
     if (!item.second.is_string()) {
       LOG(ERROR) << "Item in the properties mapping is not a string.";
       return absl::nullopt;
@@ -101,13 +99,12 @@
   }
 
   // Parse the Play Store version
-  const base::Value* play_version = parsed_json->FindKeyOfType(
-      "play_store_version", base::Value::Type::STRING);
+  const std::string* play_version = dict.FindString("play_store_version");
   if (!play_version) {
     LOG(ERROR) << "No Play Store version in JSON.";
     return absl::nullopt;
   }
-  arc_features.play_store_version = play_version->GetString();
+  arc_features.play_store_version = *play_version;
 
   return arc_features;
 }
diff --git a/ash/components/arc/enterprise/snapshot_hours_policy_service.cc b/ash/components/arc/enterprise/snapshot_hours_policy_service.cc
index 5d95260..fae6ba1 100644
--- a/ash/components/arc/enterprise/snapshot_hours_policy_service.cc
+++ b/ash/components/arc/enterprise/snapshot_hours_policy_service.cc
@@ -90,11 +90,10 @@
   if (!IsArcEnabled())
     return;
 
-  const auto* dict = local_state_->GetDictionary(prefs::kArcSnapshotHours);
-  if (!dict)
-    return;
+  const base::Value::Dict& dict =
+      local_state_->GetValueDict(prefs::kArcSnapshotHours);
 
-  const auto* timezone = dict->FindStringKey("timezone");
+  const auto* timezone = dict.FindString("timezone");
   std::string timezone_str = "";
   if (!timezone || *timezone == "UNSET") {
     std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::detectHostTimeZone());
@@ -111,11 +110,11 @@
     return;
   }
 
-  const auto* intervals = dict->FindListKey("intervals");
+  const auto* intervals = dict.FindList("intervals");
   if (!intervals)
     return;
 
-  for (const auto& entry : intervals->GetListDeprecated()) {
+  for (const auto& entry : *intervals) {
     if (!entry.is_dict())
       continue;
     auto interval =
diff --git a/ash/components/tether/tether_host_response_recorder.cc b/ash/components/tether/tether_host_response_recorder.cc
index 089b7d8..8a89e52 100644
--- a/ash/components/tether/tether_host_response_recorder.cc
+++ b/ash/components/tether/tether_host_response_recorder.cc
@@ -77,8 +77,7 @@
 bool TetherHostResponseRecorder::AddRecentResponse(
     const std::string& device_id,
     const std::string& pref_name) {
-  const base::Value* ids = pref_service_->GetList(pref_name);
-  base::Value::ConstListView ids_list = ids->GetListDeprecated();
+  const base::Value::List& ids_list = pref_service_->GetValueList(pref_name);
 
   std::string first_device_id_in_list;
   if (!ids_list.empty() && ids_list[0].is_string())
@@ -90,21 +89,18 @@
     return false;
   }
 
-  // Create a mutable copy of the stored IDs, or create one if it has yet to be
-  // stored.
-  base::Value updated_ids =
-      ids ? ids->Clone() : base::Value(base::Value::Type::LIST);
+  // Create a mutable copy of the stored IDs.
+  base::Value::List updated_ids = ids_list.Clone();
 
   // Remove the device ID if it was already present in the list.
   base::Value device_id_value(device_id);
-  updated_ids.EraseListValue(device_id_value);
+  updated_ids.EraseValue(device_id_value);
 
   // Add the device ID to the front of the queue.
-  updated_ids.Insert(updated_ids.GetListDeprecated().begin(),
-                     std::move(device_id_value));
+  updated_ids.Insert(updated_ids.begin(), std::move(device_id_value));
 
   // Store the updated list back in |pref_service_|.
-  pref_service_->Set(pref_name, std::move(updated_ids));
+  pref_service_->SetList(pref_name, std::move(updated_ids));
 
   return true;
 }
@@ -113,11 +109,8 @@
     const std::string& pref_name) const {
   std::vector<std::string> device_ids;
 
-  const base::Value* ids = pref_service_->GetList(pref_name);
-  if (!ids)
-    return device_ids;
-
-  for (const auto& entry : ids->GetListDeprecated()) {
+  const base::Value::List& ids = pref_service_->GetValueList(pref_name);
+  for (const auto& entry : ids) {
     if (entry.is_string())
       device_ids.push_back(entry.GetString());
   }
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index 9bc3623..990d389e 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -407,10 +407,10 @@
 // Loads mirror info for each external display, the info will later be used to
 // restore mirror mode.
 void LoadExternalDisplayMirrorInfo(PrefService* local_state) {
-  const base::Value* pref_data =
-      local_state->Get(prefs::kExternalDisplayMirrorInfo);
+  const base::Value::List& pref_data =
+      local_state->GetValueList(prefs::kExternalDisplayMirrorInfo);
   std::set<int64_t> external_display_mirror_info;
-  for (const auto& it : pref_data->GetListDeprecated()) {
+  for (const auto& it : pref_data) {
     const std::string* display_id_str = it.GetIfString();
     if (!display_id_str)
       continue;
@@ -428,14 +428,14 @@
 // Loads mixed mirror mode parameters which will later be used to restore mixed
 // mirror mode. Return false if the parameters fail to be loaded.
 void LoadDisplayMixedMirrorModeParams(PrefService* local_state) {
-  const base::Value* pref_data =
-      local_state->Get(prefs::kDisplayMixedMirrorModeParams);
+  const base::Value::Dict& pref_data =
+      local_state->GetValueDict(prefs::kDisplayMixedMirrorModeParams);
 
   // This function is called once for system (re)start, so the parameters should
   // be empty.
   DCHECK(!GetDisplayManager()->mixed_mirror_mode_params());
 
-  auto* mirroring_source_id_value = pref_data->FindKey(kMirroringSourceId);
+  auto* mirroring_source_id_value = pref_data.Find(kMirroringSourceId);
   if (!mirroring_source_id_value)
     return;
 
@@ -447,14 +447,13 @@
   }
 
   auto* mirroring_destination_ids_value =
-      pref_data->FindKey(kMirroringDestinationIds);
+      pref_data.Find(kMirroringDestinationIds);
   if (!mirroring_destination_ids_value)
     return;
 
   DCHECK(mirroring_destination_ids_value->is_list());
   display::DisplayIdList mirroring_destination_ids;
-  for (const auto& entry :
-       mirroring_destination_ids_value->GetListDeprecated()) {
+  for (const auto& entry : mirroring_destination_ids_value->GetList()) {
     DCHECK(entry.is_string());
     int64_t id;
     if (!base::StringToInt64(entry.GetString(), &id))
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index e95e0c2b..2d388d8d 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -260,15 +260,14 @@
                     "mirroring_source_id="
                  << source_id << " and mirroring_destination_ids="
                  << base::JoinString(expected_dest_id_strs, ","));
-    const base::Value* prefs =
-        local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
-    ASSERT_TRUE(prefs);
-    EXPECT_THAT(prefs->FindStringKey("mirroring_source_id"),
+    const base::Value::Dict& prefs =
+        local_state()->GetValueDict(prefs::kDisplayMixedMirrorModeParams);
+    EXPECT_THAT(prefs.FindString("mirroring_source_id"),
                 testing::Pointee(base::NumberToString(source_id)));
-    const auto* mirror_ids = prefs->FindListKey("mirroring_destination_ids");
+    const auto* mirror_ids = prefs.FindList("mirroring_destination_ids");
     ASSERT_TRUE(mirror_ids);
     display::DisplayIdList pref_dest_ids;
-    for (const auto& value : mirror_ids->GetListDeprecated()) {
+    for (const auto& value : *mirror_ids) {
       int64_t id;
       EXPECT_TRUE(base::StringToInt64(value.GetString(), &id));
       pref_dest_ids.push_back(id);
@@ -285,10 +284,10 @@
         testing::Message()
         << "Expected to read kExternalDisplayMirrorInfo with list values="
         << base::JoinString(expected_display_id_strs, ","));
-    const base::Value& prefs =
-        local_state()->GetValue(prefs::kExternalDisplayMirrorInfo);
+    const base::Value::List& prefs =
+        local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
     std::set<int64_t> read_ids;
-    for (const auto& value : prefs.GetListDeprecated()) {
+    for (const auto& value : prefs) {
       int64_t id;
       EXPECT_TRUE(base::StringToInt64(value.GetString(), &id));
       read_ids.insert(id);
@@ -463,9 +462,9 @@
   EXPECT_EQ(dummy_layout->placement_list[0].offset,
             stored_layout.placement_list[0].offset);
 
-  const base::Value* external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(0U, external_display_mirror_info->GetListDeprecated().size());
+  const base::Value::List* external_display_mirror_info =
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(0U, external_display_mirror_info->size());
 
   const base::Value* properties =
       local_state()->GetDictionary(prefs::kDisplayProperties);
@@ -596,11 +595,11 @@
   EXPECT_FALSE(property->FindIntKey("height"));
 
   external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(1U, external_display_mirror_info->GetListDeprecated().size());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(1U, external_display_mirror_info->size());
   // ExternalDisplayInfo stores ID without output index.
   EXPECT_EQ(base::NumberToString(display::GetDisplayIdWithoutOutputIndex(id2)),
-            external_display_mirror_info->GetListDeprecated()[0].GetString());
+            (*external_display_mirror_info)[0].GetString());
 
   // External display's selected resolution must not change
   // by mirroring.
@@ -1422,12 +1421,11 @@
   external_display_mirror_info.emplace(first_display_masked_id);
   StoreExternalDisplayMirrorInfo(external_display_mirror_info);
   LoadDisplayPreferences();
-  const base::Value* pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(1U, pref_external_display_mirror_info->GetListDeprecated().size());
-  EXPECT_EQ(
-      base::NumberToString(first_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[0].GetString());
+  const base::Value::List* pref_external_display_mirror_info =
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(1U, pref_external_display_mirror_info->size());
+  EXPECT_EQ(base::NumberToString(first_display_masked_id),
+            (*pref_external_display_mirror_info)[0].GetString());
 
   // Add first display, mirror mode restores and the external display mirror
   // info does not change.
@@ -1435,11 +1433,10 @@
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_TRUE(display_manager()->IsInMirrorMode());
   pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(1U, pref_external_display_mirror_info->GetListDeprecated().size());
-  EXPECT_EQ(
-      base::NumberToString(first_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[0].GetString());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(1U, pref_external_display_mirror_info->size());
+  EXPECT_EQ(base::NumberToString(first_display_masked_id),
+            (*pref_external_display_mirror_info)[0].GetString());
 
   // Add second display, mirror mode persists and the second display id is added
   // to the external display mirror info.
@@ -1447,14 +1444,12 @@
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_TRUE(display_manager()->IsInMirrorMode());
   pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(2U, pref_external_display_mirror_info->GetListDeprecated().size());
-  EXPECT_EQ(
-      base::NumberToString(first_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[0].GetString());
-  EXPECT_EQ(
-      base::NumberToString(second_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[1].GetString());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(2U, pref_external_display_mirror_info->size());
+  EXPECT_EQ(base::NumberToString(first_display_masked_id),
+            (*pref_external_display_mirror_info)[0].GetString());
+  EXPECT_EQ(base::NumberToString(second_display_masked_id),
+            (*pref_external_display_mirror_info)[1].GetString());
 
   // Disconnect all external displays.
   display_info_list.erase(display_info_list.begin() + 1,
@@ -1467,11 +1462,10 @@
   StoreExternalDisplayMirrorInfo(external_display_mirror_info);
   LoadDisplayPreferences();
   pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(1U, pref_external_display_mirror_info->GetListDeprecated().size());
-  EXPECT_EQ(
-      base::NumberToString(second_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[0].GetString());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(1U, pref_external_display_mirror_info->size());
+  EXPECT_EQ(base::NumberToString(second_display_masked_id),
+            (*pref_external_display_mirror_info)[0].GetString());
 
   // Add first display, mirror mode is off and the external display mirror info
   // does not change.
@@ -1479,11 +1473,10 @@
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_FALSE(display_manager()->IsInMirrorMode());
   pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(1U, pref_external_display_mirror_info->GetListDeprecated().size());
-  EXPECT_EQ(
-      base::NumberToString(second_display_masked_id),
-      pref_external_display_mirror_info->GetListDeprecated()[0].GetString());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(1U, pref_external_display_mirror_info->size());
+  EXPECT_EQ(base::NumberToString(second_display_masked_id),
+            (*pref_external_display_mirror_info)[0].GetString());
 
   // Add second display, mirror mode remains off and the second display id is
   // removed from the external display mirror info.
@@ -1491,8 +1484,8 @@
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_FALSE(display_manager()->IsInMirrorMode());
   pref_external_display_mirror_info =
-      local_state()->GetList(prefs::kExternalDisplayMirrorInfo);
-  EXPECT_EQ(0U, pref_external_display_mirror_info->GetListDeprecated().size());
+      &local_state()->GetValueList(prefs::kExternalDisplayMirrorInfo);
+  EXPECT_EQ(0U, pref_external_display_mirror_info->size());
 }
 
 TEST_F(DisplayPrefsTest, ExternalDisplayConnectedBeforeLoadingPrefs) {
@@ -1576,15 +1569,15 @@
   EXPECT_EQ(first_display_id, destination_ids[0]);
 
   // Check the preferences.
-  const base::Value* pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
+  const base::Value::Dict* pref_data =
+      &local_state()->GetValueDict(prefs::kDisplayMixedMirrorModeParams);
   EXPECT_EQ(base::NumberToString(internal_display_id),
-            pref_data->FindKey("mirroring_source_id")->GetString());
-  const base::Value* destination_ids_value =
-      pref_data->FindKey("mirroring_destination_ids");
-  EXPECT_EQ(1U, destination_ids_value->GetListDeprecated().size());
+            *pref_data->FindString("mirroring_source_id"));
+  const base::Value::List* destination_ids_list =
+      pref_data->FindList("mirroring_destination_ids");
+  EXPECT_EQ(1U, destination_ids_list->size());
   EXPECT_EQ(base::NumberToString(first_display_id),
-            destination_ids_value->GetListDeprecated()[0].GetString());
+            (*destination_ids_list)[0].GetString());
 
   // Overwrite current mixed mirror mode with a new configuration. (Mirror from
   // the first external display to the second external display)
@@ -1602,13 +1595,13 @@
 
   // Check the preferences.
   pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
+      &local_state()->GetValueDict(prefs::kDisplayMixedMirrorModeParams);
   EXPECT_EQ(base::NumberToString(first_display_id),
-            pref_data->FindKey("mirroring_source_id")->GetString());
-  destination_ids_value = pref_data->FindKey("mirroring_destination_ids");
-  EXPECT_EQ(1U, destination_ids_value->GetListDeprecated().size());
+            *pref_data->FindString("mirroring_source_id"));
+  destination_ids_list = pref_data->FindList("mirroring_destination_ids");
+  EXPECT_EQ(1U, destination_ids_list->size());
   EXPECT_EQ(base::NumberToString(second_display_id),
-            destination_ids_value->GetListDeprecated()[0].GetString());
+            (*destination_ids_list)[0].GetString());
 
   // Turn off mirror mode.
   display_manager()->SetMirrorMode(display::MirrorMode::kOff, absl::nullopt);
@@ -1616,8 +1609,8 @@
 
   // Check the preferences.
   pref_data =
-      local_state()->GetDictionary(prefs::kDisplayMixedMirrorModeParams);
-  EXPECT_TRUE(pref_data->DictEmpty());
+      &local_state()->GetValueDict(prefs::kDisplayMixedMirrorModeParams);
+  EXPECT_TRUE(pref_data->empty());
 }
 
 TEST_F(DisplayPrefsTest, SaveTabletModeWithSingleDisplay) {
diff --git a/ash/public/cpp/system_tray_client.h b/ash/public/cpp/system_tray_client.h
index d14a4bd..646b681 100644
--- a/ash/public/cpp/system_tray_client.h
+++ b/ash/public/cpp/system_tray_client.h
@@ -159,6 +159,14 @@
                                  bool& opened_pwa,
                                  GURL& finalized_event_url) = 0;
 
+  // Shown when the device is on a non-stable release track and the user clicks
+  // the channel/version button from quick settings.
+  virtual void ShowChannelInfoAdditionalDetails() = 0;
+
+  // Shown when the device is on a non-stable release track and the user clicks
+  // the "send feedback" button.
+  virtual void ShowChannelInfoGiveFeedback() = 0;
+
  protected:
   SystemTrayClient() {}
 };
diff --git a/ash/public/cpp/test/test_system_tray_client.cc b/ash/public/cpp/test/test_system_tray_client.cc
index a7c1532..49284e6 100644
--- a/ash/public/cpp/test/test_system_tray_client.cc
+++ b/ash/public/cpp/test/test_system_tray_client.cc
@@ -120,4 +120,8 @@
     bool& opened_pwa,
     GURL& final_event_url) {}
 
+void TestSystemTrayClient::ShowChannelInfoAdditionalDetails() {}
+
+void TestSystemTrayClient::ShowChannelInfoGiveFeedback() {}
+
 }  // namespace ash
diff --git a/ash/public/cpp/test/test_system_tray_client.h b/ash/public/cpp/test/test_system_tray_client.h
index 4db1910..4eb3398 100644
--- a/ash/public/cpp/test/test_system_tray_client.h
+++ b/ash/public/cpp/test/test_system_tray_client.h
@@ -66,6 +66,8 @@
                          const base::Time& date,
                          bool& opened_pwa,
                          GURL& final_event_url) override;
+  void ShowChannelInfoAdditionalDetails() override;
+  void ShowChannelInfoGiveFeedback() override;
 
   int show_bluetooth_settings_count() const {
     return show_bluetooth_settings_count_;
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index a6d5ed8..4af6f8b 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -250,6 +250,7 @@
     "reorder_nudge_dark_tablet.icon",
     "reorder_nudge_light_clamshell.icon",
     "reorder_nudge_light_tablet.icon",
+    "request_feedback.icon",
     "resume.icon",
     "save_desk_as_template.icon",
     "save_desk_for_later.icon",
diff --git a/ash/resources/vector_icons/request_feedback.icon b/ash/resources/vector_icons/request_feedback.icon
new file mode 100644
index 0000000..f4714a5
--- /dev/null
+++ b/ash/resources/vector_icons/request_feedback.icon
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 9, 5,
+H_LINE_TO, 11,
+V_LINE_TO, 9,
+H_LINE_TO, 9,
+V_LINE_TO, 5,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 9, 10,
+H_LINE_TO, 11,
+V_LINE_TO, 12,
+H_LINE_TO, 9,
+V_LINE_TO, 10,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 3.67f, 2,
+H_LINE_TO, 16,
+CUBIC_TO, 16.92f, 2, 18, 2.58f, 18, 3.5f,
+V_LINE_TO, 13.5f,
+CUBIC_TO, 18, 14.42f, 16.92f, 15, 16, 15,
+H_LINE_TO, 5,
+LINE_TO, 2, 17.5f,
+LINE_TO, 2.01f, 3.67f,
+CUBIC_TO, 2.01f, 2.75f, 2.75f, 2, 3.67f, 2,
+CLOSE,
+MOVE_TO, 4, 13,
+H_LINE_TO, 16,
+V_LINE_TO, 4,
+H_LINE_TO, 4,
+V_LINE_TO, 13,
+CLOSE
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl.cc b/ash/services/device_sync/cryptauth_device_manager_impl.cc
index 99f8e4e..10d94f9 100644
--- a/ash/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_manager_impl.cc
@@ -287,9 +287,9 @@
 }
 
 void AddBeaconSeedsToExternalDevice(
-    const base::Value& beacon_seeds,
+    const base::Value::List& beacon_seeds,
     cryptauth::ExternalDeviceInfo* external_device) {
-  for (const base::Value& seed_dictionary : beacon_seeds.GetListDeprecated()) {
+  for (const base::Value& seed_dictionary : beacon_seeds) {
     if (!seed_dictionary.is_dict()) {
       PA_LOG(WARNING) << "Unable to retrieve BeaconSeed dictionary; "
                       << "skipping.";
@@ -334,11 +334,11 @@
 }
 
 void AddSoftwareFeaturesToExternalDevice(
-    const base::Value& software_features_dictionary,
+    const base::Value::Dict& software_features_dictionary,
     cryptauth::ExternalDeviceInfo* external_device,
     bool old_unlock_key_value_from_prefs,
     bool old_mobile_hotspot_supported_from_prefs) {
-  for (const auto it : software_features_dictionary.DictItems()) {
+  for (const auto it : software_features_dictionary) {
     std::string software_feature = it.first;
     if (SoftwareFeatureStringToEnum(software_feature) ==
         cryptauth::SoftwareFeature::UNKNOWN_FEATURE) {
@@ -406,10 +406,10 @@
 // Converts an unlock key dictionary stored in user prefs to an
 // cryptauth::ExternalDeviceInfo proto. Returns true if the dictionary is valid,
 // and the parsed proto is written to |external_device|.
-bool DictionaryToUnlockKey(const base::Value& dictionary,
+bool DictionaryToUnlockKey(const base::Value::Dict& dictionary,
                            cryptauth::ExternalDeviceInfo* external_device) {
   const std::string* public_key_b64 =
-      dictionary.FindStringKey(kExternalDeviceKeyPublicKey);
+      dictionary.FindString(kExternalDeviceKeyPublicKey);
   if (!public_key_b64) {
     // The public key is a required field, so if it is absent, there is no
     // valid data to return.
@@ -427,7 +427,7 @@
   external_device->set_public_key(public_key);
 
   const std::string* device_name_b64 =
-      dictionary.FindStringKey(kExternalDeviceKeyDeviceName);
+      dictionary.FindString(kExternalDeviceKeyDeviceName);
   if (device_name_b64) {
     std::string device_name;
     if (base::Base64UrlDecode(*device_name_b64,
@@ -438,7 +438,7 @@
   }
 
   const std::string* bluetooth_address_b64 =
-      dictionary.FindStringKey(kExternalDeviceKeyBluetoothAddress);
+      dictionary.FindString(kExternalDeviceKeyBluetoothAddress);
   if (bluetooth_address_b64) {
     std::string bluetooth_address;
     if (base::Base64UrlDecode(*bluetooth_address_b64,
@@ -451,12 +451,12 @@
   // TODO(crbug.com/848477): Migrate |unlockable| into
   // |supported_software_features|.
   absl::optional<bool> unlockable =
-      dictionary.FindBoolKey(kExternalDeviceKeyUnlockable);
+      dictionary.FindBool(kExternalDeviceKeyUnlockable);
   if (unlockable.has_value())
     external_device->set_unlockable(unlockable.value());
 
   const std::string* last_update_time_millis_str =
-      dictionary.FindStringKey(kExternalDeviceKeyLastUpdateTimeMillis);
+      dictionary.FindString(kExternalDeviceKeyLastUpdateTimeMillis);
   if (last_update_time_millis_str) {
     int64_t last_update_time_millis;
     if (base::StringToInt64(*last_update_time_millis_str,
@@ -469,30 +469,30 @@
   }
 
   absl::optional<int> device_type =
-      dictionary.FindIntKey(kExternalDeviceKeyDeviceType);
+      dictionary.FindInt(kExternalDeviceKeyDeviceType);
   if (device_type.has_value() &&
       cryptauth::DeviceType_IsValid(device_type.value())) {
     external_device->set_device_type(DeviceTypeEnumToString(
         static_cast<cryptauth::DeviceType>(device_type.value())));
   }
 
-  const base::Value* beacon_seeds =
-      dictionary.FindListKey(kExternalDeviceKeyBeaconSeeds);
+  const base::Value::List* beacon_seeds =
+      dictionary.FindList(kExternalDeviceKeyBeaconSeeds);
   if (beacon_seeds)
     AddBeaconSeedsToExternalDevice(*beacon_seeds, external_device);
 
   absl::optional<bool> arc_plus_plus =
-      dictionary.FindBoolKey(kExternalDeviceKeyArcPlusPlus);
+      dictionary.FindBool(kExternalDeviceKeyArcPlusPlus);
   if (arc_plus_plus.has_value())
     external_device->set_arc_plus_plus(arc_plus_plus.value());
 
   absl::optional<bool> pixel_phone =
-      dictionary.FindBoolKey(kExternalDeviceKeyPixelPhone);
+      dictionary.FindBool(kExternalDeviceKeyPixelPhone);
   if (pixel_phone.has_value())
     external_device->set_pixel_phone(pixel_phone.value());
 
   const std::string* no_pii_device_name_b64 =
-      dictionary.FindStringKey(kExternalDeviceKeyNoPiiDeviceName);
+      dictionary.FindString(kExternalDeviceKeyNoPiiDeviceName);
   if (no_pii_device_name_b64) {
     std::string no_pii_device_name;
     if (base::Base64UrlDecode(*no_pii_device_name_b64,
@@ -503,12 +503,12 @@
   }
 
   absl::optional<bool> unlock_key =
-      dictionary.FindBoolKey(kExternalDeviceKeyUnlockKey);
+      dictionary.FindBool(kExternalDeviceKeyUnlockKey);
   absl::optional<bool> mobile_hotspot_supported =
-      dictionary.FindBoolKey(kExternalDeviceKeyMobileHotspotSupported);
+      dictionary.FindBool(kExternalDeviceKeyMobileHotspotSupported);
 
-  const base::Value* software_features_dictionary =
-      dictionary.FindDictKey(kDictionaryKeySoftwareFeatures);
+  const base::Value::Dict* software_features_dictionary =
+      dictionary.FindDict(kDictionaryKeySoftwareFeatures);
   if (software_features_dictionary) {
     AddSoftwareFeaturesToExternalDevice(
         *software_features_dictionary, external_device,
@@ -755,7 +755,7 @@
   for (const auto& it : unlock_key_list) {
     if (it.is_dict()) {
       cryptauth::ExternalDeviceInfo unlock_key;
-      if (DictionaryToUnlockKey(it, &unlock_key)) {
+      if (DictionaryToUnlockKey(it.GetDict(), &unlock_key)) {
         synced_devices_.push_back(unlock_key);
       } else {
         PA_LOG(ERROR) << "Unable to deserialize unlock key dictionary:\n" << it;
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index 2b7a2e0f..d8a06541 100644
--- a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -184,22 +184,21 @@
     const PrefService& pref_service) {
   ExpectSyncedDevicesAreEqual(expected_devices, devices);
 
-  const base::Value* synced_devices_pref =
-      pref_service.GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
-  ASSERT_EQ(expected_devices.size(),
-            synced_devices_pref->GetListDeprecated().size());
-  for (size_t i = 0; i < synced_devices_pref->GetListDeprecated().size(); ++i) {
+  const base::Value::List& synced_devices_pref =
+      pref_service.GetValueList(prefs::kCryptAuthDeviceSyncUnlockKeys);
+  ASSERT_EQ(expected_devices.size(), synced_devices_pref.size());
+  for (size_t i = 0; i < synced_devices_pref.size(); ++i) {
     SCOPED_TRACE(base::StringPrintf("Compare pref dictionary at index=%d",
                                     static_cast<int>(i)));
-    const base::Value& device_dictionary =
-        synced_devices_pref->GetListDeprecated()[i];
-    EXPECT_TRUE(device_dictionary.is_dict());
+    const base::Value::Dict* device_dictionary =
+        synced_devices_pref[i].GetIfDict();
+    EXPECT_TRUE(device_dictionary);
 
     const auto& expected_device = expected_devices[i];
 
     std::string public_key;
     const std::string* public_key_b64 =
-        device_dictionary.FindStringKey("public_key");
+        device_dictionary->FindString("public_key");
     EXPECT_TRUE(public_key_b64);
     EXPECT_TRUE(base::Base64UrlDecode(
         *public_key_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
@@ -209,7 +208,7 @@
 
     std::string device_name;
     const std::string* device_name_b64 =
-        device_dictionary.FindStringKey("device_name");
+        device_dictionary->FindString("device_name");
     if (device_name_b64) {
       EXPECT_TRUE(base::Base64UrlDecode(
           *device_name_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
@@ -222,7 +221,7 @@
 
     std::string no_pii_device_name;
     const std::string* no_pii_device_name_b64 =
-        device_dictionary.FindStringKey("no_pii_device_name");
+        device_dictionary->FindString("no_pii_device_name");
     if (no_pii_device_name_b64) {
       EXPECT_TRUE(base::Base64UrlDecode(
           *no_pii_device_name_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
@@ -235,7 +234,7 @@
 
     std::string bluetooth_address;
     const std::string* bluetooth_address_b64 =
-        device_dictionary.FindStringKey("bluetooth_address");
+        device_dictionary->FindString("bluetooth_address");
     if (bluetooth_address_b64) {
       EXPECT_TRUE(base::Base64UrlDecode(
           *bluetooth_address_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
@@ -246,8 +245,7 @@
       EXPECT_FALSE(expected_device.has_bluetooth_address());
     }
 
-    absl::optional<bool> unlock_key =
-        device_dictionary.FindBoolKey("unlock_key");
+    absl::optional<bool> unlock_key = device_dictionary->FindBool("unlock_key");
     if (unlock_key.has_value()) {
       EXPECT_TRUE(expected_device.has_unlock_key());
       EXPECT_EQ(expected_device.unlock_key(), unlock_key.value());
@@ -255,8 +253,7 @@
       EXPECT_FALSE(expected_device.has_unlock_key());
     }
 
-    absl::optional<bool> unlockable =
-        device_dictionary.FindBoolKey("unlockable");
+    absl::optional<bool> unlockable = device_dictionary->FindBool("unlockable");
     if (unlockable.has_value()) {
       EXPECT_TRUE(expected_device.has_unlockable());
       EXPECT_EQ(expected_device.unlockable(), unlockable.value());
@@ -265,7 +262,7 @@
     }
 
     const std::string* last_update_time_millis_str =
-        device_dictionary.FindStringKey("last_update_time_millis");
+        device_dictionary->FindString("last_update_time_millis");
     if (last_update_time_millis_str) {
       int64_t last_update_time_millis;
       EXPECT_TRUE(base::StringToInt64(*last_update_time_millis_str,
@@ -278,7 +275,7 @@
     }
 
     absl::optional<bool> mobile_hotspot_supported =
-        device_dictionary.FindBoolKey("mobile_hotspot_supported");
+        device_dictionary->FindBool("mobile_hotspot_supported");
     if (mobile_hotspot_supported.has_value()) {
       EXPECT_TRUE(expected_device.has_mobile_hotspot_supported());
       EXPECT_EQ(expected_device.mobile_hotspot_supported(),
@@ -287,8 +284,7 @@
       EXPECT_FALSE(expected_device.has_mobile_hotspot_supported());
     }
 
-    absl::optional<int> device_type =
-        device_dictionary.FindIntKey("device_type");
+    absl::optional<int> device_type = device_dictionary->FindInt("device_type");
     if (device_type.has_value()) {
       EXPECT_TRUE(expected_device.has_device_type());
       EXPECT_EQ(DeviceTypeStringToEnum(expected_device.device_type()),
@@ -296,15 +292,13 @@
     } else {
       EXPECT_FALSE(expected_device.has_device_type());
     }
-    const base::Value* beacon_seeds_from_prefs =
-        device_dictionary.FindListKey("beacon_seeds");
+    const base::Value::List* beacon_seeds_from_prefs =
+        device_dictionary->FindList("beacon_seeds");
     if (beacon_seeds_from_prefs) {
       ASSERT_EQ(static_cast<size_t>(expected_device.beacon_seeds_size()),
-                beacon_seeds_from_prefs->GetListDeprecated().size());
-      for (size_t i = 0;
-           i < beacon_seeds_from_prefs->GetListDeprecated().size(); i++) {
-        const base::Value& seed =
-            beacon_seeds_from_prefs->GetListDeprecated()[i];
+                beacon_seeds_from_prefs->size());
+      for (size_t i = 0; i < beacon_seeds_from_prefs->size(); i++) {
+        const base::Value& seed = (*beacon_seeds_from_prefs)[i];
         ASSERT_TRUE(seed.is_dict());
 
         const std::string* data_b64 = seed.FindStringKey("beacon_seed_data");
@@ -337,7 +331,7 @@
     }
 
     absl::optional<bool> arc_plus_plus =
-        device_dictionary.FindBoolKey("arc_plus_plus");
+        device_dictionary->FindBool("arc_plus_plus");
     if (arc_plus_plus.has_value()) {
       EXPECT_TRUE(expected_device.has_arc_plus_plus());
       EXPECT_EQ(expected_device.arc_plus_plus(), arc_plus_plus.value());
@@ -346,7 +340,7 @@
     }
 
     absl::optional<bool> pixel_phone =
-        device_dictionary.FindBoolKey("pixel_phone");
+        device_dictionary->FindBool("pixel_phone");
     if (pixel_phone.has_value()) {
       EXPECT_TRUE(expected_device.has_pixel_phone());
       EXPECT_EQ(expected_device.pixel_phone(), pixel_phone.value());
@@ -354,13 +348,13 @@
       EXPECT_FALSE(expected_device.has_pixel_phone());
     }
 
-    const base::Value* software_features_from_prefs =
-        device_dictionary.FindDictKey("software_features");
+    const base::Value::Dict* software_features_from_prefs =
+        device_dictionary->FindDict("software_features");
     if (software_features_from_prefs) {
       std::vector<cryptauth::SoftwareFeature> supported_software_features;
       std::vector<cryptauth::SoftwareFeature> enabled_software_features;
 
-      for (const auto it : software_features_from_prefs->DictItems()) {
+      for (const auto it : *software_features_from_prefs) {
         ASSERT_TRUE(it.second.is_int());
 
         cryptauth::SoftwareFeature software_feature =
@@ -961,9 +955,9 @@
 
   device_manager_->Start();
   EXPECT_EQ(1u, device_manager_->GetSyncedDevices().size());
-  EXPECT_EQ(1u, pref_service_.GetList(prefs::kCryptAuthDeviceSyncUnlockKeys)
-                    ->GetListDeprecated()
-                    .size());
+  EXPECT_EQ(
+      1u,
+      pref_service_.GetValueList(prefs::kCryptAuthDeviceSyncUnlockKeys).size());
 
   FireSchedulerForSync(cryptauth::INVOCATION_REASON_PERIODIC);
   ASSERT_FALSE(success_callback_.is_null());
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index daafb44..f383d2d70 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -149,6 +149,9 @@
   // window when that window is closed.
   virtual void ForceSkipWarningUserOnClose(
       const std::vector<aura::Window*>& windows) = 0;
+
+  // Retrieves the official Chrome version string e.g. 105.0.5178.0.
+  virtual std::string GetVersionString() = 0;
 };
 
 }  // namespace ash
diff --git a/ash/style/system_toast_style.cc b/ash/style/system_toast_style.cc
index 15530d1..46fba86 100644
--- a/ash/style/system_toast_style.cc
+++ b/ash/style/system_toast_style.cc
@@ -13,6 +13,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/pill_button.h"
+#include "ash/style/system_shadow.h"
 #include "ash/system/toast/toast_overlay.h"
 #include "ash/wm/work_area_insets.h"
 #include "base/strings/strcat.h"
@@ -173,6 +174,13 @@
         toast_corner_radius, views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
+
+  // Since system toast has a very large corner radius, we should use the shadow
+  // on texture layer. Refer to `ash::SystemShadowOnTextureLayer` for more
+  // details.
+  shadow_ = SystemShadow::CreateShadowOnTextureLayer(
+      SystemShadow::Type::kElevation12);
+  shadow_->SetRoundedCornerRadius(toast_corner_radius);
 }
 
 SystemToastStyle::~SystemToastStyle() = default;
@@ -205,6 +213,18 @@
   label_->SetText(text);
 }
 
+void SystemToastStyle::AddedToWidget() {
+  // Attach the shadow at the bottom of the widget layer.
+  auto* shadow_layer = shadow_->GetLayer();
+  auto* widget_layer = GetWidget()->GetLayer();
+
+  widget_layer->Add(shadow_layer);
+  widget_layer->StackAtBottom(shadow_layer);
+
+  // Update shadow content bounds with the bounds of widget layer.
+  shadow_->SetContentBounds(gfx::Rect(widget_layer->bounds().size()));
+}
+
 void SystemToastStyle::OnThemeChanged() {
   views::View::OnThemeChanged();
 
diff --git a/ash/style/system_toast_style.h b/ash/style/system_toast_style.h
index f8e1c9b..b226e0b 100644
--- a/ash/style/system_toast_style.h
+++ b/ash/style/system_toast_style.h
@@ -19,6 +19,7 @@
 namespace ash {
 
 class ScopedA11yOverrideWindowSetter;
+class SystemShadow;
 
 // A view that has rounded corner with label and button inside. The label shows
 // the information. The button inside is optional and has certain functionality
@@ -53,6 +54,7 @@
 
  private:
   // views::View:
+  void AddedToWidget() override;
   void OnThemeChanged() override;
 
   views::Label* label_ = nullptr;
@@ -62,6 +64,8 @@
   // Tells the toast if the dismiss button is already highlighted if one exists.
   bool is_dismiss_button_highlighted_ = false;
 
+  std::unique_ptr<SystemShadow> shadow_;
+
   // Updates the current a11y override window when the dismiss button is being
   // highlighted.
   std::unique_ptr<ScopedA11yOverrideWindowSetter> scoped_a11y_overrider_;
diff --git a/ash/system/channel_indicator/channel_indicator.cc b/ash/system/channel_indicator/channel_indicator.cc
index e2a0fd02..a9fe873 100644
--- a/ash/system/channel_indicator/channel_indicator.cc
+++ b/ash/system/channel_indicator/channel_indicator.cc
@@ -4,26 +4,16 @@
 
 #include "ash/system/channel_indicator/channel_indicator.h"
 
-#include <string>
-#include <utility>
-
-#include "ash/public/cpp/shelf_config.h"
-#include "ash/public/cpp/style/dark_light_mode_controller.h"
-#include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/system/model/system_tray_model.h"
+#include "ash/shelf/shelf.h"
+#include "ash/system/channel_indicator/channel_indicator_utils.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/system/tray/tray_item_view.h"
-#include "components/session_manager/session_manager_types.h"
-#include "components/version_info/channel.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/views/view.h"
 
 namespace ash {
 
@@ -31,64 +21,6 @@
 
 constexpr int kIndicatorBgCornerRadius = 50;
 
-bool IsDisplayableChannel(version_info::Channel channel) {
-  switch (channel) {
-    case version_info::Channel::BETA:
-    case version_info::Channel::DEV:
-    case version_info::Channel::CANARY:
-      return true;
-    default:
-      return false;
-  }
-}
-
-SkColor GetFgColor(version_info::Channel channel) {
-  bool is_dark_mode_enabled =
-      DarkLightModeController::Get()->IsDarkModeEnabled();
-  switch (channel) {
-    case version_info::Channel::BETA:
-      return is_dark_mode_enabled ? gfx::kGoogleBlue200 : gfx::kGoogleBlue900;
-    case version_info::Channel::DEV:
-      return is_dark_mode_enabled ? gfx::kGoogleGreen200 : gfx::kGoogleGreen900;
-    case version_info::Channel::CANARY:
-      return is_dark_mode_enabled ? gfx::kGoogleYellow200 : gfx::kGoogleGrey900;
-    default:
-      return 0;
-  }
-}
-
-SkColor GetBgColor(version_info::Channel channel) {
-  bool is_dark_mode_enabled =
-      DarkLightModeController::Get()->IsDarkModeEnabled();
-  switch (channel) {
-    case version_info::Channel::BETA:
-      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleBlue300, 0x55)
-                                  : gfx::kGoogleBlue200;
-    case version_info::Channel::DEV:
-      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleGreen300, 0x55)
-                                  : gfx::kGoogleGreen200;
-    case version_info::Channel::CANARY:
-      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleYellow300, 0x55)
-                                  : gfx::kGoogleYellow200;
-    default:
-      return 0;
-  }
-}
-
-int GetStringResource(version_info::Channel channel) {
-  DCHECK(IsDisplayableChannel(channel));
-  switch (channel) {
-    case version_info::Channel::BETA:
-      return IDS_ASH_STATUS_TRAY_CHANNEL_BETA;
-    case version_info::Channel::DEV:
-      return IDS_ASH_STATUS_TRAY_CHANNEL_DEV;
-    case version_info::Channel::CANARY:
-      return IDS_ASH_STATUS_TRAY_CHANNEL_CANARY;
-    default:
-      return -1;
-  }
-}
-
 }  // namespace
 
 ChannelIndicatorView::ChannelIndicatorView(Shelf* shelf,
@@ -133,7 +65,7 @@
 }
 
 void ChannelIndicatorView::Update(version_info::Channel channel) {
-  if (!IsDisplayableChannel(channel))
+  if (!channel_indicator_utils::IsDisplayableChannel(channel))
     return;
 
   SetVisible(true);
@@ -143,28 +75,28 @@
 }
 
 void ChannelIndicatorView::SetImage(version_info::Channel channel) {
-  DCHECK(IsDisplayableChannel(channel));
+  DCHECK(channel_indicator_utils::IsDisplayableChannel(channel));
 
   SetBorder(views::CreateEmptyBorder(
       gfx::Insets::VH(kUnifiedTrayChannelIndicatorDimension / 2, 0)));
   image_view()->SetBackground(views::CreateRoundedRectBackground(
-      GetBgColor(channel), kIndicatorBgCornerRadius));
+      channel_indicator_utils::GetBgColor(channel), kIndicatorBgCornerRadius));
 
   switch (channel) {
     case version_info::Channel::BETA:
       image_view()->SetImage(gfx::CreateVectorIcon(
           kChannelBetaIcon, kUnifiedTrayChannelIndicatorDimension,
-          GetFgColor(channel)));
+          channel_indicator_utils::GetFgColor(channel)));
       break;
     case version_info::Channel::DEV:
       image_view()->SetImage(gfx::CreateVectorIcon(
           kChannelDevIcon, kUnifiedTrayChannelIndicatorDimension,
-          GetFgColor(channel)));
+          channel_indicator_utils::GetFgColor(channel)));
       break;
     case version_info::Channel::CANARY:
       image_view()->SetImage(gfx::CreateVectorIcon(
           kChannelCanaryIcon, kUnifiedTrayChannelIndicatorDimension,
-          GetFgColor(channel)));
+          channel_indicator_utils::GetFgColor(channel)));
       break;
     default:
       break;
@@ -172,14 +104,16 @@
 }
 
 void ChannelIndicatorView::SetAccessibleName(version_info::Channel channel) {
-  DCHECK(IsDisplayableChannel(channel));
-  accessible_name_ = l10n_util::GetStringUTF16(GetStringResource(channel));
+  DCHECK(channel_indicator_utils::IsDisplayableChannel(channel));
+  accessible_name_ = l10n_util::GetStringUTF16(
+      channel_indicator_utils::GetChannelNameStringResourceID(channel, true));
   image_view()->SetAccessibleName(accessible_name_);
 }
 
 void ChannelIndicatorView::SetTooltip(version_info::Channel channel) {
-  DCHECK(IsDisplayableChannel(channel));
-  tooltip_ = l10n_util::GetStringUTF16(GetStringResource(channel));
+  DCHECK(channel_indicator_utils::IsDisplayableChannel(channel));
+  tooltip_ = l10n_util::GetStringUTF16(
+      channel_indicator_utils::GetChannelNameStringResourceID(channel, true));
 }
 
 }  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator.h b/ash/system/channel_indicator/channel_indicator.h
index ce410e4f..d05075f 100644
--- a/ash/system/channel_indicator/channel_indicator.h
+++ b/ash/system/channel_indicator/channel_indicator.h
@@ -39,9 +39,14 @@
   void SetAccessibleName(version_info::Channel channel);
   void SetTooltip(version_info::Channel channel);
 
+  // The localized string used to announce this view in accessibility mode.
   std::u16string accessible_name_;
+
+  // The localized string displayed when this view is hovered-over.
   std::u16string tooltip_;
-  version_info::Channel channel_;
+
+  // The release track on which this devices resides.
+  const version_info::Channel channel_;
 };
 
 }  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
new file mode 100644
index 0000000..9c5d623
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
@@ -0,0 +1,180 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator_quick_settings_view.h"
+
+#include "ash/public/cpp/system_tray_client.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/channel_indicator/channel_indicator_utils.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/skia_conversions.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/highlight_path_generator.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+namespace {
+
+constexpr int kVersionButtonHeight = 32;
+constexpr int kVersionButtonBorderRadius = 4;
+constexpr int kVersionButtonImageLabelSpacing = 8;
+
+constexpr int kVersionButtonMarginVertical = 6;
+constexpr int kVersionButtonMarginHorizontal = 16;
+
+constexpr int kVersionButtonLargeCornerRadius = 16;
+constexpr int kVersionButtonSmallCornerRadius = 4;
+constexpr SkScalar kVersionButtonCorners[] = {
+    kVersionButtonLargeCornerRadius, kVersionButtonLargeCornerRadius,
+    kVersionButtonSmallCornerRadius, kVersionButtonSmallCornerRadius,
+    kVersionButtonSmallCornerRadius, kVersionButtonSmallCornerRadius,
+    kVersionButtonLargeCornerRadius, kVersionButtonLargeCornerRadius};
+
+constexpr int kSubmitFeedbackButtonMarginVertical = 6;
+constexpr int kSubmitFeedbackButtonMarginHorizontal = 16;
+
+constexpr int kSubmitFeedbackButtonLargeCornerRadius = 16;
+constexpr int kSubmitFeedbackButtonSmallCornerRadius = 4;
+constexpr SkScalar kSubmitFeedbackButtonCorners[] = {
+    kSubmitFeedbackButtonSmallCornerRadius,
+    kSubmitFeedbackButtonSmallCornerRadius,
+    kSubmitFeedbackButtonLargeCornerRadius,
+    kSubmitFeedbackButtonLargeCornerRadius,
+    kSubmitFeedbackButtonLargeCornerRadius,
+    kSubmitFeedbackButtonLargeCornerRadius,
+    kSubmitFeedbackButtonSmallCornerRadius,
+    kSubmitFeedbackButtonSmallCornerRadius};
+
+constexpr int kButtonSpacing = 2;
+
+}  // namespace
+
+// VersionButton provides a styled button, for devices on a
+// non-stable release track, that has a label for the channel and ChromeOS
+// version.
+class ASH_EXPORT VersionButton : public views::LabelButton {
+ public:
+  explicit VersionButton(version_info::Channel channel)
+      : LabelButton(
+            base::BindRepeating([] {
+              Shell::Get()
+                  ->system_tray_model()
+                  ->client()
+                  ->ShowChannelInfoAdditionalDetails();
+            }),
+            channel_indicator_utils::GetFullReleaseTrackString(channel)),
+        channel_(channel) {
+    SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(
+        kVersionButtonMarginVertical, kVersionButtonMarginHorizontal)));
+    SetImageLabelSpacing(kVersionButtonImageLabelSpacing);
+    SetMinSize(gfx::Size(0, kVersionButtonHeight));
+    SetFocusBehavior(FocusBehavior::ALWAYS);
+    SetInstallFocusRingOnFocus(true);
+    views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
+    views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
+                                                  kVersionButtonBorderRadius);
+  }
+  VersionButton(const VersionButton&) = delete;
+  VersionButton& operator=(const VersionButton&) = delete;
+  ~VersionButton() override = default;
+
+  // views::LabelButton:
+  void PaintButtonContents(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setColor(channel_indicator_utils::GetBgColor(channel_));
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    canvas->DrawPath(
+        SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()),
+                              kVersionButtonCorners, SkPathDirection::kCW),
+        flags);
+  }
+
+  void OnThemeChanged() override {
+    views::LabelButton::OnThemeChanged();
+    SetBackgroundAndFont();
+  }
+
+ private:
+  void SetBackgroundAndFont() {
+    label()->SetFontList(
+        gfx::FontList().DeriveWithWeight(gfx::Font::Weight::MEDIUM));
+    SetEnabledTextColors(channel_indicator_utils::GetFgColor(channel_));
+  }
+
+  const version_info::Channel channel_;
+};
+
+// SubmitFeedbackButton provides a styled button, for devices on a
+// non-stable release track, that allows the user to submit feedback.
+class ASH_EXPORT SubmitFeedbackButton : public IconButton {
+ public:
+  explicit SubmitFeedbackButton(version_info::Channel channel)
+      : IconButton(base::BindRepeating([] {
+                     Shell::Get()
+                         ->system_tray_model()
+                         ->client()
+                         ->ShowChannelInfoGiveFeedback();
+                   }),
+                   IconButton::Type::kSmall,
+                   &kRequestFeedbackIcon,
+                   IDS_ASH_STATUS_TRAY_REPORT_FEEDBACK),
+        channel_(channel) {
+    SetBorder(views::CreateEmptyBorder(
+        gfx::Insets::VH(kSubmitFeedbackButtonMarginVertical,
+                        kSubmitFeedbackButtonMarginHorizontal)));
+    SetIconColor(channel_indicator_utils::GetFgColor(channel_));
+  }
+  SubmitFeedbackButton(const SubmitFeedbackButton&) = delete;
+  SubmitFeedbackButton& operator=(const SubmitFeedbackButton&) = delete;
+  ~SubmitFeedbackButton() override = default;
+
+  // views::LabelButton:
+  void PaintButtonContents(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setColor(channel_indicator_utils::GetBgColor(channel_));
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    canvas->DrawPath(SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()),
+                                           kSubmitFeedbackButtonCorners,
+                                           SkPathDirection::kCW),
+                     flags);
+    IconButton::PaintButtonContents(canvas);
+  }
+
+ private:
+  const version_info::Channel channel_;
+};
+
+ChannelIndicatorQuickSettingsView::ChannelIndicatorQuickSettingsView(
+    version_info::Channel channel) {
+  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal, kUnifiedSystemInfoViewPadding,
+      kUnifiedSystemInfoSpacing));
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kCenter);
+  layout->set_between_child_spacing(kButtonSpacing);
+
+  version_button_ = AddChildView(std::make_unique<VersionButton>(channel));
+  feedback_button_ =
+      AddChildView(std::make_unique<SubmitFeedbackButton>(channel));
+}
+
+bool ChannelIndicatorQuickSettingsView::IsVersionButtonVisibleForTesting() {
+  return version_button_->GetVisible();
+}
+
+bool ChannelIndicatorQuickSettingsView::
+    IsSubmitFeedbackButtonVisibleForTesting() {
+  return feedback_button_->GetVisible();
+}
+
+}  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.h b/ash/system/channel_indicator/channel_indicator_quick_settings_view.h
new file mode 100644
index 0000000..32f7a9a
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_QUICK_SETTINGS_VIEW_H_
+#define ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_QUICK_SETTINGS_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "components/version_info/channel.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+class VersionButton;
+class SubmitFeedbackButton;
+
+// ChannelIndicatorQuickSettingsView contains all of the views included in the
+// channel indicator UI that resides in UnifiedSystemInfoView.
+class ASH_EXPORT ChannelIndicatorQuickSettingsView : public views::View {
+ public:
+  explicit ChannelIndicatorQuickSettingsView(version_info::Channel channel);
+  ChannelIndicatorQuickSettingsView(const ChannelIndicatorQuickSettingsView&) =
+      delete;
+  ChannelIndicatorQuickSettingsView& operator=(
+      const ChannelIndicatorQuickSettingsView&) = delete;
+  ~ChannelIndicatorQuickSettingsView() override = default;
+
+  // Introspection methods for unit tests.
+  bool IsVersionButtonVisibleForTesting();
+  bool IsSubmitFeedbackButtonVisibleForTesting();
+
+ private:
+  // Refs maintained for unit test introspection methods.
+  VersionButton* version_button_ = nullptr;
+  SubmitFeedbackButton* feedback_button_ = nullptr;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_QUICK_SETTINGS_VIEW_H_
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view_unittest.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view_unittest.cc
new file mode 100644
index 0000000..4cbfc906
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator_quick_settings_view.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/test/ash_test_base.h"
+#include "components/version_info/channel.h"
+
+namespace ash {
+
+class ChannelIndicatorQuickSettingsViewTest
+    : public AshTestBase,
+      public testing::WithParamInterface<bool> {
+ public:
+  ChannelIndicatorQuickSettingsViewTest() = default;
+  ChannelIndicatorQuickSettingsViewTest(
+      const ChannelIndicatorQuickSettingsViewTest&) = delete;
+  ChannelIndicatorQuickSettingsViewTest& operator=(
+      const ChannelIndicatorQuickSettingsViewTest&) = delete;
+  ~ChannelIndicatorQuickSettingsViewTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    // Instantiate members.
+    view_ = std::make_unique<ChannelIndicatorQuickSettingsView>(
+        static_cast<version_info::Channel>(GetParam()));
+  }
+
+  // Ignored for now, will come into play with the fix for crbug.com/1344855.
+  bool IsFeedbackShown() { return GetParam(); }
+
+  ChannelIndicatorQuickSettingsView* view() { return view_.get(); }
+
+ private:
+  std::unique_ptr<ChannelIndicatorQuickSettingsView> view_;
+};
+
+// Run the `Visible` test below for each value of version_info::Channel.
+INSTANTIATE_TEST_SUITE_P(ChannelValues,
+                         ChannelIndicatorQuickSettingsViewTest,
+                         ::testing::Bool());
+
+TEST_P(ChannelIndicatorQuickSettingsViewTest, Visible) {
+  // View exists.
+  EXPECT_TRUE(view());
+
+  // Version button is always visible.
+  EXPECT_TRUE(view()->IsVersionButtonVisibleForTesting());
+
+  // Feedback button is always visible, for now. This will change with the fix
+  // for crbug.com/1344855.
+  EXPECT_TRUE(view()->IsSubmitFeedbackButtonVisibleForTesting());
+}
+
+}  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator_unittest.cc b/ash/system/channel_indicator/channel_indicator_unittest.cc
index 12c5bc4..17a13c2 100644
--- a/ash/system/channel_indicator/channel_indicator_unittest.cc
+++ b/ash/system/channel_indicator/channel_indicator_unittest.cc
@@ -21,6 +21,12 @@
     : public AshTestBase,
       public testing::WithParamInterface<version_info::Channel> {
  public:
+  ChannelIndicatorViewTest() = default;
+  ChannelIndicatorViewTest(const ChannelIndicatorViewTest&) = delete;
+  ChannelIndicatorViewTest& operator=(const ChannelIndicatorViewTest&) = delete;
+  ~ChannelIndicatorViewTest() override = default;
+
+  // AshTestBase:
   void SetUp() override {
     // Need this feature enabled in order for the `ChannelIndicatorView` to be
     // instantiated.
diff --git a/ash/system/channel_indicator/channel_indicator_utils.cc b/ash/system/channel_indicator/channel_indicator_utils.cc
new file mode 100644
index 0000000..a42e2e1
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_utils.cc
@@ -0,0 +1,100 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator_utils.h"
+
+#include <string>
+
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+
+namespace ash::channel_indicator_utils {
+
+bool IsDisplayableChannel(version_info::Channel channel) {
+  switch (channel) {
+    case version_info::Channel::BETA:
+    case version_info::Channel::DEV:
+    case version_info::Channel::CANARY:
+      return true;
+    case version_info::Channel::STABLE:
+    case version_info::Channel::UNKNOWN:
+      return false;
+  }
+}
+
+int GetChannelNameStringResourceID(version_info::Channel channel,
+                                   bool append_channel) {
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return append_channel ? IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL
+                            : IDS_ASH_STATUS_TRAY_CHANNEL_BETA;
+    case version_info::Channel::DEV:
+      return append_channel ? IDS_ASH_STATUS_TRAY_CHANNEL_DEV_CHANNEL
+                            : IDS_ASH_STATUS_TRAY_CHANNEL_DEV;
+    case version_info::Channel::CANARY:
+      return append_channel ? IDS_ASH_STATUS_TRAY_CHANNEL_CANARY_CHANNEL
+                            : IDS_ASH_STATUS_TRAY_CHANNEL_CANARY;
+    // Handle STABLE/UNKNOWN here to satisfy the compiler without using
+    // "default," but the DCHECK() above will bark if that value is ever
+    // actually passed in.
+    case version_info::Channel::STABLE:
+    case version_info::Channel::UNKNOWN:
+      return -1;
+  }
+}
+
+SkColor GetFgColor(version_info::Channel channel) {
+  bool is_dark_mode_enabled =
+      DarkLightModeController::Get()->IsDarkModeEnabled();
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return is_dark_mode_enabled ? gfx::kGoogleBlue200 : gfx::kGoogleBlue900;
+    case version_info::Channel::DEV:
+      return is_dark_mode_enabled ? gfx::kGoogleGreen200 : gfx::kGoogleGreen900;
+    case version_info::Channel::CANARY:
+      return is_dark_mode_enabled ? gfx::kGoogleYellow200 : gfx::kGoogleGrey900;
+    case version_info::Channel::STABLE:
+    case version_info::Channel::UNKNOWN:
+      return SkColorSetRGB(0x00, 0x00, 0x00);
+  }
+}
+
+SkColor GetBgColor(version_info::Channel channel) {
+  bool is_dark_mode_enabled =
+      DarkLightModeController::Get()->IsDarkModeEnabled();
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleBlue300, 0x55)
+                                  : gfx::kGoogleBlue200;
+    case version_info::Channel::DEV:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleGreen300, 0x55)
+                                  : gfx::kGoogleGreen200;
+    case version_info::Channel::CANARY:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleYellow300, 0x55)
+                                  : gfx::kGoogleYellow200;
+    case version_info::Channel::STABLE:
+    case version_info::Channel::UNKNOWN:
+      return SkColorSetRGB(0x00, 0x00, 0x00);
+  }
+}
+
+std::u16string GetFullReleaseTrackString(version_info::Channel channel) {
+  if (!IsDisplayableChannel(channel))
+    return std::u16string();
+
+  return base::StrCat(
+      {l10n_util::GetStringUTF16(
+           channel_indicator_utils::GetChannelNameStringResourceID(channel,
+                                                                   false)),
+       u" ",
+       base::UTF8ToUTF16(Shell::Get()->shell_delegate()->GetVersionString())});
+}
+
+}  // namespace ash::channel_indicator_utils
diff --git a/ash/system/channel_indicator/channel_indicator_utils.h b/ash/system/channel_indicator/channel_indicator_utils.h
new file mode 100644
index 0000000..28eb15e
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_utils.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_UTILS_H_
+#define ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_UTILS_H_
+
+#include <string>
+
+#include "ash/ash_export.h"
+#include "components/version_info/channel.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace ash::channel_indicator_utils {
+
+// Returns 'true' if `channel` is a release track name we want to show the user.
+ASH_EXPORT bool IsDisplayableChannel(version_info::Channel channel);
+
+// Returns a string resource ID for the release track `channel`. If
+// `append_channel` is 'true' then the resource ID returned is for a string that
+// has "channel" at the end e.g. "Beta Channel" instead of just "Beta". If
+// `channel` is `STABLE` or `UNKNOWN` this function will return -1.
+ASH_EXPORT int GetChannelNameStringResourceID(version_info::Channel channel,
+                                              bool append_channel);
+
+// Returns the foreground UI color for release track `channel`. If `channel` is
+// one of the displayable values then the expected `SkColor` is returned, a
+// value of SkColorSetRGB(0x00, 0x00, 0x00) otherwise.
+ASH_EXPORT SkColor GetFgColor(version_info::Channel channel);
+
+// Returns the background UI color for release track `channel`. If `channel` is
+// one of the displayable values then the expected `SkColor` is returned, a
+// value of SkColorSetRGB(0x00, 0x00, 0x00) otherwise.
+ASH_EXPORT SkColor GetBgColor(version_info::Channel channel);
+
+// Returns the text for the version button text, for release track `channel`
+// e.g. "Beta 105.0.5167.0". If `channel` is not one of the displayable values,
+// the function will return an empty std::u16string.
+ASH_EXPORT std::u16string GetFullReleaseTrackString(
+    version_info::Channel channel);
+
+}  // namespace ash::channel_indicator_utils
+
+#endif  // ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_UTILS_H_
\ No newline at end of file
diff --git a/ash/system/channel_indicator/channel_indicator_utils_unittest.cc b/ash/system/channel_indicator/channel_indicator_utils_unittest.cc
new file mode 100644
index 0000000..d849d6c4
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_utils_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator_utils.h"
+
+#include <string>
+
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test_shell_delegate.h"
+#include "components/version_info/channel.h"
+#include "ui/gfx/color_palette.h"
+
+namespace ash {
+namespace {
+
+// OS version that's set, and final button string that's expected.
+const char* kTestOsVersion = "123.45.6789.10";
+const char16_t* kTestButtonStr = u"Beta 123.45.6789.10";
+
+}  // namespace
+
+class ChannelIndicatorUtilsTest : public AshTestBase {
+ public:
+  ChannelIndicatorUtilsTest() = default;
+  ChannelIndicatorUtilsTest(const ChannelIndicatorUtilsTest&) = delete;
+  ChannelIndicatorUtilsTest& operator=(const ChannelIndicatorUtilsTest&) =
+      delete;
+  ~ChannelIndicatorUtilsTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    // Instantiate a `TestShellDelegate` with the version set to something
+    // tests can verify.
+    std::unique_ptr<TestShellDelegate> shell_delegate =
+        std::make_unique<TestShellDelegate>();
+    shell_delegate->set_version_string(kTestOsVersion);
+    AshTestBase::SetUp(std::move(shell_delegate));
+  }
+};
+
+TEST_F(ChannelIndicatorUtilsTest, IsDisplayableChannel) {
+  EXPECT_FALSE(channel_indicator_utils::IsDisplayableChannel(
+      version_info::Channel::UNKNOWN));
+  EXPECT_TRUE(channel_indicator_utils::IsDisplayableChannel(
+      version_info::Channel::CANARY));
+  EXPECT_TRUE(channel_indicator_utils::IsDisplayableChannel(
+      version_info::Channel::DEV));
+  EXPECT_TRUE(channel_indicator_utils::IsDisplayableChannel(
+      version_info::Channel::BETA));
+  EXPECT_FALSE(channel_indicator_utils::IsDisplayableChannel(
+      version_info::Channel::STABLE));
+}
+
+TEST_F(ChannelIndicatorUtilsTest, GetChannelNameStringResourceID) {
+  // Non-displayable channel should yield a resource_id of -1.
+  EXPECT_EQ(channel_indicator_utils::GetChannelNameStringResourceID(
+                version_info::Channel::STABLE, false),
+            -1);
+
+  // Same thing if `append_channel` is `true`.
+  EXPECT_EQ(channel_indicator_utils::GetChannelNameStringResourceID(
+                version_info::Channel::STABLE, true),
+            -1);
+
+  // Displayable channel should yield a valid resource_id.
+  EXPECT_EQ(channel_indicator_utils::GetChannelNameStringResourceID(
+                version_info::Channel::BETA, false),
+            IDS_ASH_STATUS_TRAY_CHANNEL_BETA);
+
+  // An equally-valid resource_id if `append_channel` is `true`.
+  EXPECT_EQ(channel_indicator_utils::GetChannelNameStringResourceID(
+                version_info::Channel::BETA, true),
+            IDS_ASH_STATUS_TRAY_CHANNEL_BETA_CHANNEL);
+}
+
+TEST_F(ChannelIndicatorUtilsTest, GetColors) {
+  // Non-displayable channel should yield fg/bg colors of 0.
+  EXPECT_EQ(channel_indicator_utils::GetFgColor(version_info::Channel::STABLE),
+            SkColorSetRGB(0x00, 0x00, 0x00));
+  EXPECT_EQ(channel_indicator_utils::GetBgColor(version_info::Channel::STABLE),
+            SkColorSetRGB(0x00, 0x00, 0x00));
+
+  // Displayable channel should yield valid, nonzero fg/bg colors. Check with
+  // dark mode not enabled first.
+  DarkLightModeController::Get()->SetDarkModeEnabledForTest(false);
+  EXPECT_EQ(channel_indicator_utils::GetFgColor(version_info::Channel::BETA),
+            gfx::kGoogleBlue900);
+  EXPECT_EQ(channel_indicator_utils::GetBgColor(version_info::Channel::BETA),
+            gfx::kGoogleBlue200);
+
+  // Check with dark mode enabled.
+  DarkLightModeController::Get()->SetDarkModeEnabledForTest(true);
+  EXPECT_EQ(channel_indicator_utils::GetFgColor(version_info::Channel::BETA),
+            gfx::kGoogleBlue200);
+  EXPECT_EQ(channel_indicator_utils::GetBgColor(version_info::Channel::BETA),
+            SkColorSetA(gfx::kGoogleBlue300, 0x55));
+}
+
+TEST_F(ChannelIndicatorUtilsTest, GetFullReleaseTrackString) {
+  // Channel is not displayable, no string.
+  EXPECT_TRUE(channel_indicator_utils::GetFullReleaseTrackString(
+                  version_info::Channel::STABLE)
+                  .empty());
+
+  // Channel is displayable, string that's expected.
+  EXPECT_EQ(channel_indicator_utils::GetFullReleaseTrackString(
+                version_info::Channel::BETA),
+            kTestButtonStr);
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index 6bce420..301a052 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -10,8 +10,11 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/shell_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
+#include "ash/system/channel_indicator/channel_indicator_quick_settings_view.h"
+#include "ash/system/channel_indicator/channel_indicator_utils.h"
 #include "ash/system/enterprise/enterprise_domain_observer.h"
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/clock_observer.h"
@@ -24,6 +27,7 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/i18n/time_formatting.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -260,7 +264,7 @@
     auto seperator = std::make_unique<views::Label>();
     seperator->SetText(l10n_util::GetStringUTF16(
         IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR));
-    separator_ = AddChildView(std::move(seperator));
+    separator_view_ = AddChildView(std::move(seperator));
     status_ = AddChildView(std::make_unique<views::Label>());
     Update();
   }
@@ -275,7 +279,7 @@
     const auto color =
         GetContentLayerColor(ContentLayerType::kTextColorSecondary);
     ConfigureLabel(percentage_, color);
-    ConfigureLabel(separator_, color);
+    ConfigureLabel(separator_view_, color);
     ConfigureLabel(status_, color);
   }
 
@@ -291,8 +295,9 @@
 
     percentage_->SetVisible(!percentage_text.empty() &&
                             !use_smart_charging_ui_);
-    separator_->SetVisible(!percentage_text.empty() &&
-                           !use_smart_charging_ui_ && !status_text.empty());
+    separator_view_->SetVisible(!percentage_text.empty() &&
+                                !use_smart_charging_ui_ &&
+                                !status_text.empty());
     status_->SetVisible(!status_text.empty());
 
     percentage_->NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
@@ -300,7 +305,7 @@
   }
 
   views::Label* percentage_ = nullptr;
-  views::Label* separator_ = nullptr;
+  views::Label* separator_view_ = nullptr;
   views::Label* status_ = nullptr;
 
   const bool use_smart_charging_ui_;
@@ -585,34 +590,88 @@
 
 }  // namespace
 
+// A view that contains date, battery status, and whether the device
+// is enterprise managed.
+class ManagementPowerDateComboView : public views::View {
+ public:
+  explicit ManagementPowerDateComboView(
+      UnifiedSystemTrayController* controller) {
+    auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kHorizontal,
+        kUnifiedSystemInfoViewPadding, kUnifiedSystemInfoSpacing));
+    layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kCenter);
+    AddChildView(std::make_unique<DateView>(controller));
+
+    if (PowerStatus::Get()->IsBatteryPresent()) {
+      separator_view_ = AddChildView(std::make_unique<views::Separator>());
+      separator_view_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
+      separator_view_->SetPreferredLength(kUnifiedSystemInfoHeight);
+
+      const bool use_smart_charging_ui = UseSmartChargingUI();
+      if (use_smart_charging_ui)
+        AddChildView(std::make_unique<BatteryIconView>(controller));
+      AddChildView(std::make_unique<BatteryLabelView>(controller,
+                                                      use_smart_charging_ui));
+    }
+
+    auto* spacing = AddChildView(std::make_unique<views::View>());
+    layout->SetFlexForView(spacing, 1);
+
+    enterprise_managed_view_ =
+        AddChildView(std::make_unique<EnterpriseManagedView>(controller));
+    supervised_view_ = AddChildView(std::make_unique<SupervisedUserView>());
+  }
+  ManagementPowerDateComboView(const ManagementPowerDateComboView&) = delete;
+  ManagementPowerDateComboView& operator=(const ManagementPowerDateComboView&) =
+      delete;
+  ~ManagementPowerDateComboView() override = default;
+
+  // Introspection methods for unit tests, that call into individual views.
+  bool IsEnterpriseManagedVisibleForTesting() {
+    return enterprise_managed_view_->GetVisible();
+  }
+
+  bool IsSupervisedVisibleForTesting() {
+    return supervised_view_->GetVisible();
+  }
+
+ private:
+  // Pointer to the actual child view is maintained for unit testing, owned by
+  // `ManagementPowerDateComboView`.
+  EnterpriseManagedView* enterprise_managed_view_ = nullptr;
+
+  // Pointer to the actual child view is maintained for unit testing, owned by
+  // `ManagementPowerDateComboView`.
+  SupervisedUserView* supervised_view_ = nullptr;
+
+  // Separator between date and battery views, owned by
+  // `ManagementPowerDateComboView`.
+  views::Separator* separator_view_ = nullptr;
+};
+
 UnifiedSystemInfoView::UnifiedSystemInfoView(
     UnifiedSystemTrayController* controller) {
+  // Layout for the overall UnifiedSystemInfoView.
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kHorizontal, kUnifiedSystemInfoViewPadding,
+      views::BoxLayout::Orientation::kVertical, kUnifiedSystemInfoViewPadding,
       kUnifiedSystemInfoSpacing));
   layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  AddChildView(std::make_unique<DateView>(controller));
+  // Construct a ManagementPowerDateComboView and save off a raw pointer, to
+  // facilitate introspection needed for unit tests.
+  combo_view_ =
+      AddChildView(std::make_unique<ManagementPowerDateComboView>(controller));
 
-  if (PowerStatus::Get()->IsBatteryPresent()) {
-    separator_ = AddChildView(std::make_unique<views::Separator>());
-    separator_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
-    separator_->SetPreferredLength(kUnifiedSystemInfoHeight);
-
-    const bool use_smart_charging_ui = UseSmartChargingUI();
-    if (use_smart_charging_ui)
-      AddChildView(std::make_unique<BatteryIconView>(controller));
-    AddChildView(
-        std::make_unique<BatteryLabelView>(controller, use_smart_charging_ui));
+  // If the release track is not "stable" then channel indicator UI for quick
+  // settings is put up.
+  auto channel = Shell::Get()->shell_delegate()->GetChannel();
+  if (features::IsReleaseTrackUiEnabled() &&
+      channel_indicator_utils::IsDisplayableChannel(channel)) {
+    channel_view_ = AddChildView(
+        std::make_unique<ChannelIndicatorQuickSettingsView>(channel));
   }
-
-  auto* spacing = AddChildView(std::make_unique<views::View>());
-  layout->SetFlexForView(spacing, 1);
-
-  enterprise_managed_ =
-      AddChildView(std::make_unique<EnterpriseManagedView>(controller));
-  supervised_ = AddChildView(std::make_unique<SupervisedUserView>());
 }
 
 UnifiedSystemInfoView::~UnifiedSystemInfoView() = default;
@@ -625,6 +684,18 @@
   Layout();
 }
 
+bool UnifiedSystemInfoView::IsEnterpriseManagedVisibleForTesting() {
+  return combo_view_->IsEnterpriseManagedVisibleForTesting();  // IN-TEST
+}
+
+bool UnifiedSystemInfoView::IsSupervisedVisibleForTesting() {
+  return combo_view_->IsSupervisedVisibleForTesting();  // IN-TEST
+}
+
+bool UnifiedSystemInfoView::IsChannelIndicatorQuickSettingsVisibleForTesting() {
+  return channel_view_ && channel_view_->GetVisible();  // IN-TEST
+}
+
 BEGIN_METADATA(UnifiedSystemInfoView, views::View)
 END_METADATA
 
diff --git a/ash/system/unified/unified_system_info_view.h b/ash/system/unified/unified_system_info_view.h
index db8951d5..bbbd9d62 100644
--- a/ash/system/unified/unified_system_info_view.h
+++ b/ash/system/unified/unified_system_info_view.h
@@ -11,15 +11,13 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
-namespace views {
-class Separator;
-}  // namespace views
-
 namespace ash {
 
+class ManagementPowerDateComboView;
+class ChannelIndicatorQuickSettingsView;
+
 // A view at the bottom of UnifiedSystemTray bubble that shows system
-// information. The view contains date, battery status, and whether the device
-// is enterprise managed or not.
+// information.
 class ASH_EXPORT UnifiedSystemInfoView : public views::View {
  public:
   METADATA_HEADER(UnifiedSystemInfoView);
@@ -34,6 +32,11 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   void ChildVisibilityChanged(views::View* child) override;
 
+  // Introspection methods needed for unit tests.
+  bool IsEnterpriseManagedVisibleForTesting();
+  bool IsSupervisedVisibleForTesting();
+  bool IsChannelIndicatorQuickSettingsVisibleForTesting();
+
  private:
   FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest, EnterpriseManagedVisible);
   FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest,
@@ -42,14 +45,14 @@
                            EnterpriseUserManagedVisible);
   FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewNoSessionTest, ChildVisible);
 
-  // EnterpriseManagedView for unit testing. Owned by this view. Null if
-  // kManagedDeviceUIRedesign is enabled.
-  views::View* enterprise_managed_ = nullptr;
-  // SupervisedUserView for unit testing. Owned by this view . Null if
-  // kManagedDeviceUIRedesign is enabled.
-  views::View* supervised_ = nullptr;
+  // Raw pointer to the combo view (owned by `UnifiedSystemInfoView`) that
+  // facilitates introspection needed for unit tests.
+  ManagementPowerDateComboView* combo_view_ = nullptr;
 
-  views::Separator* separator_ = nullptr;
+  // Raw pointer to the channel indicator quick settings view (owned by
+  // `UnifiedSystemInfoView`) that facilitates introspection needed for unit
+  // tests.
+  ChannelIndicatorQuickSettingsView* channel_view_ = nullptr;
 };
 
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_info_view_unittest.cc b/ash/system/unified/unified_system_info_view_unittest.cc
index 0c56982..bbab3af 100644
--- a/ash/system/unified/unified_system_info_view_unittest.cc
+++ b/ash/system/unified/unified_system_info_view_unittest.cc
@@ -14,13 +14,47 @@
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test_shell_delegate.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/scoped_feature_list.h"
+#include "components/version_info/channel.h"
 
 namespace ash {
 
-class UnifiedSystemInfoViewTest : public AshTestBase,
-                                  public testing::WithParamInterface<bool> {
+// `UnifiedSystemInfoView` contains a set of "baseline" UI elements that are
+// always visible, but some elements are visible only under certain conditions.
+// To verify that these "conditional" UI elements are visible or not-visible
+// only when expected, each `UnifiedSystemInfoViewTest` test case is executed
+// with every possible combination of the following flags, passed as a
+// parameter.
+enum class TestFlags : uint8_t {
+  // No conditional UI flags are set.
+  kNone = 0b00000000,
+
+  // Enterprise/management status display is enabled.
+  kManagedDeviceUi = 0b00000001,
+
+  // Release track UI is visible if two conditions are met: (1) the feature that
+  // guards its display is enabled (kReleaseTrackUi) and (2) the release track
+  // itself is a value other than "stable" (kReleaseTrackNotStable). Each
+  // combination of one, none, or both of these conditions is a valid scenario.
+  kReleaseTrackUi = 0b00000010,
+  kReleaseTrackNotStable = 0b00000100,
+};
+
+TestFlags operator&(TestFlags a, TestFlags b) {
+  return static_cast<TestFlags>(static_cast<uint8_t>(a) &
+                                static_cast<uint8_t>(b));
+}
+
+TestFlags operator|(TestFlags a, TestFlags b) {
+  return static_cast<TestFlags>(static_cast<uint8_t>(a) |
+                                static_cast<uint8_t>(b));
+}
+
+class UnifiedSystemInfoViewTest
+    : public AshTestBase,
+      public testing::WithParamInterface<TestFlags> {
  public:
   UnifiedSystemInfoViewTest() = default;
   UnifiedSystemInfoViewTest(const UnifiedSystemInfoViewTest&) = delete;
@@ -29,18 +63,45 @@
   ~UnifiedSystemInfoViewTest() override = default;
 
   void SetUp() override {
-    AshTestBase::SetUp();
+    // Provide our own `TestShellDelegate`, with a non-stable channel set if
+    // the passed-in parameter dictates.
+    std::unique_ptr<TestShellDelegate> shell_delegate =
+        std::make_unique<TestShellDelegate>();
+    if (IsReleaseTrackNotStable())
+      shell_delegate->set_channel(version_info::Channel::BETA);
+    AshTestBase::SetUp(std::move(shell_delegate));
 
+    // Enable/disable of the two features we care about is conditional on the
+    // passed-in parameter.
     scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
-    scoped_feature_list_->InitWithFeatureState(
-        features::kManagedDeviceUIRedesign, IsManagedDeviceUIRedesignEnabled());
+    std::vector<base::Feature> enabled_features, disabled_features;
+    if (IsManagedDeviceUIRedesignEnabled())
+      enabled_features.push_back(features::kManagedDeviceUIRedesign);
+    else
+      disabled_features.push_back(features::kManagedDeviceUIRedesign);
+    if (IsReleaseTrackUiEnabled())
+      enabled_features.push_back(features::kReleaseTrackUi);
+    else
+      disabled_features.push_back(features::kReleaseTrackUi);
+    scoped_feature_list_->InitWithFeatures(enabled_features, disabled_features);
 
+    // Instantiate members.
     model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
     controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
     info_view_ = std::make_unique<UnifiedSystemInfoView>(controller_.get());
   }
 
-  bool IsManagedDeviceUIRedesignEnabled() const { return GetParam(); }
+  bool IsManagedDeviceUIRedesignEnabled() const {
+    return (GetParam() & TestFlags::kManagedDeviceUi) != TestFlags::kNone;
+  }
+
+  bool IsReleaseTrackUiEnabled() const {
+    return (GetParam() & TestFlags::kReleaseTrackUi) != TestFlags::kNone;
+  }
+
+  bool IsReleaseTrackNotStable() const {
+    return (GetParam() & TestFlags::kReleaseTrackNotStable) != TestFlags::kNone;
+  }
 
   void TearDown() override {
     info_view_.reset();
@@ -63,14 +124,25 @@
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 };
 
+// Execute each test case with every possible combination of `TestFlags`.
 INSTANTIATE_TEST_SUITE_P(
     All,
     UnifiedSystemInfoViewTest,
-    testing::Bool() /* IsManagedDeviceUIRedesignEnabled() */);
+    testing::Values(TestFlags::kNone,
+                    TestFlags::kManagedDeviceUi,
+                    TestFlags::kReleaseTrackUi,
+                    TestFlags::kManagedDeviceUi | TestFlags::kReleaseTrackUi,
+                    TestFlags::kReleaseTrackNotStable,
+                    TestFlags::kManagedDeviceUi |
+                        TestFlags::kReleaseTrackNotStable,
+                    TestFlags::kReleaseTrackUi |
+                        TestFlags::kReleaseTrackNotStable,
+                    TestFlags::kManagedDeviceUi | TestFlags::kReleaseTrackUi |
+                        TestFlags::kReleaseTrackNotStable));
 
 TEST_P(UnifiedSystemInfoViewTest, EnterpriseManagedVisible) {
   // By default, EnterpriseManagedView is not shown.
-  EXPECT_FALSE(info_view()->enterprise_managed_->GetVisible());
+  EXPECT_FALSE(info_view()->IsEnterpriseManagedVisibleForTesting());
 
   // Simulate enterprise information becoming available.
   enterprise_domain()->SetDeviceEnterpriseInfo(
@@ -78,7 +150,12 @@
                            ManagementDeviceMode::kChromeEnterprise});
 
   // EnterpriseManagedView should be shown.
-  EXPECT_TRUE(info_view()->enterprise_managed_->GetVisible());
+  EXPECT_TRUE(info_view()->IsEnterpriseManagedVisibleForTesting());
+
+  // If the release track UI is enabled AND the release track is non-stable, the
+  // ChannelIndicatorQuickSettingsView is shown.
+  EXPECT_EQ(IsReleaseTrackUiEnabled() && IsReleaseTrackNotStable(),
+            info_view()->IsChannelIndicatorQuickSettingsVisibleForTesting());
 }
 
 TEST_P(UnifiedSystemInfoViewTest, EnterpriseManagedVisibleForActiveDirectory) {
@@ -89,19 +166,29 @@
                            ManagementDeviceMode::kChromeEnterprise});
 
   // EnterpriseManagedView should be shown.
-  EXPECT_TRUE(info_view()->enterprise_managed_->GetVisible());
+  EXPECT_TRUE(info_view()->IsEnterpriseManagedVisibleForTesting());
+
+  // If the release track UI is enabled AND the release track is non-stable, the
+  // ChannelIndicatorQuickSettingsView is shown.
+  EXPECT_EQ(IsReleaseTrackUiEnabled() && IsReleaseTrackNotStable(),
+            info_view()->IsChannelIndicatorQuickSettingsVisibleForTesting());
 }
 
 TEST_P(UnifiedSystemInfoViewTest, EnterpriseUserManagedVisible) {
   // By default, EnterpriseManagedView is not shown.
-  EXPECT_FALSE(info_view()->enterprise_managed_->GetVisible());
+  EXPECT_FALSE(info_view()->IsEnterpriseManagedVisibleForTesting());
 
   // Simulate enterprise information becoming available.
   enterprise_domain()->SetEnterpriseAccountDomainInfo("example.com");
 
   // EnterpriseManagedView should be shown if the feature is enabled.
   EXPECT_EQ(IsManagedDeviceUIRedesignEnabled(),
-            info_view()->enterprise_managed_->GetVisible());
+            info_view()->IsEnterpriseManagedVisibleForTesting());
+
+  // If the release track UI is enabled AND the release track is non-stable, the
+  // ChannelIndicatorQuickSettingsView is shown.
+  EXPECT_EQ(IsReleaseTrackUiEnabled() && IsReleaseTrackNotStable(),
+            info_view()->IsChannelIndicatorQuickSettingsVisibleForTesting());
 }
 
 using UnifiedSystemInfoViewNoSessionTest = NoSessionAshTestBase;
@@ -118,7 +205,7 @@
   // Before login the supervised user view is invisible.
   {
     auto info_view = std::make_unique<UnifiedSystemInfoView>(controller.get());
-    EXPECT_FALSE(info_view->supervised_->GetVisible());
+    EXPECT_FALSE(info_view->IsSupervisedVisibleForTesting());
   }
 
   // Simulate a supervised user logging in.
@@ -133,7 +220,7 @@
   // Now the supervised user view is visible.
   {
     auto info_view = std::make_unique<UnifiedSystemInfoView>(controller.get());
-    EXPECT_TRUE(info_view->supervised_->GetVisible());
+    EXPECT_TRUE(info_view->IsSupervisedVisibleForTesting());
   }
 }
 
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index 2d401a7c..0cd0493 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -5,6 +5,7 @@
 #include "ash/test_shell_delegate.h"
 
 #include <memory>
+#include <string>
 
 #include "ash/accessibility/default_accessibility_delegate.h"
 #include "ash/capture_mode/test_capture_mode_delegate.h"
@@ -119,4 +120,8 @@
   return channel_;
 }
 
+std::string TestShellDelegate::GetVersionString() {
+  return version_string_;
+}
+
 }  // namespace ash
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 08e8f8b..db9404ea 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -6,6 +6,7 @@
 #define ASH_TEST_SHELL_DELEGATE_H_
 
 #include <memory>
+#include <string>
 
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "ash/shell_delegate.h"
@@ -67,9 +68,14 @@
   void OpenFeedbackPageForPersistentDesksBar() override {}
   void SetLastCommittedURLForWindow(const GURL& url);
   version_info::Channel GetChannel() override;
+  std::string GetVersionString() override;
 
   void set_channel(version_info::Channel channel) { channel_ = channel; }
 
+  void set_version_string(const std::string& string) {
+    version_string_ = string;
+  }
+
  private:
   // True if the current top window can go back.
   bool can_go_back_ = true;
@@ -91,6 +97,8 @@
   GURL last_committed_url_ = GURL::EmptyGURL();
 
   version_info::Channel channel_ = version_info::Channel::UNKNOWN;
+
+  std::string version_string_;
 };
 
 }  // namespace ash
diff --git a/ash/webui/os_feedback_ui/backend/BUILD.gn b/ash/webui/os_feedback_ui/backend/BUILD.gn
index a4151c0..867d97a5 100644
--- a/ash/webui/os_feedback_ui/backend/BUILD.gn
+++ b/ash/webui/os_feedback_ui/backend/BUILD.gn
@@ -12,6 +12,8 @@
     "feedback_service_provider.h",
     "help_content_provider.cc",
     "help_content_provider.h",
+    "histogram_util.cc",
+    "histogram_util.h",
     "os_feedback_delegate.h",
   ]
 
diff --git a/ash/webui/os_feedback_ui/backend/feedback_service_provider.cc b/ash/webui/os_feedback_ui/backend/feedback_service_provider.cc
index ca7c630e..eeea38a 100644
--- a/ash/webui/os_feedback_ui/backend/feedback_service_provider.cc
+++ b/ash/webui/os_feedback_ui/backend/feedback_service_provider.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/webui/os_feedback_ui/backend/histogram_util.h"
 #include "ash/webui/os_feedback_ui/backend/os_feedback_delegate.h"
 #include "ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom.h"
 #include "base/bind.h"
@@ -23,9 +24,14 @@
 
 FeedbackServiceProvider::FeedbackServiceProvider(
     std::unique_ptr<OsFeedbackDelegate> feedback_delegate)
-    : feedback_delegate_(std::move(feedback_delegate)) {}
+    : feedback_delegate_(std::move(feedback_delegate)) {
+  open_timestamp_ = base::Time::Now();
+}
 
-FeedbackServiceProvider::~FeedbackServiceProvider() = default;
+FeedbackServiceProvider::~FeedbackServiceProvider() {
+  const base::TimeDelta time_open = base::Time::Now() - open_timestamp_;
+  ash::os_feedback_ui::metrics::EmitFeedbackAppOpenDuration(time_open);
+}
 
 void FeedbackServiceProvider::GetFeedbackContext(
     GetFeedbackContextCallback callback) {
diff --git a/ash/webui/os_feedback_ui/backend/feedback_service_provider.h b/ash/webui/os_feedback_ui/backend/feedback_service_provider.h
index 3e751fb..3221d67 100644
--- a/ash/webui/os_feedback_ui/backend/feedback_service_provider.h
+++ b/ash/webui/os_feedback_ui/backend/feedback_service_provider.h
@@ -9,6 +9,7 @@
 
 #include "ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -45,6 +46,9 @@
   std::unique_ptr<OsFeedbackDelegate> feedback_delegate_;
   mojo::Receiver<os_feedback_ui::mojom::FeedbackServiceProvider> receiver_{
       this};
+  // Timestamp of when the app was opened. Used to calculate a duration for
+  // metrics.
+  base::Time open_timestamp_;
   base::WeakPtrFactory<FeedbackServiceProvider> weak_ptr_factory_{this};
 };
 
diff --git a/ash/webui/os_feedback_ui/backend/histogram_util.cc b/ash/webui/os_feedback_ui/backend/histogram_util.cc
new file mode 100644
index 0000000..ee1320029
--- /dev/null
+++ b/ash/webui/os_feedback_ui/backend/histogram_util.cc
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "ash/webui/os_feedback_ui/backend/histogram_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/time/time.h"
+
+namespace ash::os_feedback_ui::metrics {
+
+void EmitFeedbackAppOpenDuration(const base::TimeDelta& time_elapsed) {
+  base::UmaHistogramLongTimes100(kFeedbackAppOpenDuration, time_elapsed);
+}
+
+}  // namespace ash::os_feedback_ui::metrics
diff --git a/ash/webui/os_feedback_ui/backend/histogram_util.h b/ash/webui/os_feedback_ui/backend/histogram_util.h
new file mode 100644
index 0000000..116129e5
--- /dev/null
+++ b/ash/webui/os_feedback_ui/backend/histogram_util.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_OS_FEEDBACK_UI_BACKEND_HISTOGRAM_UTIL_H_
+#define ASH_WEBUI_OS_FEEDBACK_UI_BACKEND_HISTOGRAM_UTIL_H_
+
+namespace base {
+class TimeDelta;
+}  // namespace base
+
+namespace ash::os_feedback_ui::metrics {
+
+constexpr char kFeedbackAppOpenDuration[] = "Feedback.ChromeOSApp.OpenDuration";
+
+void EmitFeedbackAppOpenDuration(const base::TimeDelta& time_elapsed);
+
+}  // namespace ash::os_feedback_ui::metrics
+
+#endif  // ASH_WEBUI_OS_FEEDBACK_UI_BACKEND_HISTOGRAM_UTIL_H_
\ No newline at end of file
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc
index fd5668c..15130917 100644
--- a/ash/webui/os_feedback_ui/os_feedback_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -61,7 +61,9 @@
       {"previewImageAriaLabel", IDS_FEEDBACK_TOOL_PREVIEW_IMAGE_ARIA_LABEL},
       {"addFileLabel", IDS_FEEDBACK_TOOL_ADD_FILE_LABEL},
       {"replaceFileLabel", IDS_FEEDBACK_TOOL_REPLACE_FILE_LABEL},
+      {"replaceFileArialLabel", IDS_FEEDBACK_TOOL_REPLACE_FILE_ARIA_LABEL},
       {"userEmailLabel", IDS_FEEDBACK_TOOL_USER_EMAIL_LABEL},
+      {"userEmailAriaLabel", IDS_FEEDBACK_TOOL_USER_EMAIL_ARIA_LABEL},
       {"shareDiagnosticDataLabel",
        IDS_FEEDBACK_TOOL_SHARE_DIAGNOSTIC_DATA_LABEL},
       {"sharePageUrlLabel", IDS_FEEDBACK_TOOL_SHARE_PAGE_URL_LABEL},
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.html b/ash/webui/os_feedback_ui/resources/file_attachment.html
index 5d1b03865..d6bffdf 100644
--- a/ash/webui/os_feedback_ui/resources/file_attachment.html
+++ b/ash/webui/os_feedback_ui/resources/file_attachment.html
@@ -92,7 +92,8 @@
   <input type="checkbox" id="selectFileCheckbox">
   <div id="replaceFileInfo">
     <div id="selectedFileName" class="overflow-text">[[selectedFileName_]]</div>
-    <button id="replaceFileLabel" class="file-input"
+    <button id="replaceFileButton" class="file-input"
+        aria-label="[[i18n('replaceFileArialLabel')]]"
         on-click="handleOpenFileInputClick_">
       [[i18n('replaceFileLabel')]]
     </button>
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html
index f3d621c9..b486bdc 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -171,7 +171,7 @@
         hidden$="[[!hasEmail_(feedbackContext)]]">
       <h2 id="userEmailLabel">[[i18n('userEmailLabel')]]</h2>
       <select id="userEmailDropDown" class="md-select"
-          aria-labelledby="userEmailLabel">
+          aria-label="[[i18n('userEmailAriaLabel')]]">
         <option value$="[[feedbackContext.email]]"
             class="email-dropdown">
           [[feedbackContext.email]]
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index 4351964..7e62002 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -84,6 +84,9 @@
         this.i18n('attachScreenshotCheckboxAriaLabel');
     this.$.imageButton.ariaLabel = this.i18n(
         'previewImageAriaLabel', this.$.screenshotCheckLabel.textContent);
+    // The default role is combobox. Set it in JS to avoid the JSCompiler
+    // error not recognizing the role property.
+    this.$.userEmailDropDown.role = 'listbox';
 
     // Set up event listener for email change to retarget |this| to be the
     // ShareDataPageElement's context.
diff --git a/ash/webui/projector_app/test/projector_message_handler_unittest.cc b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
index 205055e..278d7fc 100644
--- a/ash/webui/projector_app/test/projector_message_handler_unittest.cc
+++ b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
@@ -172,12 +172,12 @@
   EXPECT_TRUE(call_data.arg2()->GetBool());
   ASSERT_TRUE(call_data.arg3()->is_list());
 
-  const auto& list_view = call_data.arg3()->GetListDeprecated();
+  const base::Value::List& list = call_data.arg3()->GetList();
   // There is only one account in the identity manager.
-  EXPECT_EQ(list_view.size(), 1u);
+  EXPECT_EQ(list.size(), 1u);
 
   // Ensure that the entry is an account with a the valid email.
-  const auto& account = list_view[0];
+  const auto& account = list[0];
   const std::string* email = account.FindStringPath("email");
   ASSERT_NE(email, nullptr);
   EXPECT_EQ(*email, kTestUserEmail);
@@ -414,11 +414,11 @@
   EXPECT_TRUE(call_data.arg2()->GetBool());
   ASSERT_TRUE(call_data.arg3()->is_list());
 
-  const auto& list_view = call_data.arg3()->GetListDeprecated();
+  const base::Value::List& list = call_data.arg3()->GetList();
   // There is only one screencast.
-  EXPECT_EQ(list_view.size(), 1u);
+  EXPECT_EQ(list.size(), 1u);
 
-  const auto& screencast = list_view[0];
+  const auto& screencast = list[0];
   EXPECT_EQ(*(screencast.FindStringPath("name")), name);
   EXPECT_EQ(*(screencast.FindDoublePath("createdTime")), 0);
   EXPECT_EQ(*(screencast.FindBoolPath("uploadFailed")), false);
diff --git a/ash/wm/desks/desks_restore_util.cc b/ash/wm/desks/desks_restore_util.cc
index 772c6ae..873072f 100644
--- a/ash/wm/desks/desks_restore_util.cc
+++ b/ash/wm/desks/desks_restore_util.cc
@@ -260,20 +260,20 @@
   }
 
   ListPrefUpdate name_update(primary_user_prefs, prefs::kDesksNamesList);
-  base::Value* name_pref_data = name_update.Get();
-  name_pref_data->ClearList();
+  base::Value::List& name_pref_data = name_update->GetList();
+  name_pref_data.clear();
 
   const auto& desks = DesksController::Get()->desks();
   for (const auto& desk : desks) {
     // Desks whose names were not changed by the user, are stored as empty
     // strings. They're just place holders to restore the correct desks count.
     // RestorePrimaryUserDesks() restores only non-empty desks names.
-    name_pref_data->Append(desk->is_name_set_by_user()
-                               ? base::UTF16ToUTF8(desk->name())
-                               : std::string());
+    name_pref_data.Append(desk->is_name_set_by_user()
+                              ? base::UTF16ToUTF8(desk->name())
+                              : std::string());
   }
 
-  DCHECK_EQ(name_pref_data->GetListDeprecated().size(), desks.size());
+  DCHECK_EQ(name_pref_data.size(), desks.size());
 
   if (IsNowInValidTimePeriod() &&
       !primary_user_prefs->GetBoolean(kUserHasUsedDesksRecently)) {
@@ -293,8 +293,8 @@
 
   // Save per-desk metrics.
   ListPrefUpdate metrics_update(primary_user_prefs, prefs::kDesksMetricsList);
-  base::Value* metrics_pref_data = metrics_update.Get();
-  metrics_pref_data->ClearList();
+  base::Value::List& metrics_pref_data = metrics_update->GetList();
+  metrics_pref_data.clear();
 
   auto* desks_controller = DesksController::Get();
   const auto& desks = desks_controller->desks();
@@ -307,10 +307,10 @@
     metrics_dict.SetIntKey(kLastDayVisitedKey, desk->last_day_visited());
     metrics_dict.SetBoolKey(kInteractedWithThisWeekKey,
                             desk->interacted_with_this_week());
-    metrics_pref_data->Append(std::move(metrics_dict));
+    metrics_pref_data.Append(std::move(metrics_dict));
   }
 
-  DCHECK_EQ(metrics_pref_data->GetListDeprecated().size(), desks.size());
+  DCHECK_EQ(metrics_pref_data.size(), desks.size());
 
   // Save weekly active report time.
   DictionaryPrefUpdate weekly_active_desks_update(
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index cb049be3..ae64254 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -3442,10 +3442,10 @@
                                      std::vector<std::string> desk_names) {
     DCHECK(prefs);
     ListPrefUpdate update(prefs, prefs::kDesksNamesList);
-    base::Value* pref_data = update.Get();
-    ASSERT_TRUE(pref_data->GetListDeprecated().empty());
+    base::Value::List& pref_data = update->GetList();
+    ASSERT_TRUE(pref_data.empty());
     for (auto desk_name : desk_names)
-      pref_data->Append(desk_name);
+      pref_data.Append(desk_name);
   }
 
   void SimulateUserLogin(const AccountId& account_id) {
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 7022318a..15495a6 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-9.20220722.2.1
+9.20220723.1.1
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 76f0bd1..92c0bea 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision vars in //DEPS.
-  libcxx_revision = "eb79671bfbedd77b747d01dee8c0479ff1693f88"
+  libcxx_revision = "ae6c9d1fb41406da05a3b6def66151b41fff086c"
 }
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 7804140c..c9d6077 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1421,7 +1421,7 @@
     "//v8:v8_headers",
   ]
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     public_deps += [ "//ppapi/host" ]
   }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 9b5c631..d553d4d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=106
 MINOR=0
-BUILD=5196
+BUILD=5197
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 4d95ab1..9e45963 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -1680,6 +1680,16 @@
         mRemoveWindowBackgroundDone = true;
     }
 
+    /**
+     * @return The primary display size of the device, in inches.
+     */
+    private double getPrimaryDisplaySizeInInches() {
+        DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(this);
+        double xInches = display.getDisplayWidth() / display.getXdpi();
+        double yInches = display.getDisplayHeight() / display.getYdpi();
+        return Math.sqrt(Math.pow(xInches, 2) + Math.pow(yInches, 2));
+    }
+
     @Override
     public void finishNativeInitialization() {
         mNativeInitialized = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/feed/FeedActionDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/feed/FeedActionDelegateImpl.java
index 4b1ca6d..9bc1f93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/feed/FeedActionDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/feed/FeedActionDelegateImpl.java
@@ -55,15 +55,16 @@
     }
 
     @Override
-    public void openSuggestionUrl(int disposition, LoadUrlParams params, Runnable onPageLoaded,
-            Callback<VisitResult> onVisitComplete) {
+    public void openSuggestionUrl(int disposition, LoadUrlParams params, boolean inGroup,
+            Runnable onPageLoaded, Callback<VisitResult> onVisitComplete) {
         params.setReferrer(
                 new Referrer(SuggestionsConfig.getReferrerUrl(ChromeFeatureList.INTEREST_FEED_V2),
                         // WARNING: ReferrerPolicy.ALWAYS is assumed by other Chrome code for NTP
                         // tiles to set consider_for_ntp_most_visited.
                         org.chromium.network.mojom.ReferrerPolicy.ALWAYS));
 
-        Tab tab = mNavigationDelegate.openUrl(disposition, params);
+        Tab tab = inGroup ? mNavigationDelegate.openUrlInGroup(disposition, params)
+                          : mNavigationDelegate.openUrl(disposition, params);
 
         NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
index aee7ca2a..70d136f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
@@ -14,6 +14,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
@@ -115,12 +116,18 @@
             TileGroup.Delegate tileGroupDelegate, TouchEnabledDelegate touchEnabledDelegate) {
         mActivityLifecycleDispatcher.register(this);
         Profile profile = Profile.getLastUsedRegularProfile();
+        int titleLines =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.NEW_TAB_PAGE_TILES_TITLE_WRAP_AROUND)
+                ? 2
+                : 1;
         if (mRenderer == null) {
             mRenderer = new TileRenderer(mActivity, SuggestionsConfig.getTileStyle(mUiConfig),
-                    TITLE_LINES, suggestionsUiDelegate.getImageFetcher());
+                    titleLines, suggestionsUiDelegate.getImageFetcher());
         } else {
             mRenderer.setImageFetcher(suggestionsUiDelegate.getImageFetcher());
+            mRenderer.setTitleLines(titleLines);
         }
+        mRenderer.onNativeInitializationReady();
 
         mContextMenuManager = new ContextMenuManager(suggestionsUiDelegate.getNavigationDelegate(),
                 touchEnabledDelegate, mActivity::closeContextMenu, CONTEXT_MENU_USER_ACTION_PREFIX);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
index 1e65776..bffbc44b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -64,10 +64,11 @@
 
     @TileStyle
     private final int mStyle;
-    private final int mTitleLinesCount;
     private final int mDesiredIconSize;
     private final int mMinIconSize;
     private final float mIconCornerRadius;
+    private int mTitleLinesCount;
+    private boolean mNativeInitializationComplete;
 
     @LayoutRes
     private final int mLayout;
@@ -167,6 +168,14 @@
     }
 
     /**
+     * Override currently set maximum number of title lines.
+     * @param titleLines The new max number of title lines to be shown under the tile icon.
+     */
+    public void setTitleLines(int titleLines) {
+        mTitleLinesCount = titleLines;
+    }
+
+    /**
      * Record that a tile was clicked for IPH reasons.
      */
     private void recordTileClickedForIPH(String eventName) {
@@ -211,7 +220,7 @@
 
         tileView.initialize(tile, mTitleLinesCount);
 
-        if (!LibraryLoader.getInstance().isInitialized() || setupDelegate == null) {
+        if (!mNativeInitializationComplete || setupDelegate == null) {
             return tileView;
         }
 
@@ -278,6 +287,14 @@
     }
 
     /**
+     * Notify the component that the native initialization has completed and the component can
+     * safely execute native code.
+     */
+    public void onNativeInitializationReady() {
+        mNativeInitializationComplete = true;
+    }
+
+    /**
      * Given a Tile data and TileView, apply appropriate content description that will be announced
      * when the view is focused for accessibility.
      * The objective of the description is to offer audible guidance that helps users differentiate
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
index da487d0..6442a59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
@@ -23,6 +23,11 @@
  * Utilities for requesting desktop sites support.
  */
 public class RequestDesktopUtils {
+    private static final String PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES =
+            "default_on_display_size_threshold_inches";
+    private static final double DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES =
+            12.0;
+
     // Note: these values must match the UserAgentRequestType enum in enums.xml.
     @IntDef({UserAgentRequestType.REQUEST_DESKTOP, UserAgentRequestType.REQUEST_MOBILE})
     @Retention(RetentionPolicy.SOURCE)
@@ -104,4 +109,22 @@
         WebsitePreferenceBridge.setContentSettingDefaultScope(browserContextHandle,
                 ContentSettingsType.REQUEST_DESKTOP_SITE, url, url, contentSettingValue);
     }
+
+    /**
+     * @param displaySizeInInches The device primary display size, in inches.
+     * @return Whether the desktop site global setting should be default-enabled.
+     */
+    public static boolean shouldDefaultEnableGlobalSetting(double displaySizeInInches) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS)) {
+            return false;
+        }
+
+        // TODO(crbug.com/1343916): Also check new SharedPreferences for REQUEST_DESKTOP_SITE to
+        // determine if the setting should be updated. Also nice to have a Finch configurable
+        // boolean to disable default-enabling this setting on low RAM devices.
+        return displaySizeInInches >= ChromeFeatureList.getFieldTrialParamByFeatureAsDouble(
+                       ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS,
+                       PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
+                       DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES);
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index d20ed43..063b80b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -41,6 +41,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
+
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
@@ -51,7 +52,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.PackageManagerWrapper;
 import org.chromium.base.test.util.Restriction;
@@ -1170,14 +1170,20 @@
         mActivityTestRule.startMainActivityOnBlankPage();
 
         String url = mTestServer.getURL(NAVIGATION_FROM_TIMEOUT_PAGE);
-        @OverrideUrlLoadingResultType
-        int result = loadUrlAndWaitForIntentUrl(url, true, null, PageTransition.AUTO_BOOKMARK);
+        if (RedirectHandler.isRefactoringEnabled()) {
+            @OverrideUrlLoadingResultType
+            int result = loadUrlAndWaitForIntentUrl(url, false, null, PageTransition.AUTO_BOOKMARK);
+            Assert.assertEquals(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result);
+            assertMessagePresent();
+        } else {
+            loadUrlAndWaitForIntentUrl(url, true, null, PageTransition.AUTO_BOOKMARK);
+        }
     }
 
     @Test
     @LargeTest
-    @DisabledTest(message = "Re-enable when crbug.com/1300539 is fixed.")
     public void testRedirectFromBookmarkWithFallback() throws Exception {
+        if (!RedirectHandler.isRefactoringEnabled()) return;
         mActivityTestRule.startMainActivityOnBlankPage();
 
         String fallbackUrl = mTestServer.getURL(FALLBACK_LANDING_PATH);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
index 51e4f09..b1356cb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
@@ -33,7 +33,6 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowDrawable;
 
-import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.chrome.R;
@@ -129,7 +128,6 @@
         mPostTaskRunner = new ShadowPostTaskImpl();
         ShadowPostTask.setTestImpl(mPostTaskRunner);
 
-        LibraryLoader.getInstance().setLibrariesLoadedForNativeTests();
         TemplateUrlServiceFactory.setInstanceForTesting(mMockTemplateUrlService);
 
         mSharedParent = new LinearLayout(mActivity);
@@ -146,21 +144,23 @@
         doReturn(mBitmap).when(mIconGenerator).generateIconForUrl(any(GURL.class));
     }
 
-    private void buildTileView(@TileStyle int style) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
+    private SuggestionsTileView buildTileView(@TileStyle int style, int titleLines) {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
             TileRenderer tileRenderer =
-                    new TileRenderer(mActivity, style, TITLE_LINES, mMockImageFetcher);
+                    new TileRenderer(mActivity, style, titleLines, mMockImageFetcher);
             tileRenderer.setIconGeneratorForTesting(mIconGenerator);
+            tileRenderer.onNativeInitializationReady();
             SuggestionsTileView tileView =
                     tileRenderer.buildTileView(mTile, mSharedParent, mTileSetupDelegate);
             Assert.assertNotNull(tileView);
+            return tileView;
         });
     }
 
     @Test
     @SmallTest
     public void testBuildTestView_Modern_noDecoration() {
-        buildTileView(TileStyle.MODERN);
+        buildTileView(TileStyle.MODERN, TITLE_LINES);
         // Expect no callbacks: we don't have any icon to offer there.
         mPostTaskRunner.runAll();
         verify(mTileSetupCallback, times(0)).run();
@@ -169,7 +169,7 @@
     @Test
     @SmallTest
     public void testBuildTileView_ModernCondensed_noDecoration() {
-        buildTileView(TileStyle.MODERN_CONDENSED);
+        buildTileView(TileStyle.MODERN_CONDENSED, TITLE_LINES);
         // Expect no callbacks: we don't have any icon to offer there.
         mPostTaskRunner.runAll();
         verify(mTileSetupCallback, times(0)).run();
@@ -178,7 +178,7 @@
     @Test
     @SmallTest
     public void testBuildTileView_ModernCondensed_fallbackColor() {
-        buildTileView(TileStyle.MODERN_CONDENSED);
+        buildTileView(TileStyle.MODERN_CONDENSED, TITLE_LINES);
         // Expect no callbacks: we don't have any icon to offer there.
         mPostTaskRunner.runAll();
         verify(mMockImageFetcher, times(1))
@@ -199,7 +199,7 @@
     @Test
     @SmallTest
     public void testBuildTileView_ModernCondensed_favicon() {
-        buildTileView(TileStyle.MODERN_CONDENSED);
+        buildTileView(TileStyle.MODERN_CONDENSED, TITLE_LINES);
         // Expect no callbacks: we don't have any icon to offer there.
         mPostTaskRunner.runAll();
         verify(mMockImageFetcher, times(1))
@@ -223,7 +223,7 @@
                 .when(mMockTemplateUrlService)
                 .isSearchResultsPageFromDefaultSearchProvider(any());
 
-        buildTileView(TileStyle.MODERN);
+        buildTileView(TileStyle.MODERN, TITLE_LINES);
 
         verify(mTileSetupCallback, times(0)).run();
         mPostTaskRunner.runAll();
@@ -236,4 +236,44 @@
         Assert.assertEquals(
                 R.drawable.ic_suggestion_magnifier, shadowDrawable.getCreatedFromResId());
     }
+
+    @Test
+    @SmallTest
+    public void testTileTitle_multiLineSearch() {
+        doReturn(true)
+                .when(mMockTemplateUrlService)
+                .isSearchResultsPageFromDefaultSearchProvider(any());
+        SuggestionsTileView tileView = buildTileView(TileStyle.MODERN, 2);
+        Assert.assertEquals(2, tileView.getTitleView().getMaxLines());
+    }
+
+    @Test
+    @SmallTest
+    public void testTileTitle_multiLineURL() {
+        doReturn(false)
+                .when(mMockTemplateUrlService)
+                .isSearchResultsPageFromDefaultSearchProvider(any());
+        SuggestionsTileView tileView = buildTileView(TileStyle.MODERN, 2);
+        Assert.assertEquals(2, tileView.getTitleView().getMaxLines());
+    }
+
+    @Test
+    @SmallTest
+    public void testTileTitle_singleLineSearch() {
+        doReturn(true)
+                .when(mMockTemplateUrlService)
+                .isSearchResultsPageFromDefaultSearchProvider(any());
+        SuggestionsTileView tileView = buildTileView(TileStyle.MODERN, 1);
+        Assert.assertEquals(1, tileView.getTitleView().getMaxLines());
+    }
+
+    @Test
+    @SmallTest
+    public void testTileTitle_singleLineURL() {
+        doReturn(false)
+                .when(mMockTemplateUrlService)
+                .isSearchResultsPageFromDefaultSearchProvider(any());
+        SuggestionsTileView tileView = buildTileView(TileStyle.MODERN, 1);
+        Assert.assertEquals(1, tileView.getTitleView().getMaxLines());
+    }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index d341ab0f..58995262 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-105.0.5195.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-105.0.5195.3_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ac0711c..05d688ae 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -7293,7 +7293,8 @@
     ]
   }
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "plugins/chrome_content_browser_client_plugins_part.cc",
       "plugins/chrome_content_browser_client_plugins_part.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7182f5c0..8fbb614a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6220,6 +6220,11 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_ANDROID)
+    {"ntp-tiles-title-wrap-around",
+     flag_descriptions::kNewTabPageTilesTitleWrapAroundName,
+     flag_descriptions::kNewTabPageTilesTitleWrapAroundDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kNewTabPageTilesTitleWrapAround)},
+
     {"new-window-app-menu", flag_descriptions::kNewWindowAppMenuName,
      flag_descriptions::kNewWindowAppMenuDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kNewWindowAppMenu)},
@@ -8963,6 +8968,13 @@
      FEATURE_VALUE_TYPE(features::kRequestDesktopSiteAdditions)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"request-desktop-site-defaults",
+     flag_descriptions::kRequestDesktopSiteDefaultsName,
+     flag_descriptions::kRequestDesktopSiteDefaultsDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kRequestDesktopSiteDefaults)},
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if !BUILDFLAG(IS_ANDROID)
     {"enable-web-hid-on-extension-service-worker",
      flag_descriptions::kEnableWebHidOnExtensionServiceWorkerName,
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index 63c9909..adbb97d 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -273,6 +273,8 @@
       prefs->GetString(prefs::kDemoModeRetailerId);
   demo_app_installer_attributes["store_id"] =
       prefs->GetString(prefs::kDemoModeStoreId);
+  demo_app_installer_attributes["demo_country"] =
+      prefs->GetString(prefs::kDemoModeCountry);
   return demo_app_installer_attributes;
 }
 
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index cbebfe8b..487664f 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -283,11 +283,15 @@
   return etld_plus_one.empty() ? site.spec() : etld_plus_one;
 }
 
-developer::SiteInfo CreateSiteInfo(const std::string& site,
-                                   developer::UserSiteSet site_set) {
+developer::SiteInfo CreateSiteInfo(
+    const std::string& site,
+    absl::optional<developer::UserSiteSet> site_set) {
   developer::SiteInfo site_info;
   site_info.site = site;
-  site_info.site_list = site_set;
+  if (site_set.has_value())
+    site_info.site_list = *site_set;
+  else
+    site_info.num_extensions = 1;
   return site_info;
 }
 
@@ -296,11 +300,82 @@
     std::map<std::string, developer::SiteGroup>* site_groups,
     const std::string& site,
     const std::string& etld_plus_one,
-    developer::UserSiteSet site_set) {
+    absl::optional<developer::UserSiteSet> site_set) {
   auto [it, inserted] = site_groups->try_emplace(etld_plus_one);
-  if (inserted)
+  if (inserted) {
     it->second.etld_plus_one = etld_plus_one;
-  it->second.sites.push_back(CreateSiteInfo(site, site_set));
+    it->second.sites.push_back(CreateSiteInfo(site, site_set));
+  } else {
+    auto site_info = std::find_if(
+        it->second.sites.begin(), it->second.sites.end(),
+        [site](const developer::SiteInfo& info) { return info.site == site; });
+
+    if (site_info == it->second.sites.end())
+      it->second.sites.push_back(CreateSiteInfo(site, site_set));
+    else
+      site_info->num_extensions++;
+  }
+}
+
+// Adds an extension's granted host permissions to `site_groups`,
+void ProcessSitesForRuntimeHostPermissions(
+    std::map<std::string, developer::SiteGroup>* site_groups,
+    const URLPatternSet& user_specified_sites,
+    const developer::RuntimeHostPermissions& permissions) {
+  // Track the set of eTLD+1s covered by the extension's granted host
+  // permissions.
+  std::set<std::string> etld_plus_ones;
+  for (const auto& host : permissions.hosts) {
+    // Convert the host permission pattern back to a URLPattern so it can
+    // be easily modified for comparing against user specified sites and for
+    // fetching the eTLD+1.
+    URLPattern pattern(Extension::kValidHostPermissionSchemes, host.host);
+    pattern.SetPath("");
+
+    // Ignore patterns that are empty, are not granted, or match with user
+    // specified sites.
+    if (pattern.host().empty() || !host.granted ||
+        user_specified_sites.ContainsPattern(pattern)) {
+      continue;
+    }
+
+    pattern.SetMatchSubdomains(false);
+    pattern.SetScheme("http");
+
+    std::string etld_plus_one = GetETldPlusOne(GURL(pattern.GetAsString()));
+    etld_plus_ones.insert(etld_plus_one);
+    AddSiteToSiteGroups(site_groups, host.host, etld_plus_one, absl::nullopt);
+  }
+
+  // Increment the extension count for each eTLD+1 covered by this extension's
+  // host permissions.
+  for (const auto& etld_plus_one : etld_plus_ones)
+    (*site_groups)[etld_plus_one].num_extensions++;
+}
+
+// Updates numExtensions counts in `site_groups` for `extension`. Note that this
+// should only be called for extensions with effective all hosts access.
+void UpdateSiteGroupCountsForExtension(
+    std::map<std::string, developer::SiteGroup>* site_groups,
+    const Extension* extension) {
+  const URLPatternSet extension_patterns =
+      extension->permissions_data()->GetEffectiveHostPermissions();
+  for (auto& entry : *site_groups) {
+    bool can_run_on_site_group = false;
+    for (developer::SiteInfo& site_info : entry.second.sites) {
+      if (site_info.site_list)
+        continue;
+
+      URLPattern pattern(Extension::kValidHostPermissionSchemes,
+                         site_info.site);
+      if (extension_patterns.ContainsPattern(pattern)) {
+        can_run_on_site_group = true;
+        site_info.num_extensions++;
+      }
+    }
+    if (can_run_on_site_group)
+      entry.second.num_extensions++;
+  }
 }
 
 }  // namespace
@@ -2265,22 +2340,76 @@
   std::map<std::string, developer::SiteGroup> site_groups;
   const PermissionsManager::UserPermissionsSettings& settings =
       PermissionsManager::Get(browser_context())->GetUserPermissionsSettings();
+  URLPatternSet user_specified_sites;
   for (const url::Origin& site : settings.permitted_sites) {
+    user_specified_sites.AddOrigin(Extension::kValidHostPermissionSchemes,
+                                   site.GetURL());
     AddSiteToSiteGroups(&site_groups, site.Serialize(),
                         GetETldPlusOne(site.GetURL()),
                         developer::USER_SITE_SET_PERMITTED);
   }
 
   for (const url::Origin& site : settings.restricted_sites) {
+    user_specified_sites.AddOrigin(Extension::kValidHostPermissionSchemes,
+                                   site.GetURL());
     AddSiteToSiteGroups(&site_groups, site.Serialize(),
                         GetETldPlusOne(site.GetURL()),
                         developer::USER_SITE_SET_RESTRICTED);
   }
 
+  std::vector<const Extension*> extensions_with_all_hosts;
+  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
+
+  // Note: we are only counting enabled extensions as the returned extension
+  // counts will reflect how many extensions can actually run on each site at
+  // the current moment.
+  for (const auto& extension : registry->enabled_extensions()) {
+    // TODO(crbug.com/1331137): Some extensions can access certain sites even if
+    // the user cannot modify their permissions. These also need to be added to
+    // another list so the frontend knows that their site access cannot be
+    // modified.
+    if (!ui_util::ShouldDisplayInExtensionSettings(*extension) ||
+        !ScriptingPermissionsModifier(browser_context(), extension)
+             .CanAffectExtension()) {
+      continue;
+    }
+
+    developer::RuntimeHostPermissions permissions =
+        ExtensionInfoGenerator::CreateRuntimeHostPermissionsInfo(
+            browser_context(), *extension);
+
+    bool has_specific_hosts =
+        (!permissions.has_all_hosts &&
+         permissions.host_access == developer::HOST_ACCESS_ON_ALL_SITES) ||
+        permissions.host_access == developer::HOST_ACCESS_ON_SPECIFIC_SITES;
+
+    if (permissions.host_access == developer::HOST_ACCESS_ON_ALL_SITES &&
+        permissions.has_all_hosts) {
+      extensions_with_all_hosts.push_back(extension.get());
+    } else if (has_specific_hosts) {
+      ProcessSitesForRuntimeHostPermissions(&site_groups, user_specified_sites,
+                                            permissions);
+    }
+  }
+
+  // Specifying a "broad enough" host permission like "*://*.com/*" makes an
+  // extension "match all hosts". However, the extension does not truly have
+  // access to all sites, hence we iterate over all populated sites in
+  // `site_groups` and update the count for the extension for each site that it
+  // has access to.
+  for (const Extension* extension : extensions_with_all_hosts)
+    UpdateSiteGroupCountsForExtension(&site_groups, extension);
+
   std::vector<developer::SiteGroup> site_group_list;
   site_group_list.reserve(site_groups.size());
-  for (auto& entry : site_groups)
+  for (auto& entry : site_groups) {
+    // Sort the sites in each SiteGroup in ascending order by site.
+    std::sort(entry.second.sites.begin(), entry.second.sites.end(),
+              [](const developer::SiteInfo& a, const developer::SiteInfo& b) {
+                return b.site > a.site;
+              });
     site_group_list.push_back(std::move(entry.second));
+  }
 
   return RespondNow(
       ArgumentList(developer::GetUserAndExtensionSitesByEtld::Results::Create(
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index 0631f5a..46d1f00 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/scoped_observation.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/values_test_util.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/error_console/error_console.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
@@ -156,6 +157,35 @@
       << function->GetError();
 }
 
+void AddExtensionAndGrantPermissions(Profile* profile,
+                                     ExtensionService* service,
+                                     const Extension& extension) {
+  PermissionsUpdater updater(profile);
+  updater.InitializePermissions(&extension);
+  updater.GrantActivePermissions(&extension);
+  service->AddExtension(&extension);
+}
+
+void RunAddHostPermission(Profile* profile,
+                          const Extension& extension,
+                          base::StringPiece host,
+                          bool should_succeed,
+                          const char* expected_error) {
+  SCOPED_TRACE(host);
+  scoped_refptr<ExtensionFunction> function =
+      base::MakeRefCounted<api::DeveloperPrivateAddHostPermissionFunction>();
+
+  std::string args = base::StringPrintf(R"(["%s", "%s"])",
+                                        extension.id().c_str(), host.data());
+  if (should_succeed) {
+    EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile))
+        << function->GetError();
+  } else {
+    EXPECT_EQ(expected_error, api_test_utils::RunFunctionAndReturnError(
+                                  function.get(), args, profile));
+  }
+}
+
 }  // namespace
 
 class DeveloperPrivateApiUnitTest : public ExtensionServiceTestWithInstall {
@@ -1424,50 +1454,37 @@
   EXPECT_FALSE(modifier.HasWithheldHostPermissions());
   modifier.SetWithholdHostPermissions(true);
 
-  auto run_add_host_permission = [this, extension](base::StringPiece host,
-                                                   bool should_succeed,
-                                                   const char* expected_error) {
-    SCOPED_TRACE(host);
-    scoped_refptr<ExtensionFunction> function =
-        base::MakeRefCounted<api::DeveloperPrivateAddHostPermissionFunction>();
-
-    std::string args = base::StringPrintf(R"(["%s", "%s"])",
-                                          extension->id().c_str(), host.data());
-    if (should_succeed) {
-      EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile()))
-          << function->GetError();
-    } else {
-      EXPECT_EQ(expected_error, api_test_utils::RunFunctionAndReturnError(
-                                    function.get(), args, profile()));
-    }
-  };
-
   const GURL kExampleCom("https://example.com/");
   EXPECT_FALSE(modifier.HasGrantedHostPermission(kExampleCom));
-  run_add_host_permission("https://example.com/*", true, nullptr);
+  RunAddHostPermission(profile(), *extension, "https://example.com/*",
+                       /*should_succeed=*/true, nullptr);
   EXPECT_TRUE(modifier.HasGrantedHostPermission(kExampleCom));
 
   const GURL kGoogleCom("https://google.com");
   const GURL kMapsGoogleCom("https://maps.google.com/");
   EXPECT_FALSE(modifier.HasGrantedHostPermission(kGoogleCom));
   EXPECT_FALSE(modifier.HasGrantedHostPermission(kMapsGoogleCom));
-  run_add_host_permission("https://*.google.com/*", true, nullptr);
+  RunAddHostPermission(profile(), *extension, "https://*.google.com/*",
+                       /*should_succeed=*/true, nullptr);
   EXPECT_TRUE(modifier.HasGrantedHostPermission(kGoogleCom));
   EXPECT_TRUE(modifier.HasGrantedHostPermission(kMapsGoogleCom));
 
-  run_add_host_permission(kInvalidHost, false, kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, kInvalidHost,
+                       /*should_succeed=*/false, kInvalidHostError);
   // Path of the pattern must exactly match "/*".
-  run_add_host_permission("https://example.com/", false, kInvalidHostError);
-  run_add_host_permission("https://example.com/foobar", false,
-                          kInvalidHostError);
-  run_add_host_permission("https://example.com/#foobar", false,
-                          kInvalidHostError);
-  run_add_host_permission("https://example.com/*foobar", false,
-                          kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, "https://example.com/",
+                       /*should_succeed=*/false, kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, "https://example.com/foobar",
+                       /*should_succeed=*/false, kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, "https://example.com/#foobar",
+                       /*should_succeed=*/false, kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, "https://example.com/*foobar",
+                       /*should_succeed=*/false, kInvalidHostError);
 
   // Cannot grant chrome:-scheme URLs.
   GURL chrome_host("chrome://settings/*");
-  run_add_host_permission(chrome_host.spec(), false, kInvalidHostError);
+  RunAddHostPermission(profile(), *extension, chrome_host.spec(),
+                       /*should_succeed=*/false, kInvalidHostError);
 
   EXPECT_FALSE(modifier.HasGrantedHostPermission(chrome_host));
 }
@@ -1969,7 +1986,7 @@
 }
 
 TEST_F(DeveloperPrivateApiUnitTest,
-       DeveloperPrivateGetUserAndExtensionSitesByEtld) {
+       DeveloperPrivateGetUserAndExtensionSitesByEtld_UserSites) {
   PermissionsManager* manager = PermissionsManager::Get(browser_context());
 
   // Add two sites under the eTLD+1 example.com, and one under eTLD+1 google.ca.
@@ -1984,46 +2001,207 @@
   EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError();
   const base::Value::List* results = function->GetResultList();
   ASSERT_EQ(1u, results->size());
-  ASSERT_TRUE((*results)[0].is_list());
-  const base::Value::List& list = (*results)[0].GetList();
-  ASSERT_EQ(2u, list.size());
 
-  auto site_info_matcher =
-      [](const std::string& site,
-         const api::developer_private::UserSiteSet& site_list) {
-        return testing::AllOf(
-            testing::Field(&api::developer_private::SiteInfo::site, site),
-            testing::Field(&api::developer_private::SiteInfo::site_list,
-                           site_list));
-      };
+  EXPECT_THAT((*results)[0], base::test::IsJson(R"([{
+    "etldPlusOne": "example.com",
+    "numExtensions": 0,
+    "sites": [{
+      "siteList": "PERMITTED",
+      "numExtensions": 0,
+      "site": "http://a.example.com",
+    }, {
+      "siteList": "RESTRICTED",
+      "numExtensions": 0,
+      "site": "http://b.example.com",
+    }]
+  }, {
+    "etldPlusOne": "google.ca",
+    "numExtensions": 0,
+    "sites": [{
+      "siteList": "RESTRICTED",
+      "numExtensions": 0,
+      "site": "http://google.ca",
+    }]
+  }])"));
+}
 
-  // There should be two SiteGroups for the two eTLD+1s.
-  std::unique_ptr<api::developer_private::SiteGroup> example_info =
-      api::developer_private::SiteGroup::FromValue(list[0]);
-  ASSERT_TRUE(example_info);
+TEST_F(DeveloperPrivateApiUnitTest,
+       DeveloperPrivateGetUserAndExtensionSitesByEtld_UserAndExtensionSites) {
+  PermissionsManager* manager = PermissionsManager::Get(browser_context());
+  manager->AddUserPermittedSite(
+      url::Origin::Create(GURL("http://images.google.com")));
+  manager->AddUserRestrictedSite(
+      url::Origin::Create(GURL("http://www.asdf.com")));
 
-  EXPECT_EQ("example.com", example_info->etld_plus_one);
-  EXPECT_THAT(
-      example_info->sites,
-      testing::UnorderedElementsAre(
-          site_info_matcher(
-              "http://a.example.com",
-              api::developer_private::UserSiteSet::USER_SITE_SET_PERMITTED),
-          site_info_matcher(
-              "http://b.example.com",
-              api::developer_private::UserSiteSet::USER_SITE_SET_RESTRICTED)));
+  scoped_refptr<const Extension> extension_1 =
+      ExtensionBuilder("test")
+          .AddPermission("https://*.google.com/")
+          .AddPermission("http://www.google.com/")
+          .AddPermission("http://images.google.com/")
+          .AddPermission("https://example.com/")
+          .Build();
 
-  std::unique_ptr<api::developer_private::SiteGroup> google_info =
-      api::developer_private::SiteGroup::FromValue(list[1]);
-  ASSERT_TRUE(google_info);
+  scoped_refptr<const Extension> extension_2 =
+      ExtensionBuilder("test_2")
+          .AddPermission("https://mail.google.com/")
+          .AddPermission("http://www.google.com/")
+          .AddPermission("http://www.asdf.com/")
+          .Build();
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_1);
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_2);
 
-  // Check the contents of the SiteInfo under google.ca.
-  EXPECT_EQ("google.ca", google_info->etld_plus_one);
-  EXPECT_THAT(
-      google_info->sites,
-      testing::UnorderedElementsAre(site_info_matcher(
-          "http://google.ca",
-          api::developer_private::UserSiteSet::USER_SITE_SET_RESTRICTED)));
+  scoped_refptr<ExtensionFunction> function = base::MakeRefCounted<
+      api::DeveloperPrivateGetUserAndExtensionSitesByEtldFunction>();
+  EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError();
+  const base::Value::List* results = function->GetResultList();
+  ASSERT_EQ(1u, results->size());
+
+  // asdf.com and http://www.asdf.com should not have any extensions counted
+  // because they are associated with user specified sites.
+  EXPECT_THAT((*results)[0], base::test::IsJson(R"([{
+    "etldPlusOne": "asdf.com",
+    "numExtensions": 0,
+    "sites": [{
+      "siteList": "RESTRICTED",
+      "numExtensions": 0,
+      "site": "http://www.asdf.com",
+    }]
+  }, {
+    "etldPlusOne": "example.com",
+    "numExtensions": 1,
+    "sites": [{
+      "numExtensions": 1,
+      "site": "https://example.com/*",
+    }]
+  }, {
+    "etldPlusOne": "google.com",
+    "numExtensions": 2,
+    "sites": [{
+      "siteList": "PERMITTED",
+      "numExtensions": 0,
+      "site": "http://images.google.com",
+    }, {
+      "numExtensions": 2,
+      "site": "http://www.google.com/*",
+    }, {
+      "numExtensions": 1,
+      "site": "https://*.google.com/*",
+    }, {
+      "numExtensions": 1,
+      "site": "https://mail.google.com/*",
+    }]
+  }])"));
+}
+
+TEST_F(DeveloperPrivateApiUnitTest,
+       DeveloperPrivateGetUserAndExtensionSitesByEtld_EffectiveAllHosts) {
+  PermissionsManager* manager = PermissionsManager::Get(browser_context());
+  manager->AddUserPermittedSite(
+      url::Origin::Create(GURL("http://images.google.ca")));
+
+  scoped_refptr<const Extension> extension_1 =
+      ExtensionBuilder("specific_hosts")
+          .AddPermission("https://*.google.ca/")
+          .AddPermission("http://www.example.com/")
+          .Build();
+
+  scoped_refptr<const Extension> extension_2 =
+      ExtensionBuilder("all_.com").AddPermission("*://*.com/*").Build();
+
+  scoped_refptr<const Extension> extension_3 =
+      ExtensionBuilder("all_urls").AddPermission("<all_urls>").Build();
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_1);
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_2);
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_3);
+
+  scoped_refptr<ExtensionFunction> function = base::MakeRefCounted<
+      api::DeveloperPrivateGetUserAndExtensionSitesByEtldFunction>();
+  EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError();
+  const base::Value::List* results = function->GetResultList();
+  ASSERT_EQ(1u, results->size());
+
+  // `extension_2` should not be counted for https://*.google.ca/* as it cannot
+  // run on .ca sites.
+  EXPECT_THAT((*results)[0], base::test::IsJson(R"([{
+    "etldPlusOne": "example.com",
+    "numExtensions": 3,
+    "sites": [{
+      "numExtensions": 3,
+      "site": "http://www.example.com/*",
+    }]
+  }, {
+    "etldPlusOne": "google.ca",
+    "numExtensions": 2,
+    "sites": [{
+      "numExtensions": 0,
+      "site": "http://images.google.ca",
+      "siteList": "PERMITTED",
+    }, {
+      "numExtensions": 2,
+      "site": "https://*.google.ca/*",
+    }]
+  }])"));
+}
+
+TEST_F(DeveloperPrivateApiUnitTest,
+       DeveloperPrivateGetUserAndExtensionSitesByEtld_RuntimeGrantedHosts) {
+  scoped_refptr<const Extension> extension_1 =
+      ExtensionBuilder("runtime_hosts").AddPermission("<all_urls>").Build();
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_1);
+
+  auto get_user_and_extension_sites = [this](const std::string& expected_json) {
+    scoped_refptr<ExtensionFunction> function = base::MakeRefCounted<
+        api::DeveloperPrivateGetUserAndExtensionSitesByEtldFunction>();
+    EXPECT_TRUE(RunFunction(function, base::ListValue()))
+        << function->GetError();
+    const base::Value::List* results = function->GetResultList();
+    ASSERT_EQ(1u, results->size());
+    EXPECT_THAT((*results)[0], base::test::IsJson(expected_json));
+  };
+
+  get_user_and_extension_sites(R"([])");
+
+  ScriptingPermissionsModifier modifier(profile(), extension_1.get());
+  EXPECT_FALSE(modifier.HasWithheldHostPermissions());
+  modifier.SetWithholdHostPermissions(true);
+
+  get_user_and_extension_sites(R"([])");
+
+  const std::string kExampleCom = "https://example.com/*";
+  RunAddHostPermission(profile(), *extension_1, kExampleCom,
+                       /*should_succeed=*/true, nullptr);
+
+  get_user_and_extension_sites(R"([{
+    "etldPlusOne": "example.com",
+    "numExtensions": 1,
+    "sites": [{
+      "numExtensions": 1,
+      "site": "https://example.com/*",
+    }]
+  }])");
+
+  scoped_refptr<const Extension> extension_2 =
+      ExtensionBuilder("test").AddPermission(kExampleCom).Build();
+  AddExtensionAndGrantPermissions(profile(), service(), *extension_2);
+
+  get_user_and_extension_sites(R"([{
+    "etldPlusOne": "example.com",
+    "numExtensions": 2,
+    "sites": [{
+      "numExtensions": 2,
+      "site": "https://example.com/*",
+    }]
+  }])");
+
+  RunUpdateHostAccess(*extension_1, "ON_ALL_SITES");
+  get_user_and_extension_sites(R"([{
+    "etldPlusOne": "example.com",
+    "numExtensions": 2,
+    "sites": [{
+      "numExtensions": 2,
+      "site": "https://example.com/*",
+    }]
+  }])");
 }
 
 class DeveloperPrivateApiAllowlistUnitTest
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
index ec7ce69..3f636ad 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -360,45 +360,10 @@
                                             extension.GetType()));
   permissions->simple_permissions = get_permission_messages(api_messages);
 
-  auto runtime_host_permissions =
-      std::make_unique<developer::RuntimeHostPermissions>();
-
-  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context);
-  // "Effective" granted permissions are stored in different prefs, based on
-  // whether host permissions are withheld.
-  // TODO(devlin): Create a common helper method to retrieve granted prefs based
-  // on whether host permissions are withheld?
-  std::unique_ptr<const PermissionSet> granted_permissions;
-  // Add the host access data, including the mode and any runtime-granted
-  // hosts.
-  if (!permissions_modifier.HasWithheldHostPermissions()) {
-    granted_permissions =
-        extension_prefs->GetGrantedPermissions(extension.id());
-    runtime_host_permissions->host_access = developer::HOST_ACCESS_ON_ALL_SITES;
-  } else {
-    granted_permissions =
-        extension_prefs->GetRuntimeGrantedPermissions(extension.id());
-    if (granted_permissions->effective_hosts().is_empty()) {
-      runtime_host_permissions->host_access = developer::HOST_ACCESS_ON_CLICK;
-    } else if (granted_permissions->ShouldWarnAllHosts(false)) {
-      runtime_host_permissions->host_access =
-          developer::HOST_ACCESS_ON_ALL_SITES;
-    } else {
-      runtime_host_permissions->host_access =
-          developer::HOST_ACCESS_ON_SPECIFIC_SITES;
-    }
-  }
-
-  runtime_host_permissions->hosts = GetSpecificSiteControls(
-      *granted_permissions,
-      extension.permissions_data()->withheld_permissions());
-  constexpr bool kIncludeApiPermissions = false;
-  runtime_host_permissions->has_all_hosts =
-      extension.permissions_data()->withheld_permissions().ShouldWarnAllHosts(
-          kIncludeApiPermissions) ||
-      granted_permissions->ShouldWarnAllHosts(kIncludeApiPermissions);
-
-  permissions->runtime_host_permissions = std::move(runtime_host_permissions);
+  permissions->runtime_host_permissions =
+      std::make_unique<developer::RuntimeHostPermissions>(
+          ExtensionInfoGenerator::CreateRuntimeHostPermissionsInfo(
+              browser_context, extension));
 }
 
 }  // namespace
@@ -489,6 +454,51 @@
   }
 }
 
+developer::RuntimeHostPermissions
+ExtensionInfoGenerator::CreateRuntimeHostPermissionsInfo(
+    content::BrowserContext* browser_context,
+    const Extension& extension) {
+  ScriptingPermissionsModifier permissions_modifier(
+      browser_context, base::WrapRefCounted(&extension));
+  developer::RuntimeHostPermissions runtime_host_permissions;
+
+  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context);
+  // "Effective" granted permissions are stored in different prefs, based on
+  // whether host permissions are withheld.
+  // TODO(devlin): Create a common helper method to retrieve granted prefs based
+  // on whether host permissions are withheld?
+  std::unique_ptr<const PermissionSet> granted_permissions;
+  // Add the host access data, including the mode and any runtime-granted
+  // hosts.
+  if (!permissions_modifier.HasWithheldHostPermissions()) {
+    granted_permissions =
+        extension_prefs->GetGrantedPermissions(extension.id());
+    runtime_host_permissions.host_access = developer::HOST_ACCESS_ON_ALL_SITES;
+  } else {
+    granted_permissions =
+        extension_prefs->GetRuntimeGrantedPermissions(extension.id());
+    if (granted_permissions->effective_hosts().is_empty()) {
+      runtime_host_permissions.host_access = developer::HOST_ACCESS_ON_CLICK;
+    } else if (granted_permissions->ShouldWarnAllHosts(false)) {
+      runtime_host_permissions.host_access =
+          developer::HOST_ACCESS_ON_ALL_SITES;
+    } else {
+      runtime_host_permissions.host_access =
+          developer::HOST_ACCESS_ON_SPECIFIC_SITES;
+    }
+  }
+
+  runtime_host_permissions.hosts = GetSpecificSiteControls(
+      *granted_permissions,
+      extension.permissions_data()->withheld_permissions());
+  constexpr bool kIncludeApiPermissions = false;
+  runtime_host_permissions.has_all_hosts =
+      extension.permissions_data()->withheld_permissions().ShouldWarnAllHosts(
+          kIncludeApiPermissions) ||
+      granted_permissions->ShouldWarnAllHosts(kIncludeApiPermissions);
+  return runtime_host_permissions;
+}
+
 void ExtensionInfoGenerator::CreateExtensionInfoHelper(
     const Extension& extension,
     developer::ExtensionState state) {
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.h b/chrome/browser/extensions/api/developer_private/extension_info_generator.h
index 2bb652f..a906c676 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.h
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.h
@@ -64,6 +64,12 @@
                             bool include_terminated,
                             ExtensionInfosCallback callback);
 
+  // Creates and synchronously returns a RuntimeHostPermissions object with the
+  // given extension's host permissions.
+  static api::developer_private::RuntimeHostPermissions
+  CreateRuntimeHostPermissionsInfo(content::BrowserContext* browser_context,
+                                   const Extension& extension);
+
  private:
   // Creates an ExtensionInfo for the given |extension| and |state|, and
   // asynchronously adds it to the |list|.
diff --git a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
index 21d8be99..7220ded8 100644
--- a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
+++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
@@ -19,7 +19,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/common/extension.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index c5c79b27..f350a5b 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -682,18 +682,7 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
-class ContentScriptApiTestWithTrustedDOMTypesEnabled
-    : public ContentScriptApiTest {
- public:
-  ContentScriptApiTestWithTrustedDOMTypesEnabled() {
-    feature_list_.InitAndEnableFeature(features::kTrustedDOMTypes);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(ContentScriptApiTestWithTrustedDOMTypesEnabled,
+IN_PROC_BROWSER_TEST_F(ContentScriptApiTest,
                        ContentScriptBypassPageTrustedTypes) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   extensions::ResultCatcher catcher;
diff --git a/chrome/browser/extensions/extension_action_runner.cc b/chrome/browser/extensions/extension_action_runner.cc
index 43b2913..74d39f61 100644
--- a/chrome/browser/extensions/extension_action_runner.cc
+++ b/chrome/browser/extensions/extension_action_runner.cc
@@ -27,8 +27,10 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "components/crx_file/id_util.h"
 #include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -38,8 +40,10 @@
 #include "extensions/browser/api/declarative_net_request/rules_monitor_service.h"
 #include "extensions/browser/extension_action.h"
 #include "extensions/browser/extension_action_manager.h"
+#include "extensions/browser/permissions_manager.h"
 #include "extensions/common/api/extension_action/action_info.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/extension_id.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/permissions/permission_set.h"
@@ -134,10 +138,10 @@
 
 void ExtensionActionRunner::GrantTabPermissions(
     const std::vector<const Extension*>& extensions) {
-  bool refresh_required = std::any_of(
-      extensions.begin(), extensions.end(), [this](const Extension* extension) {
-        return GetBlockedActions(extension->id()) & kRefreshRequiredActionsMask;
-      });
+  bool refresh_required = std::any_of(extensions.begin(), extensions.end(),
+                                      [this](const Extension* extension) {
+                                        return PageNeedsRefreshToRun(extension);
+                                      });
 
   if (!refresh_required) {
     // Immediately grant permissions to every extension.
@@ -152,14 +156,12 @@
   // Every extension that wants tab permission is currently blocked and must
   // have "on click" access.
   const GURL& url = web_contents()->GetLastCommittedURL();
-  constexpr SitePermissionsHelper::SiteAccess kExpectedSiteAccess =
-      SitePermissionsHelper::SiteAccess::kOnClick;
   auto permissions =
       SitePermissionsHelper(Profile::FromBrowserContext(browser_context_));
   DCHECK(std::all_of(extensions.begin(), extensions.end(),
                      [url, &permissions](const Extension* extension) {
                        return permissions.GetSiteAccess(*extension, url) ==
-                              kExpectedSiteAccess;
+                              SitePermissionsHelper::SiteAccess::kOnClick;
                      }));
 
   // Running the action a single time does not update permissions.
@@ -167,9 +169,9 @@
   std::vector<ExtensionId> extension_ids = GetExtensionIds(extensions);
   ShowReloadPageBubble(
       extension_ids, update_permissions,
-      base::BindOnce(&ExtensionActionRunner::OnReloadPageBubbleAccepted,
-                     weak_factory_.GetWeakPtr(), extension_ids, url,
-                     kExpectedSiteAccess, kExpectedSiteAccess));
+      base::BindOnce(&ExtensionActionRunner::
+                         OnReloadPageBubbleAcceptedForGrantTabPermissions,
+                     weak_factory_.GetWeakPtr(), extension_ids, url));
 }
 
 void ExtensionActionRunner::HandlePageAccessModified(
@@ -188,15 +190,16 @@
   // reload page bubble after the user blocks the extension re enforces the user
   // confidence on blocking the extension. Also, this scenario should not be
   // that common and therefore hopefully is not too noisy.
-  if (revoking_permissions || blocked_actions & kRefreshRequiredActionsMask) {
+  if (revoking_permissions || PageNeedsRefreshToRun(extension)) {
     constexpr bool update_permissions = true;
     std::vector<ExtensionId> extension_ids{extension->id()};
     ShowReloadPageBubble(
         extension_ids, update_permissions,
-        base::BindOnce(&ExtensionActionRunner::OnReloadPageBubbleAccepted,
-                       weak_factory_.GetWeakPtr(), extension_ids,
-                       web_contents()->GetLastCommittedURL(), current_access,
-                       new_access));
+        base::BindOnce(
+            &ExtensionActionRunner::
+                OnReloadPageBubbleAcceptedForExtensionSiteAccessChange,
+            weak_factory_.GetWeakPtr(), extension->id(),
+            web_contents()->GetLastCommittedURL(), current_access, new_access));
     return;
   }
 
@@ -209,10 +212,106 @@
     const base::flat_set<ToolbarActionsModel::ActionId>& action_ids,
     const url::Origin& origin,
     PermissionsManager::UserSiteSetting new_site_settings) {
-  // TODO(crbug.com/1319555): Show reload page bubble when changing user site
-  // settings needs a page reload.
-  extensions::PermissionsManager::Get(browser_context_)
-      ->UpdateUserSiteSetting(origin, new_site_settings);
+  auto* registry = ExtensionRegistry::Get(browser_context_);
+  std::vector<const Extension*> extensions;
+  extensions.reserve(action_ids.size());
+  for (const auto& action_id : action_ids) {
+    const Extension* extension =
+        registry->enabled_extensions().GetByID(action_id);
+    DCHECK(extension);
+    extensions.push_back(extension);
+  }
+
+  auto* permissions_manager =
+      extensions::PermissionsManager::Get(browser_context_);
+  auto current_site_settings = permissions_manager->GetUserSiteSetting(origin);
+  DCHECK_NE(new_site_settings, current_site_settings);
+
+  bool refresh_required = false;
+  if (current_site_settings ==
+      PermissionsManager::UserSiteSetting::kBlockAllExtensions) {
+    // When the user blocks all the extensions, each extension's page access is
+    // set as "denied". Blocked actions in the ExtensionActionRunner are
+    // computed by checking if a page access is "withheld". Therefore, we
+    // always need a refresh since we don't know if there are any extensions
+    // that would have wanted to run if the page had not been restricted by the
+    // user.
+    refresh_required = true;
+  } else {
+    SitePermissionsHelper permissions_helper(
+        Profile::FromBrowserContext(browser_context_));
+
+    switch (new_site_settings) {
+      case PermissionsManager::UserSiteSetting::kGrantAllExtensions: {
+        DCHECK_EQ(current_site_settings,
+                  PermissionsManager::UserSiteSetting::kCustomizeByExtension);
+        // Refresh the page if any extension that wants site access and needs a
+        // page refresh to run will gain site access.
+        refresh_required = std::any_of(
+            extensions.begin(), extensions.end(),
+            [&permissions_helper, this](const Extension* extension) {
+              return permissions_helper.GetSiteInteraction(*extension,
+                                                           web_contents()) ==
+                         SitePermissionsHelper::SiteInteraction::kWithheld &&
+                     PageNeedsRefreshToRun(extension);
+            });
+        break;
+      }
+      case PermissionsManager::UserSiteSetting::kBlockAllExtensions: {
+        // Refresh the page if any extension that had site access will lose it.
+        refresh_required = std::any_of(
+            extensions.begin(), extensions.end(),
+            [&permissions_helper, this](const Extension* extension) {
+              return permissions_helper.GetSiteInteraction(*extension,
+                                                           web_contents()) ==
+                     SitePermissionsHelper::SiteInteraction::kGranted;
+            });
+        break;
+      }
+      case PermissionsManager::UserSiteSetting::kCustomizeByExtension: {
+        DCHECK_EQ(current_site_settings,
+                  PermissionsManager::UserSiteSetting::kGrantAllExtensions);
+        // Refresh the page if any extension that had site access will lose it.
+        // Since every extension currently has access via user site
+        // settings, only extensions with "on click" site access will lose
+        // access. This is because `SitePermissionsHelper::SiteAccess` does not
+        // take into account user site settings, which means granting all
+        // extensions access doesn't change the extension's specific site
+        // access.
+        // TODO(emiliapaz): `SitePermissionsHelper::SiteAccess` should take into
+        // account user site settings. This is not a problem now, because
+        // `SitePermissionsHelper::GetSiteAccess` is called only after checking
+        // a) user site setting is "customize by extension" or b) selecting site
+        // access is possible (e.g. is not a policy restricted site, extension
+        // requests host permissions). However, this can be easily wrongly
+        // called in the future. For this change, a major restructure in
+        // permissions struct and enums will be needed.
+        refresh_required = std::any_of(
+            extensions.begin(), extensions.end(),
+            [&permissions_helper, this](const Extension* extension) {
+              return permissions_helper.GetSiteAccess(
+                         *extension, web_contents()->GetLastCommittedURL()) ==
+                     SitePermissionsHelper::SiteAccess::kOnClick;
+            });
+        break;
+      }
+    }
+  }
+
+  if (refresh_required) {
+    std::vector<extensions::ExtensionId> extension_ids(action_ids.begin(),
+                                                       action_ids.end());
+    ShowReloadPageBubble(
+        extension_ids, true,
+        base::BindOnce(&ExtensionActionRunner::
+                           OnReloadPageBubbleAcceptedForUserSiteSettingsChange,
+                       weak_factory_.GetWeakPtr(), origin, new_site_settings));
+    return;
+  }
+
+  permissions_manager->UpdateUserSiteSetting(origin, new_site_settings);
+  // TODO(emiliapaz): Run blocked actions for extensions that have a blocked
+  // action but don't require a page refresh to run.
 }
 
 void ExtensionActionRunner::OnActiveTabPermissionGranted(
@@ -445,53 +544,70 @@
                        std::move(callback));
 }
 
-// TODO(emiliapaz): Changing user site settings should also trigger the reload
-// page. Once it is implemented, the callback needs to grant user site settings
-// access. Consider separating callback for each purpose (e.g -ForTabPermission,
-// -ForSiteAccessChange, - ForUserSiteSettingChange).
-void ExtensionActionRunner::OnReloadPageBubbleAccepted(
+void ExtensionActionRunner::OnReloadPageBubbleAcceptedForGrantTabPermissions(
     const std::vector<ExtensionId>& extension_ids,
-    const GURL& page_url,
-    SitePermissionsHelper::SiteAccess current_access,
-    SitePermissionsHelper::SiteAccess new_access) {
+    const GURL& page_url) {
   // If the web contents have navigated to a different origin, do nothing.
   if (!url::IsSameOriginWith(page_url, web_contents()->GetLastCommittedURL()))
     return;
 
   auto* registry = ExtensionRegistry::Get(browser_context_);
-  auto get_extension = [registry](ExtensionId extension_id) {
-    return registry->enabled_extensions().GetByID(extension_id);
-  };
-
-  if (current_access != new_access) {
-    // Only a single extension can update its site access since multiple
-    // extensions cannot change their site access at the same time.
-    DCHECK_EQ(int(extension_ids.size()), 1);
-    const Extension* extension = get_extension(extension_ids[0]);
-    // Extension could have been removed while the reload page bubble was open.
+  for (const auto& extension_id : extension_ids) {
+    const Extension* extension =
+        registry->enabled_extensions().GetByID(extension_id);
     if (!extension)
-      return;
+      continue;
 
-    UpdatePageAccessSettings(extension, current_access, new_access);
-  } else {
-    // Multiple extension and a single extension that doesn't change its site
-    // access are granted one time access.
-    for (const auto& extension_id : extension_ids) {
-      const Extension* extension = get_extension(extension_id);
-      if (!extension)
-        continue;
-
-      base::AutoReset<bool> ignore_active_tab(&ignore_active_tab_granted_,
-                                              true);
-      TabHelper::FromWebContents(web_contents())
-          ->active_tab_permission_granter()
-          ->GrantIfRequested(extension);
-    }
+    base::AutoReset<bool> ignore_active_tab(&ignore_active_tab_granted_, true);
+    TabHelper::FromWebContents(web_contents())
+        ->active_tab_permission_granter()
+        ->GrantIfRequested(extension);
   }
 
   web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
 }
 
+void ExtensionActionRunner::
+    OnReloadPageBubbleAcceptedForExtensionSiteAccessChange(
+        const ExtensionId& extension_id,
+        const GURL& page_url,
+        SitePermissionsHelper::SiteAccess current_access,
+        SitePermissionsHelper::SiteAccess new_access) {
+  DCHECK_NE(current_access, new_access);
+  // If the web contents have navigated to a different origin, do nothing.
+  if (!url::IsSameOriginWith(page_url, web_contents()->GetLastCommittedURL()))
+    return;
+
+  // Extension could have been removed while the reload page bubble was open.
+  const Extension* extension = ExtensionRegistry::Get(browser_context_)
+                                   ->enabled_extensions()
+                                   .GetByID(extension_id);
+  if (!extension)
+    return;
+
+  UpdatePageAccessSettings(extension, current_access, new_access);
+
+  web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
+}
+
+void ExtensionActionRunner::OnReloadPageBubbleAcceptedForUserSiteSettingsChange(
+    const url::Origin& origin,
+    extensions::PermissionsManager::UserSiteSetting site_settings) {
+  // If the web contents have navigated to a different origin, do nothing.
+  if (origin != web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin())
+    return;
+
+  extensions::PermissionsManager::Get(browser_context_)
+      ->UpdateUserSiteSetting(origin, site_settings);
+
+  // TODO(emiliapaz): Updating site settings is an asynchronous process. Reload
+  // page could happen before the process is complete and the renderers may not
+  // be aware of the new permission state. Reload only after site settings
+  // finished updating. Also, see if we could have the same problem when
+  // reloading the page after site access change.
+  web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
+}
+
 void ExtensionActionRunner::UpdatePageAccessSettings(
     const Extension* extension,
     SitePermissionsHelper::SiteAccess current_access,
@@ -547,6 +663,10 @@
   NotifyChange(extension);
 }
 
+bool ExtensionActionRunner::PageNeedsRefreshToRun(const Extension* extension) {
+  return GetBlockedActions(extension->id()) & kRefreshRequiredActionsMask;
+}
+
 void ExtensionActionRunner::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   declarative_net_request::RulesMonitorService* rules_monitor_service =
diff --git a/chrome/browser/extensions/extension_action_runner.h b/chrome/browser/extensions/extension_action_runner.h
index a80cec4..bb9495d 100644
--- a/chrome/browser/extensions/extension_action_runner.h
+++ b/chrome/browser/extensions/extension_action_runner.h
@@ -195,13 +195,26 @@
                             bool update_permissions,
                             base::OnceClosure callback);
 
-  // Called when the reload page bubble is accepted.
-  void OnReloadPageBubbleAccepted(
+  // Called when the reload page bubble is accepted. Grants one time site access
+  // to `page_url` for each extension in `extension_ids`.
+  void OnReloadPageBubbleAcceptedForGrantTabPermissions(
       const std::vector<ExtensionId>& extension_ids,
+      const GURL& page_url);
+
+  // Called when the reload page bubble is accepted. Updates site access of
+  // `extension_id` from `current_access` to `new_access` for `page_url`.
+  void OnReloadPageBubbleAcceptedForExtensionSiteAccessChange(
+      const ExtensionId& extension_id,
       const GURL& page_url,
       SitePermissionsHelper::SiteAccess current_access,
       SitePermissionsHelper::SiteAccess new_access);
 
+  // Called when the reload page bubble is accepted. Updates user site setting
+  // of `origin` to `site_settings`.
+  void OnReloadPageBubbleAcceptedForUserSiteSettingsChange(
+      const url::Origin& origin,
+      extensions::PermissionsManager::UserSiteSetting site_settings);
+
   // Handles permission changes necessary for page access modification of the
   // |extension|.
   void UpdatePageAccessSettings(
@@ -213,6 +226,10 @@
   // requirement, this will grant activeTab permission to the extension.
   void RunBlockedActions(const Extension* extension);
 
+  // Returns true if the given `extension` needs a page refresh to run a blocked
+  // action.
+  bool PageNeedsRefreshToRun(const Extension* extension);
+
   // content::WebContentsObserver implementation.
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
diff --git a/chrome/browser/extensions/extension_action_runner_browsertest.cc b/chrome/browser/extensions/extension_action_runner_browsertest.cc
index 6d89d9b..5a9fdcf 100644
--- a/chrome/browser/extensions/extension_action_runner_browsertest.cc
+++ b/chrome/browser/extensions/extension_action_runner_browsertest.cc
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/extensions/site_permissions_helper.h"
@@ -27,8 +28,10 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/fenced_frame_test_util.h"
 #include "extensions/browser/extension_action.h"
+#include "extensions/browser/permissions_manager.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/permissions_manager_waiter.h"
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -38,6 +41,7 @@
 namespace {
 
 using SiteAccess = SitePermissionsHelper::SiteAccess;
+using UserSiteSetting = PermissionsManager::UserSiteSetting;
 
 const char kAllHostsScheme[] = "*://*/*";
 const char kExplicitHostsScheme[] = "http://127.0.0.1/*";
@@ -74,20 +78,18 @@
   return content::ExecuteScript(web_contents, "1 == 1;");
 }
 
-// For use with blocked actions browsertests that put the result in
-// window.localStorage. Returns whether the extension injected a script.
+// Returns whether the extension injected a script by checking the document
+// title.
 bool DidInjectScript(content::WebContents* web_contents) {
-  std::string out;
-  bool exec = content::ExecuteScriptAndExtractString(
-      web_contents,
-      "var res = window.localStorage.getItem('extResult') || 'undefined';"
-      "window.localStorage.removeItem('extResult');"
-      "window.domAutomationController.send(res);",
-      &out);
-  EXPECT_TRUE(exec);
-  EXPECT_TRUE(out == "success" || out == "undefined")
-      << "Unexpected script value: " << out;
-  return out == "success";
+  const std::u16string& title = web_contents->GetTitle();
+  if (title == u"success")
+    return true;
+  // The original page title is "OK"; this indicates the script didn't
+  // inject.
+  if (title == u"OK")
+    return false;
+  ADD_FAILURE() << "Unexpected page title found: " << title;
+  return false;
 }
 
 }  // namespace
@@ -681,4 +683,240 @@
   EXPECT_EQ(0U, action_runner->pending_scripts_.size());
 }
 
+class ExtensionActionRunnerWithUserHostControlsBrowserTest
+    : public ExtensionActionRunnerBrowserTest {
+ public:
+  ExtensionActionRunnerWithUserHostControlsBrowserTest() {
+    feature_list_.InitAndEnableFeature(
+        extensions_features::kExtensionsMenuAccessControl);
+  }
+  ~ExtensionActionRunnerWithUserHostControlsBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    ExtensionActionRunnerBrowserTest::SetUpOnMainThread();
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    permissions_manager_ = PermissionsManager::Get(browser()->profile());
+    ASSERT_TRUE(permissions_manager_);
+  }
+
+  void HandleUserSiteSettingModified(const ExtensionId& extension_id,
+                                     const url::Origin url_origin,
+                                     UserSiteSetting user_site_setting,
+                                     bool accept_bubble) {
+    ExtensionActionRunner* runner =
+        ExtensionActionRunner::GetForWebContents(web_contents());
+    ASSERT_TRUE(runner);
+    runner->accept_bubble_for_testing(accept_bubble);
+
+    if (accept_bubble) {
+      PermissionsManagerWaiter waiter(
+          extensions::PermissionsManager::Get(profile()));
+      runner->HandleUserSiteSettingModified({extension_id}, url_origin,
+                                            user_site_setting);
+      waiter.WaitForUserPermissionsSettingsChange();
+
+      // Wait for page reload.
+      EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+    } else {
+      runner->HandleUserSiteSettingModified({extension_id}, url_origin,
+                                            user_site_setting);
+      // Make sure we tried to modified user site settings by verifying waiting
+      // for the task to be posted.
+      base::RunLoop().RunUntilIdle();
+      EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+    }
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+  PermissionsManager* permissions_manager() { return permissions_manager_; }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  PermissionsManager* permissions_manager_;
+};
+
+// Tests changing user site settings when the extension has site access (which
+// is either 'on all sites' or 'on site'). Note that we don't check if extension
+// `WantsToRun` because on user-restricted sites, actions are blocked rather
+// than withheld.
+IN_PROC_BROWSER_TEST_F(ExtensionActionRunnerWithUserHostControlsBrowserTest,
+                       HandleUserSiteSettingModified_ExtensionHasAccess) {
+  // Load an extension that wants to run on every page at document start.
+  const Extension* extension = LoadExtension(
+      test_data_dir_.AppendASCII("blocked_actions/content_scripts"));
+  ASSERT_TRUE(extension);
+
+  // Navigate to a page where the extension can run.
+  const GURL url = embedded_test_server()->GetURL("/simple.html");
+  const url::Origin url_origin = url::Origin::Create(url);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  // By default, the page should have "customize by extension" user site
+  // setting and the extensions should have "on all sites" site access. The
+  // extension should have injected.
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_EQ(SitePermissionsHelper(profile()).GetSiteAccess(*extension, url),
+            SiteAccess::kOnAllSites);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "customize by extension (on site) -> "grant all extensions":
+  // maintains current site access and keeps the script injected. No refresh is
+  // required.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kGrantAllExtensions, false);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "block all extensions":
+  // not accepting the page reload bubble maintains the same user site
+  // setting and keeps the script injected.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, false);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "block all extensions":
+  // accepting the page reload bubble revokes site access, refreshes the page
+  // and does not inject the script. (Note: we will accept all the next reload
+  // page bubbles since we previously checked the behavior of not accepting the
+  // dialog).
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kBlockAllExtensions);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "block all extensions" -> "customize by extension (on site)":
+  // grants site access, refreshes the page and injects the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kCustomizeByExtension, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "customize by extension (on site)" -> "block all extensions":
+  // revokes site access, refreshes the page and does not inject
+  // the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kBlockAllExtensions);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "block all extensions" -> "grant all extensions":
+  // grants site access, refreshes the page and injects the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kGrantAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "customize by extension (on site)":
+  // maintains current site access and keeps the script injected. No refresh is
+  // required.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kCustomizeByExtension, false);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+}
+
+// Tests changing user site settings when the extension does not have site
+// access ('on click'). Note that we don't check if extension
+// `WantsToRun` because on user-restricted sites, actions are blocked rather
+// than withheld.
+IN_PROC_BROWSER_TEST_F(ExtensionActionRunnerWithUserHostControlsBrowserTest,
+                       HandleUserSiteSettingModified_ExtensionHasNoAccess) {
+  // Load an extension that wants to run on every page at document start.
+  const Extension* extension = LoadExtension(
+      test_data_dir_.AppendASCII("blocked_actions/content_scripts"));
+  ASSERT_TRUE(extension);
+
+  // Withheld extension's host permission, so extension has "on click" site
+  // access.
+  ScriptingPermissionsModifier(profile(), extension)
+      .SetWithholdHostPermissions(true);
+
+  // Navigate to a page where the extension wants to run.
+  const GURL url = embedded_test_server()->GetURL("/simple.html");
+  const url::Origin url_origin = url::Origin::Create(url);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  // The page should have "customize by extension" user site setting and "on
+  // click" site access. The extension should not have injected.
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_EQ(SitePermissionsHelper(profile()).GetSiteAccess(*extension, url),
+            SiteAccess::kOnClick);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "customize by extension (on click) -> "grant all extensions":
+  // grants site access, refreshes the page and injects the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kGrantAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "block all extensions":
+  // not accepting the page reload bubble maintains the same user site
+  // setting and keeps the script injected.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, false);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "block all extensions":
+  // accepting the page reload bubble revokes site access, refreshes the page
+  // and does not inject the script. (Note: we will accept all the next reload
+  // page bubbles since we previously checked the behavior of not accepting the
+  // dialog).
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kBlockAllExtensions);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "block all extensions" -> "customize by extension (on click)":
+  // maintains current site access, refreshes the page and still does not inject
+  // the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kCustomizeByExtension, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "customize by extension (on click)" -> "block all extensions":
+  // maintains current site access, refreshes the page and still does not inject
+  // the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kBlockAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kBlockAllExtensions);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+
+  // "block all extensions" -> "grant all extensions":
+  // grants site access, refreshes the page and injects the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kGrantAllExtensions, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kGrantAllExtensions);
+  EXPECT_TRUE(DidInjectScript(web_contents()));
+
+  // "grant all extensions" -> "customize by extension (on click)":
+  // revokes site access, refreshes tha page and does not inject the script.
+  HandleUserSiteSettingModified(extension->id(), url_origin,
+                                UserSiteSetting::kCustomizeByExtension, true);
+  EXPECT_EQ(permissions_manager()->GetUserSiteSetting(url_origin),
+            UserSiteSetting::kCustomizeByExtension);
+  EXPECT_FALSE(DidInjectScript(web_contents()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/feed/android/feed_stream.cc b/chrome/browser/feed/android/feed_stream.cc
index e71b2d9..ec135fbe 100644
--- a/chrome/browser/feed/android/feed_stream.cc
+++ b/chrome/browser/feed/android/feed_stream.cc
@@ -177,13 +177,15 @@
 void FeedStream::ReportOpenAction(JNIEnv* env,
                                   const JavaParamRef<jobject>& obj,
                                   const JavaParamRef<jobject>& j_url,
-                                  const JavaParamRef<jstring>& slice_id) {
+                                  const JavaParamRef<jstring>& slice_id,
+                                  int action_type) {
   if (!feed_stream_api_)
     return;
   std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
   feed_stream_api_->ReportOpenAction(
       url ? *url : GURL(), GetStreamType(),
-      base::android::ConvertJavaStringToUTF8(env, slice_id));
+      base::android::ConvertJavaStringToUTF8(env, slice_id),
+      static_cast<OpenActionType>(action_type));
 }
 
 void FeedStream::UpdateUserProfileOnLinkClick(
@@ -199,20 +201,6 @@
   feed_stream_api_->UpdateUserProfileOnLinkClick(*url, entities_mids_vector);
 }
 
-void FeedStream::ReportOpenInNewTabAction(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_url,
-    const JavaParamRef<jstring>& slice_id) {
-  if (!feed_stream_api_)
-    return;
-  std::unique_ptr<GURL> url = url::GURLAndroid::ToNativeGURL(env, j_url);
-
-  feed_stream_api_->ReportOpenInNewTabAction(
-      url ? *url : GURL(), GetStreamType(),
-      base::android::ConvertJavaStringToUTF8(env, slice_id));
-}
-
 void FeedStream::ReportSliceViewed(JNIEnv* env,
                                    const JavaParamRef<jobject>& obj,
                                    const JavaParamRef<jstring>& slice_id) {
diff --git a/chrome/browser/feed/android/feed_stream.h b/chrome/browser/feed/android/feed_stream.h
index 0250e98..28b057f 100644
--- a/chrome/browser/feed/android/feed_stream.h
+++ b/chrome/browser/feed/android/feed_stream.h
@@ -83,16 +83,12 @@
   void ReportOpenAction(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj,
                         const base::android::JavaParamRef<jobject>& j_url,
-                        const base::android::JavaParamRef<jstring>& slice_id);
+                        const base::android::JavaParamRef<jstring>& slice_id,
+                        int action_type);
   void UpdateUserProfileOnLinkClick(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& j_url,
       const base::android::JavaParamRef<jlongArray>& entity_mids);
-  void ReportOpenInNewTabAction(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_url,
-      const base::android::JavaParamRef<jstring>& slice_id);
   void ReportOpenInNewIncognitoTabAction(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedActionDelegate.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedActionDelegate.java
index fb973fb9..67a4653 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedActionDelegate.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedActionDelegate.java
@@ -21,11 +21,12 @@
      * Opens a url that was presented to the user as suggested content.
      * @param disposition A `org.chromium.ui.mojom.WindowOpenDisposition` value.
      * @param params What to load.
+     * @param inGroup Whether to open the url in a tab in group.
      * @param onPageLoaded Called when the page completes loading.
      * @param onVisitComplete Called when the user closes or navigates away from the page.
      */
-    void openSuggestionUrl(int disposition, LoadUrlParams params, Runnable onPageLoaded,
-            Callback<VisitResult> onVisitComplete);
+    void openSuggestionUrl(int disposition, LoadUrlParams params, boolean inGroup,
+            Runnable onPageLoaded, Callback<VisitResult> onVisitComplete);
 
     /**
      * Opens a page.
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
index 147309d..7cbbe5f 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -99,9 +99,10 @@
         public void navigateTab(String url, View actionSourceView) {
             assert ThreadUtils.runningOnUiThread();
             FeedStreamJni.get().reportOpenAction(mNativeFeedStream, FeedStream.this,
-                    mMakeGURL.apply(url), getSliceIdFromView(actionSourceView));
+                    mMakeGURL.apply(url), getSliceIdFromView(actionSourceView),
+                    OpenActionType.DEFAULT);
 
-            openSuggestionUrl(url, WindowOpenDisposition.CURRENT_TAB);
+            openSuggestionUrl(url, WindowOpenDisposition.CURRENT_TAB, /*inGroup=*/false);
 
             // Attempts to load more content if needed.
             maybeLoadMore();
@@ -110,10 +111,11 @@
         @Override
         public void navigateNewTab(String url, View actionSourceView) {
             assert ThreadUtils.runningOnUiThread();
-            FeedStreamJni.get().reportOpenInNewTabAction(mNativeFeedStream, FeedStream.this,
-                    mMakeGURL.apply(url), getSliceIdFromView(actionSourceView));
+            FeedStreamJni.get().reportOpenAction(mNativeFeedStream, FeedStream.this,
+                    mMakeGURL.apply(url), getSliceIdFromView(actionSourceView),
+                    OpenActionType.NEW_TAB);
 
-            openSuggestionUrl(url, WindowOpenDisposition.NEW_BACKGROUND_TAB);
+            openSuggestionUrl(url, WindowOpenDisposition.NEW_BACKGROUND_TAB, /*inGroup=*/false);
 
             // Attempts to load more content if needed.
             maybeLoadMore();
@@ -125,7 +127,7 @@
             FeedStreamJni.get().reportOtherUserAction(mNativeFeedStream, FeedStream.this,
                     FeedUserActionType.TAPPED_OPEN_IN_NEW_INCOGNITO_TAB);
 
-            openSuggestionUrl(url, WindowOpenDisposition.OFF_THE_RECORD);
+            openSuggestionUrl(url, WindowOpenDisposition.OFF_THE_RECORD, /*inGroup=*/false);
 
             // Attempts to load more content if needed.
             maybeLoadMore();
@@ -253,7 +255,7 @@
             }
         }
 
-        private void openSuggestionUrl(String url, int disposition) {
+        private void openSuggestionUrl(String url, int disposition, boolean inGroup) {
             boolean inNewTab = (disposition == WindowOpenDisposition.NEW_BACKGROUND_TAB
                     || disposition == WindowOpenDisposition.OFF_THE_RECORD);
 
@@ -268,7 +270,7 @@
             // triggers unbind, which can break event handling.
             PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
                 mActionDelegate.openSuggestionUrl(disposition,
-                        new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK),
+                        new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK), inGroup,
                         ()
                                 -> FeedStreamJni.get().reportPageLoaded(
                                         mNativeFeedStream, FeedStream.this, inNewTab),
@@ -276,6 +278,20 @@
                         -> FeedServiceBridge.reportOpenVisitComplete(visitResult.visitTimeMs));
             });
         }
+
+        @Override
+        public void navigateNewTabInGroup(String url, View actionSourceView) {
+            assert ThreadUtils.runningOnUiThread();
+            FeedStreamJni.get().reportOpenAction(mNativeFeedStream, FeedStream.this,
+                    mMakeGURL.apply(url), getSliceIdFromView(actionSourceView),
+                    OpenActionType.NEW_TAB_IN_GROUP);
+
+            openSuggestionUrl(url, WindowOpenDisposition.NEW_BACKGROUND_TAB,
+                    /*inGroup=*/true);
+
+            // Attempts to load more content if needed.
+            maybeLoadMore();
+        }
     }
 
     /**
@@ -1275,9 +1291,8 @@
         void reportFeedViewed(long nativeFeedStream, FeedStream caller);
         void reportSliceViewed(long nativeFeedStream, FeedStream caller, String sliceId);
         void reportPageLoaded(long nativeFeedStream, FeedStream caller, boolean inNewTab);
-        void reportOpenAction(long nativeFeedStream, FeedStream caller, GURL url, String sliceId);
-        void reportOpenInNewTabAction(
-                long nativeFeedStream, FeedStream caller, GURL url, String sliceId);
+        void reportOpenAction(long nativeFeedStream, FeedStream caller, GURL url, String sliceId,
+                @OpenActionType int openActionType);
         void reportOtherUserAction(
                 long nativeFeedStream, FeedStream caller, @FeedUserActionType int userAction);
         void reportStreamScrolled(long nativeFeedStream, FeedStream caller, int distanceDp);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
index 0cb30d4..1462661 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -466,7 +466,7 @@
         handler.navigateTab(TEST_URL, null);
         verify(mActionDelegate)
                 .openSuggestionUrl(eq(org.chromium.ui.mojom.WindowOpenDisposition.CURRENT_TAB),
-                        any(), any(), any());
+                        any(), eq(false), any(), any());
     }
 
     @Test
@@ -521,7 +521,22 @@
         verify(mActionDelegate)
                 .openSuggestionUrl(
                         eq(org.chromium.ui.mojom.WindowOpenDisposition.NEW_BACKGROUND_TAB), any(),
-                        any(), any());
+                        eq(false), any(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testNavigateNewTabInGroup() {
+        bindToView();
+        FeedStream.FeedSurfaceActionsHandler handler =
+                (FeedStream.FeedSurfaceActionsHandler) mContentManager.getContextValues(0).get(
+                        SurfaceActionsHandler.KEY);
+
+        handler.navigateNewTabInGroup(TEST_URL, null);
+        verify(mActionDelegate)
+                .openSuggestionUrl(
+                        eq(org.chromium.ui.mojom.WindowOpenDisposition.NEW_BACKGROUND_TAB), any(),
+                        eq(true), any(), any());
     }
 
     @Test
@@ -534,7 +549,7 @@
         handler.navigateIncognitoTab(TEST_URL);
         verify(mActionDelegate)
                 .openSuggestionUrl(eq(org.chromium.ui.mojom.WindowOpenDisposition.OFF_THE_RECORD),
-                        any(), any(), any());
+                        any(), eq(false), any(), any());
     }
 
     @Test
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c97a75a..a5dd2dc0 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -103,7 +103,7 @@
   {
     "name": "allow-disable-touchpad-haptic-feedback",
     "owners": [ "gavinwill", "cros-peripherals@google.com" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 112
   },
    {
     "name": "allow-dsp-based-aec",
@@ -143,7 +143,7 @@
   {
     "name": "allow-touchpad-haptic-click-settings",
     "owners": [ "gavinwill", "cros-peripherals@google.com" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 112
   },
   {
     "name": "always-enable-hdcp",
@@ -1001,7 +1001,7 @@
   {
     "name": "context-menu-popup-style",
     "owners": [ "wenyufu", "clank-app-team@google.com" ],
-    "expiry_milestone": 104
+    "expiry_milestone": 108
   },
   {
     "name": "context-menu-search-and-shop-with-google-lens",
@@ -1226,7 +1226,7 @@
     "name": "deprecate-alt-click",
     "owners": [ "zentaro@google.com", "jimmyxgong@google.com",
                 "cros-peripherals@google.com"],
-    "expiry_milestone": 105
+    "expiry_milestone": 112
   },
   {
     "name": "deprecate-assistant-stylus-features",
@@ -4498,7 +4498,7 @@
   {
     "name": "new-shortcut-mapping",
     "owners": [ "zentaro@google.com", "jimmyxgong@google.com", "cros-peripherals@google.com"],
-    "expiry_milestone": 105
+    "expiry_milestone": 112
   },
   {
     "name": "new-window-app-menu",
@@ -4533,62 +4533,62 @@
   {
     "name": "ntp-chrome-cart-module",
     "owners": [ "wychen", "tiborg", "yuezhanggg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-drive-module",
     "owners": [ "mahmadi", "tiborg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-dummy-modules",
     "owners": [ "pauladedeji", "tiborg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-middle-slot-promo-dismissal",
     "owners": [ "pauladedeji", "tiborg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-modules-drag-and-drop",
-    "owners": [ "lydialam", "mahmadi", "tiborg" ],
-    "expiry_milestone": 105
+    "owners": [ "pauladedeji", "mahmadi", "tiborg" ],
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-modules-first-run-experience",
     "owners": [ "mahmadi", "pauladedeji", "tiborg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-modules-redesigned",
-    "owners": [ "doei", "mahmadi", "tiborg" ],
-    "expiry_milestone": 105
+    "owners": [ "mahmadi", "tiborg" ],
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-modules-redesigned-layout",
     "owners": [ "pauladedeji", "tiborg" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-photos-module",
-    "owners": [ "jerem", "tiborg" ],
-    "expiry_milestone": 105
+    "owners": [ "mplg", "tiborg" ],
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-photos-opt-in-art-work",
     "owners": [ "mplg", "tiborg"],
-    "expiry_milestone":105
+    "expiry_milestone":108
   },
   {
     "name": "ntp-photos-opt-in-title",
     "owners": [ "mplg", "tiborg"],
-    "expiry_milestone":105
+    "expiry_milestone":108
   },
   {
     "name": "ntp-photos-soft-opt-out",
     "owners": [ "mplg", "tiborg"],
-    "expiry_milestone":105
+    "expiry_milestone":108
   },
   {
     "name": "ntp-realbox-match-omnibox-theme",
@@ -4622,8 +4622,8 @@
   },
   {
     "name": "ntp-recipe-tasks-module",
-    "owners": [ "mahmadi", "tiborg" ],
-    "expiry_milestone": 105
+    "owners": [ "rtatum", "tiborg" ],
+    "expiry_milestone": 108
   },
   {
     "name": "ntp-safe-browsing-module",
@@ -4631,6 +4631,11 @@
     "expiry_milestone": 105
   },
   {
+    "name": "ntp-tiles-title-wrap-around",
+    "owners": [ "ender", "chrome-omnibox-team" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "ntp-view-hierarchy-repair",
     "owners": [ "adamta", "sczs" ],
     "expiry_milestone": 98
@@ -4892,7 +4897,7 @@
   {
     "name": "oobe-hid-detection-revamp",
     "owners": [ "gordonseto", "cros-connectivity@google.com"],
-    "expiry_milestone": 105
+    "expiry_milestone": 110
   },
   {
     "name": "optimization-guide-debug-logs",
@@ -5494,6 +5499,11 @@
     "expiry_milestone": 108
   },
   {
+    "name": "request-desktop-site-defaults",
+    "owners": [ "aishwaryarj", "twellington", "clank-app-team@google.com" ],
+    "expiry_milestone": 108
+  },
+  {
     "name": "request-desktop-site-exceptions",
     "owners": [ "shuyng@google.com", "twellington", "clank-app-team@google.com" ],
     "expiry_milestone": 108
@@ -6137,7 +6147,7 @@
   {
     "name": "touch-drag-and-context-menu",
     "owners": [ "wenyufu", "clank-app-team@google.com" ],
-    "expiry_milestone": 106
+    "expiry_milestone": 108
   },
   {
     "name": "touch-selection-strategy",
@@ -6372,7 +6382,7 @@
   {
     "name": "use-search-click-for-right-click",
     "owners": [ "zentaro", "jimmyxgong", "cros-peripherals@google.com" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 112
   },
   {
     "name": "use-sf-symbols",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5b35813..cc771662 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3566,6 +3566,12 @@
 const char kNewInstanceFromDraggedLinkDescription[] =
     "Enables creation of a new instance when a link is dragged out of Chrome.";
 
+const char kNewTabPageTilesTitleWrapAroundName[] =
+    "NTP Tiles Title wrap around";
+const char kNewTabPageTilesTitleWrapAroundDescription[] =
+    "Permits longer Tile titles to wrap around to the second line "
+    "to reduce ellipsizing and improve clarity.";
+
 const char kNewWindowAppMenuName[] = "Show a menu item 'New Window'";
 const char kNewWindowAppMenuDescription[] =
     "Show a new menu item 'New Window' on tablet-sized screen when Chrome "
@@ -3723,6 +3729,12 @@
     "Secondary options in `Site settings` to request the desktop version of "
     "websites based on external display or peripheral.";
 
+const char kRequestDesktopSiteDefaultsName[] =
+    "Default settings for request desktop site on Android.";
+const char kRequestDesktopSiteDefaultsDescription[] =
+    "Request the desktop version of websites by default based on device "
+    "conditions.";
+
 const char kRequestDesktopSiteExceptionsName[] =
     "Per-site setting to request desktop site on Android.";
 const char kRequestDesktopSiteExceptionsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 4c819d1..b0b3ad1 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2030,6 +2030,9 @@
 extern const char kNewInstanceFromDraggedLinkName[];
 extern const char kNewInstanceFromDraggedLinkDescription[];
 
+extern const char kNewTabPageTilesTitleWrapAroundName[];
+extern const char kNewTabPageTilesTitleWrapAroundDescription[];
+
 extern const char kNewWindowAppMenuName[];
 extern const char kNewWindowAppMenuDescription[];
 
@@ -2120,6 +2123,9 @@
 extern const char kRequestDesktopSiteAdditionsName[];
 extern const char kRequestDesktopSiteAdditionsDescription[];
 
+extern const char kRequestDesktopSiteDefaultsName[];
+extern const char kRequestDesktopSiteDefaultsDescription[];
+
 extern const char kRequestDesktopSiteExceptionsName[];
 extern const char kRequestDesktopSiteExceptionsDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 7ed7921..37278ed1 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -252,6 +252,7 @@
     &kLocationBarModelOptimizations,
     &kMostRecentTabOnBackgroundCloseTab,
     &kNewInstanceFromDraggedLink,
+    &kNewTabPageTilesTitleWrapAround,
     &kNewWindowAppMenu,
     &kNotificationPermissionVariant,
     &kPageAnnotationsService,
@@ -271,6 +272,7 @@
     &kRelatedSearchesInBar,
     &kRelatedSearchesSimplifiedUx,
     &kRelatedSearchesUi,
+    &kRequestDesktopSiteDefaults,
     &kSafeModeForCachedFlags,
     &kSearchEnginePromoExistingDevice,
     &kSearchEnginePromoExistingDeviceV2,
@@ -723,6 +725,9 @@
 const base::Feature kNewInstanceFromDraggedLink{
     "NewInstanceFromDraggedLink", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kNewTabPageTilesTitleWrapAround{
+    "NewTabPageTilesTitleWrapAround", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kNewWindowAppMenu{"NewWindowAppMenu",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -783,6 +788,9 @@
 const base::Feature kRelatedSearchesUi{"RelatedSearchesUi",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kRequestDesktopSiteDefaults{
+    "RequestDesktopSiteDefaults", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kSafeModeForCachedFlags{"SafeModeForCachedFlags",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 68e762eb..34ecbd6 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -112,6 +112,7 @@
 extern const base::Feature kLocationBarModelOptimizations;
 extern const base::Feature kMostRecentTabOnBackgroundCloseTab;
 extern const base::Feature kNewInstanceFromDraggedLink;
+extern const base::Feature kNewTabPageTilesTitleWrapAround;
 extern const base::Feature kNewWindowAppMenu;
 extern const base::Feature kNotificationPermissionVariant;
 extern const base::Feature kOmniboxModernizeVisualUpdate;
@@ -131,6 +132,7 @@
 extern const base::Feature kRelatedSearchesInBar;
 extern const base::Feature kRelatedSearchesSimplifiedUx;
 extern const base::Feature kRelatedSearchesUi;
+extern const base::Feature kRequestDesktopSiteDefaults;
 extern const base::Feature kSearchEnginePromoExistingDevice;
 extern const base::Feature kSearchEnginePromoExistingDeviceV2;
 extern const base::Feature kSearchEnginePromoNewDevice;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 3f91f08..df155c2 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -418,6 +418,8 @@
     public static final String MOST_RECENT_TAB_ON_BACKGROUND_CLOSE_TAB =
             "MostRecentTabOnBackgroundCloseTab";
     public static final String NEW_INSTANCE_FROM_DRAGGED_LINK = "NewInstanceFromDraggedLink";
+    public static final String NEW_TAB_PAGE_TILES_TITLE_WRAP_AROUND =
+            "NewTabPageTilesTitleWrapAround";
     public static final String NEW_WINDOW_APP_MENU = "NewWindowAppMenu";
     public static final String NOTIFICATION_PERMISSION_VARIANT = "NotificationPermissionVariant";
     public static final String OFFLINE_INDICATOR = "OfflineIndicator";
@@ -494,6 +496,7 @@
     public static final String RELATED_SEARCHES_IN_BAR = "RelatedSearchesInBar";
     public static final String RELATED_SEARCHES_SIMPLIFIED_UX = "RelatedSearchesSimplifiedUx";
     public static final String RELATED_SEARCHES_UI = "RelatedSearchesUi";
+    public static final String REQUEST_DESKTOP_SITE_DEFAULTS = "RequestDesktopSiteDefaults";
     public static final String REQUEST_DESKTOP_SITE_FOR_TABLETS = "RequestDesktopSiteForTablets";
     public static final String SAFE_BROWSING_DELAYED_WARNINGS = "SafeBrowsingDelayedWarnings";
     public static final String SAFE_MODE_FOR_CACHED_FLAGS = "SafeModeForCachedFlags";
diff --git a/chrome/browser/hid/hid_browsertest.cc b/chrome/browser/hid/hid_browsertest.cc
index 4c78b9fc..db12835 100644
--- a/chrome/browser/hid/hid_browsertest.cc
+++ b/chrome/browser/hid/hid_browsertest.cc
@@ -18,7 +18,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/common/content_client.h"
 #include "content/public/test/browser_test.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/hid/hid_chooser_context_unittest.cc b/chrome/browser/hid/hid_chooser_context_unittest.cc
index 134ac5d..ee3f1d7 100644
--- a/chrome/browser/hid/hid_chooser_context_unittest.cc
+++ b/chrome/browser/hid/hid_chooser_context_unittest.cc
@@ -28,8 +28,8 @@
 #include "components/permissions/test/object_permission_context_base_mock_permission_observer.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
 #include "services/device/public/cpp/hid/hid_blocklist.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc b/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
index 0a03f4e..5f39aed0 100644
--- a/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
+++ b/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
@@ -15,7 +15,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/browser_task_environment.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
index b7cd65a2..d75b2bd 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
@@ -830,6 +830,9 @@
 }
 
 void AccessCodeCastSinkService::Shutdown() {
+  if (base::FeatureList::IsEnabled(features::kAccessCodeCastRememberDevices)) {
+    network_monitor_->RemoveObserver(this);
+  }
   // There's no guarantee that MediaRouter is still in the
   // MediaRoutesObserver. |media_routes_observer_| accesses MediaRouter in its
   // dtor. Since MediaRouter and |this| are both KeyedServices, we must not
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index bcf0c211..3aae1da 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -1250,9 +1250,6 @@
       }
     }
 
-    UMA_HISTOGRAM_MEMORY_LARGE_MB(
-        "Memory.Experimental.Total2.PrivateMemoryFootprint",
-        private_footprint_total_kb / kKiB);
 #if BUILDFLAG(IS_MAC)
     // Resident set is not populated on Mac.
     DCHECK_EQ(resident_set_total_kb, 0U);
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index 2dfcfcb..c6d0197 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -240,9 +240,6 @@
   CheckMemoryMetric("Memory.Experimental.Gpu2.IOSurface", histogram_tester, 1,
                     ValueRestriction::ABOVE_ZERO);
 #endif
-
-  CheckMemoryMetric("Memory.Experimental.Total2.PrivateMemoryFootprint",
-                    histogram_tester, count, ValueRestriction::ABOVE_ZERO);
 }
 
 void CheckStableMemoryMetrics(const base::HistogramTester& histogram_tester,
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc
index 49fe1e2..9492136 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_browsertest.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/prefetch/prefetch_prefs.h"
+#include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.h"
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.h"
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.h"
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.h"
@@ -124,6 +125,7 @@
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/network_service_test.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "services/network/test/test_network_connection_tracker.h"
 #include "services/network/test/test_network_context.h"
 #include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -525,7 +527,26 @@
     ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
 
     // Ensure the service gets created before the tests start.
-    PrefetchProxyServiceFactory::GetForProfile(browser()->profile());
+    PrefetchProxyService* service =
+        PrefetchProxyServiceFactory::GetForProfile(browser()->profile());
+    // Override the system's connection type to avoid flakes due to changing
+    // connection type during testing.
+    network_connection_tracker_ =
+        network::TestNetworkConnectionTracker::CreateInstance();
+    network_connection_tracker_->SetConnectionType(
+        network::mojom::ConnectionType::CONNECTION_3G);
+    PrefetchProxyCanaryChecker* dns_checker =
+        service->origin_prober()->GetDNSCanaryCheckerForTesting();
+    if (dns_checker) {
+      dns_checker->SetNetworkConnectionTrackerForTesting(
+          network_connection_tracker_.get());
+    }
+    PrefetchProxyCanaryChecker* tls_checker =
+        service->origin_prober()->GetTLSCanaryCheckerForTesting();
+    if (tls_checker) {
+      tls_checker->SetNetworkConnectionTrackerForTesting(
+          network_connection_tracker_.get());
+    }
 
     host_resolver()->AddRule("a.test", "127.0.0.1");
     host_resolver()->AddRule("proxy.a.test", "127.0.0.1");
@@ -1032,6 +1053,9 @@
            base::UniquePtrComparator>
       tunnels_;
 
+  std::unique_ptr<network::TestNetworkConnectionTracker>
+      network_connection_tracker_;
+
   size_t origin_server_request_count_ = 0;
 };
 
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.cc
index 6afc3f6..e702044 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.cc
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.cc
@@ -417,3 +417,8 @@
     ProcessFailure(net_error);
   }
 }
+
+void PrefetchProxyCanaryChecker::SetNetworkConnectionTrackerForTesting(
+    network::NetworkConnectionTracker* tracker) {
+  network_connection_tracker_ = tracker;
+}
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.h b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.h
index 3a9c592..f1984dc 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.h
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_canary_checker.h
@@ -119,6 +119,11 @@
   // True if checks are being attempted, including retries.
   bool IsActive() const { return time_when_set_active_.has_value(); }
 
+  // Overrides the NetworkConnectionTracker used for generating cache keys. Used
+  // for testing.
+  void SetNetworkConnectionTrackerForTesting(
+      network::NetworkConnectionTracker* tracker);
+
  protected:
   // Exposes |tick_clock| and |clock| for testing.
   PrefetchProxyCanaryChecker(Profile* profile,
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
index eee0c4b3..714580f 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.cc
@@ -315,3 +315,13 @@
   // |prober| manages its own lifetime, using the mojo pipes.
   prober.release();
 }
+
+PrefetchProxyCanaryChecker*
+PrefetchProxyOriginProber::GetDNSCanaryCheckerForTesting() {
+  return dns_canary_check_.get();
+}
+
+PrefetchProxyCanaryChecker*
+PrefetchProxyOriginProber::GetTLSCanaryCheckerForTesting() {
+  return tls_canary_check_.get();
+}
diff --git a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.h b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.h
index 7c607d5..9633ded 100644
--- a/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.h
+++ b/chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_origin_prober.h
@@ -49,6 +49,14 @@
   // failure. Used for testing.
   bool IsTLSCanaryCheckCompleteForTesting() const;
 
+  // Returns the PrefetchProxyCanaryChecker object used for DNS canary checks.
+  // Used for testing.
+  PrefetchProxyCanaryChecker* GetDNSCanaryCheckerForTesting();
+
+  // Returns the PrefetchProxyCanaryChecker object used for TLS canary checks.
+  // Used for testing.
+  PrefetchProxyCanaryChecker* GetTLSCanaryCheckerForTesting();
+
   // Starts a probe to |url| and calls |callback| with an
   // |PrefetchProxyProbeResult| to indicate success.
   using OnProbeResultCallback =
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index e653b8a8..7be69cad 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -15,35 +15,36 @@
 
 assert(enable_pdf, "enable_pdf check failed")
 
-preprocess_folder = "preprocessed"
+preprocess_folder = "${target_gen_dir}/preprocessed"
 tsc_folder = "tsc"
 
 preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_folder = preprocess_folder
   defines = [ "enable_ink=$enable_ink" ]
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
-  ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  defines = [ "enable_ink=$enable_ink" ]
-  in_files = html_wrapper_files + css_wrapper_files
+  in_files = ts_files + html_files + shared_icons_html_files + css_files
 }
 
 html_to_wrapper("html_wrapper_files") {
+  deps = [ ":preprocess" ]
+  in_folder = preprocess_folder
+  out_folder = preprocess_folder
   in_files = html_files + shared_icons_html_files
+  minify = optimize_webui
+}
+
+css_to_wrapper("css_wrapper_files") {
+  deps = [ ":preprocess" ]
+  in_folder = preprocess_folder
+  out_folder = preprocess_folder
+  in_files = css_files
+  minify = optimize_webui
 }
 
 # Preprocess and build a manifest file for the Print Preview HTML/CSS files,
 # which aren't passed to ts_library().
 print_preview_html_css_manifest = "print_preview_html_css_manifest.json"
 preprocess_if_expr("preprocess_print_preview_html_css") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_folder = preprocess_folder
   out_manifest = "$target_gen_dir/$print_preview_html_css_manifest"
   in_files = [
     "index_pp.html",
@@ -134,12 +135,8 @@
   output_dir = "$root_gen_dir/chrome"
 }
 
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
-
 ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
+  root_dir = preprocess_folder
   out_dir = "$target_gen_dir/$tsc_folder"
   tsconfig_base = "tsconfig_base.json"
   composite = true
@@ -156,8 +153,9 @@
     "//ui/webui/resources:library",
   ]
   extra_deps = [
+    ":css_wrapper_files",
+    ":html_wrapper_files",
     ":preprocess",
-    ":preprocess_generated",
   ]
 }
 
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index a76d484f..f633ddca 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -61,6 +61,7 @@
     "demos/cr_dialog_demo.html",
     "demos/cr_input/cr_input_demo.html",
     "demos/cr_radio_demo.html",
+    "demos/cr_slider/cr_slider_demo.html",
     "demos/cr_toggle_demo.html",
     "webui_gallery.html",
   ]
diff --git a/chrome/browser/resources/webui_gallery/app.ts b/chrome/browser/resources/webui_gallery/app.ts
index 8e41f76..da52ee4b 100644
--- a/chrome/browser/resources/webui_gallery/app.ts
+++ b/chrome/browser/resources/webui_gallery/app.ts
@@ -70,6 +70,11 @@
               src: 'cr_radio_demo.html',
             },
             {
+              name: 'Sliders',
+              path: 'sliders',
+              src: 'cr_slider/cr_slider_demo.html',
+            },
+            {
               name: 'Toggles',
               path: 'toggles',
               src: 'cr_toggle_demo.html',
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html
new file mode 100644
index 0000000..e4ebf4a
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>cr-slider demo</title>
+    <link rel="stylesheet" href="../demo.css">
+  </head>
+  <body>
+    <cr-slider-demo></cr-slider-demo>
+    <script src="cr_slider_demo_component.js" type="module"></script>
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.html b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.html
new file mode 100644
index 0000000..92408cbd
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.html
@@ -0,0 +1,33 @@
+<link rel="stylesheet" href="../demo.css">
+<style>
+  cr-slider {
+    width: 200px;
+  }
+</style>
+
+<h1>cr-slider</h1>
+
+<h2>Indiscrete</h2>
+<div class="demos">
+  <cr-slider id="basicSlider" min="0" max="20" value="[[basicValue_]]"
+      on-cr-slider-value-changed="onBasicValueChanged_">
+  </cr-slider>
+  <div>Value of slider: [[basicValue_]]</div>
+</div>
+
+<h2>5 ticks, increments of 5</h2>
+<div class="demos">
+  <cr-slider id="tickedSlider" ticks="[[ticks_]]"
+      marker-count="[[getMarkerCount_(showMarkers_)]]"
+      value="[[tickedValue_]]"
+      on-cr-slider-value-changed="onTickedValueChanged_">
+  </cr-slider>
+  <div>Value of slider, the index of selected tick: [[tickedValue_]]</div>
+  <div>Value of selected tick: [[getTickValue_(tickedValue_)]]</div>
+  <cr-checkbox checked="{{showMarkers_}}">Show markers</cr-checkbox>
+</div>
+
+<h2>Disabled</h2>
+<div class="demos">
+  <cr-slider min="0" max="20" value="12" disabled></cr-slider>
+</div>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.ts b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.ts
new file mode 100644
index 0000000..18a08a9
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_slider/cr_slider_demo_component.ts
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
+import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrSliderElement, SliderTick} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+
+import {getTemplate} from './cr_slider_demo_component.html.js';
+
+interface CrSliderDemoComponent {
+  $: {
+    basicSlider: CrSliderElement,
+    tickedSlider: CrSliderElement,
+  };
+}
+
+function createTicks(
+    min: number, increment: number, steps: number): SliderTick[] {
+  const ticks: SliderTick[] = [];
+  for (let i = min; i <= steps; i++) {
+    const tickValue = min + (i * increment);
+    ticks.push({
+      label: `${tickValue}`,
+      value: tickValue,
+    });
+  }
+  return ticks;
+}
+
+class CrSliderDemoComponent extends PolymerElement {
+  static get is() {
+    return 'cr-slider-demo';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      basicValue_: Number,
+      disabledTicks_: Array,
+      showMarkers_: Boolean,
+      tickedValue_: Number,
+      ticks_: Array,
+    };
+  }
+
+  private basicValue_: number = 5;
+  private showMarkers_: boolean = false;
+  private tickedValue_: number = 0;
+  private ticks_: SliderTick[] = createTicks(0, 5, 5);
+
+  private getMarkerCount_(): number {
+    if (!this.showMarkers_) {
+      return 0;
+    }
+
+    return this.ticks_.length;
+  }
+
+  private getTickValue_(): number {
+    return this.ticks_[this.tickedValue_]!.value;
+  }
+
+  private onBasicValueChanged_() {
+    this.basicValue_ = this.$.basicSlider.value;
+  }
+
+  private onTickedValueChanged_() {
+    this.tickedValue_ = this.$.tickedSlider.value;
+  }
+}
+
+customElements.define(CrSliderDemoComponent.is, CrSliderDemoComponent);
diff --git a/chrome/browser/resources/webui_gallery/demos/demo.css b/chrome/browser/resources/webui_gallery/demos/demo.css
index cb24720..d94c930 100644
--- a/chrome/browser/resources/webui_gallery/demos/demo.css
+++ b/chrome/browser/resources/webui_gallery/demos/demo.css
@@ -18,6 +18,12 @@
   margin-block-end: 16px;
 }
 
+h2 {
+  font-size: 1.2em;
+  margin-block-end: 12px;
+  margin-block-start: 16px;
+}
+
 .demos {
   align-items: flex-start;
   display: flex;
diff --git a/chrome/browser/resources/webui_gallery/webui_gallery.gni b/chrome/browser/resources/webui_gallery/webui_gallery.gni
index a55a60e..1f78282 100644
--- a/chrome/browser/resources/webui_gallery/webui_gallery.gni
+++ b/chrome/browser/resources/webui_gallery/webui_gallery.gni
@@ -9,6 +9,7 @@
   "demos/cr_action_menu_demo_component.ts",
   "demos/cr_dialog_demo_component.ts",
   "demos/cr_input/cr_input_demo_component.ts",
+  "demos/cr_slider/cr_slider_demo_component.ts",
 ]
 
 # Files that are passed as input to html_to_wrapper().
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java
index 58ac8fb..a5aa7ac 100644
--- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java
+++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleCoordinator.java
@@ -24,7 +24,7 @@
             Profile profile, int moduleContainerStbuId) {
         OnSuggestionClickCallback callback = (gurl) -> {
             currentTab.loadUrl(new LoadUrlParams(gurl));
-            RecordUserAction.record(SearchResumptionModuleMediator.ACTION_CLICK);
+            RecordUserAction.record(SearchResumptionModuleUtils.ACTION_CLICK);
         };
         mTileBuilder = new SearchResumptionTileBuilder(callback);
         mMediator = new SearchResumptionModuleMediator(
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
index e72466aa..efdba1a 100644
--- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
+++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
@@ -35,9 +35,6 @@
  */
 public class SearchResumptionModuleMediator
         implements OnSuggestionsReceivedListener, SignInStateObserver {
-    static final String ACTION_CLICK = "SearchResumptionModule.NTP.Click";
-    private static final String ACTION_SHOW = "SearchResumptionModule.NTP.Show";
-
     private final ViewStub mStub;
     private final Tab mTabToTrackSuggestion;
     private final SearchResumptionTileBuilder mTileBuilder;
@@ -99,6 +96,7 @@
         if (!initializeModule()) return;
         mTileBuilder.buildSuggestionTile(autocompleteResult.getSuggestionsList(),
                 mModuleLayoutView.findViewById(R.id.search_resumption_module_tiles_container));
+        SearchResumptionModuleUtils.recordModuleShown();
     }
 
     /**
@@ -108,6 +106,7 @@
         if (!initializeModule()) return;
         mTileBuilder.buildSuggestionTile(texts, urls,
                 mModuleLayoutView.findViewById(R.id.search_resumption_module_tiles_container));
+        SearchResumptionModuleUtils.recordModuleShown();
     }
 
     void destroy() {
@@ -210,7 +209,6 @@
                 mModel, mModuleLayoutView, new SearchResumptionModuleViewBinder());
         mModel.set(SearchResumptionModuleProperties.EXPAND_COLLAPSE_CLICK_CALLBACK,
                 this::onExpandedOrCollapsed);
-        RecordUserAction.record(ACTION_SHOW);
         return true;
     }
 
@@ -223,9 +221,12 @@
     /**
      * Saves the user's choice of expanding/collapsing of the suggestions in the SharedPreference.
      */
-    private void onExpandedOrCollapsed(Boolean expanded) {
+    @VisibleForTesting
+    void onExpandedOrCollapsed(Boolean expanded) {
         SharedPreferencesManager.getInstance().writeBoolean(
                 ChromePreferenceKeys.SEARCH_RESUMPTION_MODULE_COLLAPSE_ON_NTP, !expanded);
+        RecordUserAction.record(expanded ? SearchResumptionModuleUtils.ACTION_EXPAND
+                                         : SearchResumptionModuleUtils.ACTION_COLLAPSE);
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleUtils.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleUtils.java
index 031b43d..80da649 100644
--- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleUtils.java
+++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleUtils.java
@@ -6,9 +6,13 @@
 
 import android.view.ViewGroup;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -19,14 +23,31 @@
 import org.chromium.components.signin.identitymanager.ConsentLevel;
 import org.chromium.url.GURL;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.TimeUnit;
 
 /**
  * This is a utility class for search resumption module.
  */
 public class SearchResumptionModuleUtils {
+    @IntDef({ModuleShowStatus.EXPANDED, ModuleShowStatus.COLLAPSED, ModuleShowStatus.NUM_ENTRIES})
+    @Retention(RetentionPolicy.SOURCE)
+    // The ModuleShowStatus should be consistent with SearchResumptionModule.ModuleShowStatus in
+    // enums.xml.
+    public @interface ModuleShowStatus {
+        int EXPANDED = 0;
+        int COLLAPSED = 1;
+        int NUM_ENTRIES = 2;
+    }
+
     @VisibleForTesting
     static final String TAB_EXPIRATION_TIME_PARAM = "tab_expiration_time";
+    @VisibleForTesting
+    static final String UMA_MODULE_SHOW = "NewTabPage.SearchResumptionModule.Show";
+    static final String ACTION_CLICK = "SearchResumptionModule.NTP.Click";
+    static final String ACTION_COLLAPSE = "SearchResumptionModule.NTP.Collapse";
+    static final String ACTION_EXPAND = "SearchResumptionModule.NTP.Expand";
     static final String USE_NEW_SERVICE_PARAM = "use_new_service";
     private static final int LAST_TAB_EXPIRATION_TIME_SECONDS = 3600; // 1 Hour
 
@@ -110,4 +131,15 @@
                         ChromeFeatureList.SEARCH_RESUMPTION_MODULE_ANDROID,
                         TAB_EXPIRATION_TIME_PARAM, LAST_TAB_EXPIRATION_TIME_SECONDS);
     }
+
+    /**
+     * Records histogram when the search resumption module is shown.
+     */
+    static void recordModuleShown() {
+        boolean isCollapsed = SharedPreferencesManager.getInstance().readBoolean(
+                ChromePreferenceKeys.SEARCH_RESUMPTION_MODULE_COLLAPSE_ON_NTP, false);
+        RecordHistogram.recordEnumeratedHistogram(UMA_MODULE_SHOW,
+                isCollapsed ? ModuleShowStatus.COLLAPSED : ModuleShowStatus.EXPANDED,
+                ModuleShowStatus.NUM_ENTRIES);
+    }
 }
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
index fe46238..1f7c2330 100644
--- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
+++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
@@ -16,6 +16,7 @@
 import android.view.ViewStub;
 
 import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -33,8 +34,10 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController;
@@ -42,6 +45,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteControllerJni;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.chrome.browser.search_resumption.SearchResumptionModuleUtils.ModuleShowStatus;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.tab.Tab;
@@ -124,6 +128,7 @@
     @Mock
     private SigninManager mSignInManager;
 
+    private UserActionTester mActionTester;
     private SearchResumptionModuleMediator mMediator;
 
     @Before
@@ -152,6 +157,7 @@
         IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
         doReturn(mSignInManager).when(mIdentityServicesProvider).getSigninManager(any());
 
+        mActionTester = new UserActionTester();
         mMediator =
                 new SearchResumptionModuleMediator(mParent, mTabToTrack, mProfile, mTileBuilder);
         verify(mAutocompleteController).addOnSuggestionsReceivedListener(mListener.capture());
@@ -171,6 +177,7 @@
         IdentityServicesProvider.setInstanceForTests(null);
         ShadowChromeFeatureList.sEnableSearchResumptionModule = false;
         ShadowChromeFeatureList.sParamValues.clear();
+        mActionTester.tearDown();
     }
 
     @Test
@@ -181,6 +188,9 @@
 
         mMediator.onSuggestionsReceived(mAutocompleteResult, "", true);
         verify(mParent, times(0)).inflate();
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SearchResumptionModuleUtils.UMA_MODULE_SHOW, ModuleShowStatus.EXPANDED));
     }
 
     @Test
@@ -193,6 +203,9 @@
         mMediator.onSuggestionsReceived(mAutocompleteResult, "", true);
         verify(mParent, times(1)).inflate();
         Assert.assertEquals(View.VISIBLE, mSuggestionTilesContainerView.getVisibility());
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SearchResumptionModuleUtils.UMA_MODULE_SHOW, ModuleShowStatus.EXPANDED));
     }
 
     @Test
@@ -203,6 +216,9 @@
 
         mMediator.onSuggestionsAvailable(texts, gUrls);
         verify(mParent, times(0)).inflate();
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SearchResumptionModuleUtils.UMA_MODULE_SHOW, ModuleShowStatus.EXPANDED));
     }
     @Test
     @MediumTest
@@ -213,6 +229,17 @@
         mMediator.onSuggestionsAvailable(texts, gUrls);
         verify(mParent, times(1)).inflate();
         Assert.assertEquals(View.VISIBLE, mSuggestionTilesContainerView.getVisibility());
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SearchResumptionModuleUtils.UMA_MODULE_SHOW, ModuleShowStatus.EXPANDED));
+
+        mMediator.onExpandedOrCollapsed(true /* expand */);
+        Assert.assertTrue(
+                mActionTester.getActions().contains(SearchResumptionModuleUtils.ACTION_EXPAND));
+
+        mMediator.onExpandedOrCollapsed(false /* expand */);
+        Assert.assertTrue(
+                mActionTester.getActions().contains(SearchResumptionModuleUtils.ACTION_COLLAPSE));
     }
 
     @Test
@@ -235,7 +262,7 @@
     }
 
     @Test
-    @MediumTest
+    @SmallTest
     public void testDestroy() {
         testShowModuleWithEnoughResults();
         mMediator.destroy();
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java
index ddb11d9..97232dc8 100644
--- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java
+++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionTileBuilderUnitTest.java
@@ -20,7 +20,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
@@ -120,7 +119,6 @@
     private void createTileBuilder() {
         OnSuggestionClickCallback callback = (gUrl) -> {
             mTab.loadUrl(new LoadUrlParams(gUrl));
-            RecordUserAction.record(SearchResumptionModuleMediator.ACTION_CLICK);
         };
         mTileBuilder = new SearchResumptionTileBuilder(callback);
     }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f952c83..3d39f0c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -4467,6 +4467,8 @@
       "views/importer/import_lock_dialog_view.h",
       "views/incognito_clear_browsing_data_dialog.cc",
       "views/incognito_clear_browsing_data_dialog.h",
+      "views/incognito_clear_browsing_data_dialog_coordinator.cc",
+      "views/incognito_clear_browsing_data_dialog_coordinator.h",
       "views/infobars/alternate_nav_infobar_view.cc",
       "views/infobars/alternate_nav_infobar_view.h",
       "views/infobars/confirm_infobar.cc",
@@ -5516,7 +5518,8 @@
     deps += [ "//components/pdf/browser" ]
   }
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Make `HungPluginTabHelper` usage conditional.
+  if (enable_ppapi) {
     sources += [
       "hung_plugin_tab_helper.cc",
       "hung_plugin_tab_helper.h",
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 67530d8..5ae33e5 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -53,6 +53,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/ui_devtools/devtools_server.h"
 #include "components/user_manager/user_manager.h"
+#include "components/version_info/channel.h"
+#include "components/version_info/version_info.h"
 #include "content/public/browser/device_service.h"
 #include "content/public/browser/media_session_service.h"
 #include "content/public/browser/render_widget_host.h"
@@ -352,3 +354,7 @@
     }
   }
 }
+
+std::string ChromeShellDelegate::GetVersionString() {
+  return version_info::GetVersionNumber();
+}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 6e909de6..56ee35b 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_ASH_CHROME_SHELL_DELEGATE_H_
 
 #include <memory>
+#include <string>
 
 #include "ash/shell_delegate.h"
 #include "base/callback_forward.h"
@@ -65,6 +66,7 @@
   version_info::Channel GetChannel() override;
   void ForceSkipWarningUserOnClose(
       const std::vector<aura::Window*>& windows) override;
+  std::string GetVersionString() override;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_CHROME_SHELL_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.cc b/chrome/browser/ui/ash/system_tray_client_impl.cc
index 38a1540..0d03294d 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.cc
+++ b/chrome/browser/ui/ash/system_tray_client_impl.cc
@@ -757,6 +757,15 @@
   opened_pwa = true;
 }
 
+void SystemTrayClientImpl::ShowChannelInfoAdditionalDetails() {
+  ShowSettingsSubPageForActiveUser(
+      std::string(chromeos::settings::mojom::kDetailedBuildInfoSubpagePath));
+}
+
+void SystemTrayClientImpl::ShowChannelInfoGiveFeedback() {
+  ash::NewWindowDelegate::GetInstance()->OpenFeedbackPage();
+}
+
 SystemTrayClientImpl::SystemTrayClientImpl(SystemTrayClientImpl* mock_instance)
     : system_tray_(nullptr) {
   DCHECK(!g_system_tray_client_instance);
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.h b/chrome/browser/ui/ash/system_tray_client_impl.h
index fedf44e9..a54724e 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.h
+++ b/chrome/browser/ui/ash/system_tray_client_impl.h
@@ -104,6 +104,8 @@
                          const base::Time& date,
                          bool& opened_pwa,
                          GURL& finalized_event_url) override;
+  void ShowChannelInfoAdditionalDetails() override;
+  void ShowChannelInfoGiveFeedback() override;
 
  protected:
   // Used by mocks in tests.
diff --git a/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc b/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
index b7e2772..4925a05 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
+++ b/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
@@ -26,10 +26,10 @@
 #include "content/public/browser/hid_chooser.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/web_contents_tester.h"
-#include "services/device/hid/test_report_descriptors.h"
-#include "services/device/hid/test_util.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
 #include "services/device/public/cpp/hid/hid_switches.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
+#include "services/device/public/cpp/test/hid_test_util.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 5c23a686..ab0dbbd177 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -118,7 +118,7 @@
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
 #include "chrome/browser/ui/views/hats/hats_next_web_dialog.h"
-#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h"
+#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h"
 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
 #include "chrome/browser/ui/views/location_bar/intent_chip_button.h"
 #include "chrome/browser/ui/views/location_bar/intent_picker_view.h"
@@ -4317,21 +4317,14 @@
 }
 
 void BrowserView::ShowIncognitoClearBrowsingDataDialog() {
-  IncognitoClearBrowsingDataDialog::Show(
-      static_cast<views::View*>(BrowserView::GetBrowserViewForBrowser(browser())
-                                    ->toolbar_button_provider()
-                                    ->GetAvatarToolbarButton()),
-      browser()->profile(),
-      IncognitoClearBrowsingDataDialog::Type::kDefaultBubble);
+  IncognitoClearBrowsingDataDialogCoordinator::GetOrCreateForBrowser(browser())
+      ->Show(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
 }
 
 void BrowserView::ShowIncognitoHistoryDisclaimerDialog() {
-  IncognitoClearBrowsingDataDialog::Show(
-      static_cast<views::View*>(BrowserView::GetBrowserViewForBrowser(browser())
-                                    ->toolbar_button_provider()
-                                    ->GetAvatarToolbarButton()),
-      browser()->profile(),
-      IncognitoClearBrowsingDataDialog::Type::kHistoryDisclaimerBubble);
+  IncognitoClearBrowsingDataDialogCoordinator::GetOrCreateForBrowser(browser())
+      ->Show(IncognitoClearBrowsingDataDialogInterface::Type::
+                 kHistoryDisclaimerBubble);
 }
 
 ExclusiveAccessContext* BrowserView::GetExclusiveAccessContext() {
diff --git a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.cc b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.cc
index 4569d0afc..1384c94 100644
--- a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.cc
+++ b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h"
 
 #include "base/metrics/histogram_functions.h"
-
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
@@ -22,48 +21,6 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/layout_provider.h"
 
-namespace {
-IncognitoClearBrowsingDataDialog* g_incognito_cbd_dialog = nullptr;
-}  // namespace
-
-// static
-void IncognitoClearBrowsingDataDialog::Show(views::View* anchor_view,
-                                            Profile* incognito_profile,
-                                            Type type) {
-  g_incognito_cbd_dialog = new IncognitoClearBrowsingDataDialog(
-      anchor_view, incognito_profile, type);
-  views::Widget* const widget =
-      BubbleDialogDelegateView::CreateBubble(g_incognito_cbd_dialog);
-  widget->Show();
-}
-
-// static
-bool IncognitoClearBrowsingDataDialog::IsShowing() {
-  return g_incognito_cbd_dialog != nullptr;
-}
-
-// static
-void IncognitoClearBrowsingDataDialog::CloseDialog() {
-  if (IsShowing())
-    g_incognito_cbd_dialog->GetWidget()->Close();
-}
-
-// static
-IncognitoClearBrowsingDataDialog* IncognitoClearBrowsingDataDialog::
-    GetIncognitoClearBrowsingDataDialogForTesting() {
-  return g_incognito_cbd_dialog;
-}
-
-void IncognitoClearBrowsingDataDialog::SetDestructorCallbackForTesting(
-    base::OnceClosure callback) {
-  destructor_callback_ = std::move(callback);
-}
-
-IncognitoClearBrowsingDataDialog::~IncognitoClearBrowsingDataDialog() {
-  g_incognito_cbd_dialog = nullptr;
-  std::move(destructor_callback_).Run();
-}
-
 IncognitoClearBrowsingDataDialog::IncognitoClearBrowsingDataDialog(
     views::View* anchor_view,
     Profile* incognito_profile,
@@ -201,7 +158,8 @@
         DialogActionType::kCancel);
   }
 
-  CloseDialog();
+  GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kCloseButtonClicked);
 }
 
 BEGIN_METADATA(IncognitoClearBrowsingDataDialog,
diff --git a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h
index d0289fe..411fd8b8 100644
--- a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h
+++ b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h
@@ -5,9 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_H_
 #define CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_H_
 
-#include "base/callback.h"
 #include "base/memory/raw_ptr.h"
-
 #include "chrome/browser/ui/incognito_clear_browsing_data_dialog_interface.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
@@ -24,27 +22,16 @@
  public:
   METADATA_HEADER(IncognitoClearBrowsingDataDialog);
 
-  static void Show(views::View* anchor_view,
-                   Profile* incognito_profile,
-                   Type type);
-  static bool IsShowing();
-
-  // testing
-  static IncognitoClearBrowsingDataDialog*
-  GetIncognitoClearBrowsingDataDialogForTesting();
-  void SetDestructorCallbackForTesting(base::OnceClosure callback);
-
+  IncognitoClearBrowsingDataDialog(views::View* anchor_view,
+                                   Profile* incognito_profile,
+                                   Type type);
   IncognitoClearBrowsingDataDialog(
       const IncognitoClearBrowsingDataDialog& other) = delete;
   IncognitoClearBrowsingDataDialog& operator=(
       const IncognitoClearBrowsingDataDialog& other) = delete;
-  ~IncognitoClearBrowsingDataDialog() override;
+  ~IncognitoClearBrowsingDataDialog() override = default;
 
  private:
-  explicit IncognitoClearBrowsingDataDialog(views::View* anchor_view,
-                                            Profile* incognito_profile,
-                                            Type type);
-
   static void CloseDialog();
 
   // Helper methods to add functionality to the button.
@@ -57,7 +44,6 @@
 
   const Type dialog_type_;
   raw_ptr<Profile> incognito_profile_;
-  base::OnceClosure destructor_callback_ = base::DoNothing();
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_H_
diff --git a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_browsertest.cc b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_browsertest.cc
index 9292d34..33883ce 100644
--- a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h"
+#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h"
 #include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/chrome_features.h"
@@ -18,45 +19,75 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/test/widget_test.h"
+#include "ui/views/widget/widget_observer.h"
 
-class IncognitoClearBrowsingDataDialogBrowserTest : public DialogBrowserTest {
+namespace {
+
+class IncognitoClearBrowsingDataDialogTest : public InProcessBrowserTest {
  public:
-  void ShowUi(const std::string& name) override { OpenDialog(); }
-
-  void OpenDialog() {
+  void OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type type) {
     incognito_browser_ = CreateIncognitoBrowser(browser()->profile());
-    views::View* view = static_cast<views::View*>(
-        BrowserView::GetBrowserViewForBrowser(incognito_browser_)
-            ->toolbar_button_provider()
-            ->GetAvatarToolbarButton());
-    IncognitoClearBrowsingDataDialog::Show(
-        view, incognito_browser_->profile(),
-        IncognitoClearBrowsingDataDialog::Type::kDefaultBubble);
-    EXPECT_TRUE(IncognitoClearBrowsingDataDialog::IsShowing());
+    auto* coordinator = GetCoordinator();
+    coordinator->Show(type);
+    EXPECT_TRUE(coordinator->IsShowing());
   }
 
   Browser* GetIncognitoBrowser() { return incognito_browser_; }
 
   IncognitoClearBrowsingDataDialog* GetDialogView() {
-    return IncognitoClearBrowsingDataDialog::
-        GetIncognitoClearBrowsingDataDialogForTesting();
+    return GetCoordinator()->GetIncognitoClearBrowsingDataDialogForTesting();
+  }
+
+  views::Widget* GetDialogWidget() {
+    auto* dialog_view = GetDialogView();
+    return dialog_view ? dialog_view->GetWidget() : nullptr;
+  }
+
+  IncognitoClearBrowsingDataDialogCoordinator* GetCoordinator() {
+    return IncognitoClearBrowsingDataDialogCoordinator::GetOrCreateForBrowser(
+        incognito_browser_);
   }
 
  private:
   raw_ptr<Browser> incognito_browser_ = nullptr;
 };
 
+// Used to test that the bubble widget is destroyed before the browser.
+class BubbleWidgetDestroyedObserver : public views::WidgetObserver {
+ public:
+  explicit BubbleWidgetDestroyedObserver(views::Widget* bubble_widget) {
+    bubble_widget->AddObserver(this);
+  }
+  ~BubbleWidgetDestroyedObserver() override = default;
+
+  // views::WidgetObserver:
+  void OnWidgetDestroyed(views::Widget* widget) override {
+    ASSERT_GT(BrowserList::GetIncognitoBrowserCount(), 0u);
+  }
+};
+
+}  // namespace
+
+class IncognitoClearBrowsingDataDialogBrowserTest
+    : public SupportsTestDialog<IncognitoClearBrowsingDataDialogTest> {
+ public:
+  void ShowUi(const std::string& name) override {
+    OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
+  }
+};
+
 IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
                        InvokeUi_default) {
   ShowAndVerifyUi();
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
-                       TestDialogIsShown) {
-  OpenDialog();
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
+                       TestDialogIsShown_DefaultBubble) {
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
   auto* incognito_cbd_dialog_view = GetDialogView();
 
-  ASSERT_TRUE(IncognitoClearBrowsingDataDialog::IsShowing());
+  ASSERT_TRUE(GetCoordinator()->IsShowing());
   ASSERT_EQ(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
             incognito_cbd_dialog_view->GetDialogButtons());
   ASSERT_TRUE(
@@ -65,55 +96,48 @@
       ui::DIALOG_BUTTON_CANCEL));
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                        TestCloseWindowsButton) {
   base::HistogramTester histogram_tester;
-  OpenDialog();
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
+  auto destroyed_observer =
+      BubbleWidgetDestroyedObserver(GetDialogView()->GetWidget());
 
   GetDialogView()->AcceptDialog();
   histogram_tester.ExpectBucketCount(
       "Incognito.ClearBrowsingDataDialog.ActionType",
       IncognitoClearBrowsingDataDialog::DialogActionType::kCloseIncognito, 1);
   ui_test_utils::WaitForBrowserToClose(GetIncognitoBrowser());
-  ASSERT_EQ(0UL, BrowserList::GetIncognitoBrowserCount());
-  ASSERT_TRUE(GetDialogView() == nullptr);
+  ASSERT_EQ(0u, BrowserList::GetIncognitoBrowserCount());
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
-                       TestCancelButton) {
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest, TestCancelButton) {
   base::HistogramTester histogram_tester;
-  OpenDialog();
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
 
-  base::RunLoop run_loop;
-  GetDialogView()->SetDestructorCallbackForTesting(
-      base::BindLambdaForTesting([&]() {
-        run_loop.Quit();
-        ASSERT_FALSE(IncognitoClearBrowsingDataDialog::IsShowing());
-        ASSERT_TRUE(GetDialogView() == nullptr);
-      }));
+  views::test::WidgetDestroyedWaiter destroyed_waiter(GetDialogWidget());
 
   GetDialogView()->Cancel();
   histogram_tester.ExpectBucketCount(
       "Incognito.ClearBrowsingDataDialog.ActionType",
       IncognitoClearBrowsingDataDialog::DialogActionType::kCancel, 1);
-  run_loop.Run();
+  destroyed_waiter.Wait();
+
+  ASSERT_FALSE(GetCoordinator()->IsShowing());
+  ASSERT_FALSE(GetDialogView());
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                        TestBrowserCloseEventClosesDialogFirst) {
-  OpenDialog();
-
-  GetDialogView()->SetDestructorCallbackForTesting(
-      base::BindLambdaForTesting([&]() {
-        ASSERT_FALSE(IncognitoClearBrowsingDataDialog::IsShowing());
-        ASSERT_TRUE(GetDialogView() == nullptr);
-        ASSERT_TRUE(BrowserList::GetIncognitoBrowserCount() > 0);
-      }));
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
+  auto destroyed_observer = BubbleWidgetDestroyedObserver(GetDialogWidget());
 
   CloseBrowserSynchronously(GetIncognitoBrowser());
+
+  ASSERT_EQ(0u, BrowserList::GetIncognitoBrowserCount());
 }
 
-IN_PROC_BROWSER_TEST_F(InProcessBrowserTest,
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                        ClearBrowsingDataNavigationInIncognito) {
   Browser* incognito_browser = CreateIncognitoBrowser();
   ui_test_utils::SendToOmniboxAndSubmit(incognito_browser,
@@ -121,41 +145,18 @@
   std::u16string current_tab_title;
   ui_test_utils::GetCurrentTabTitle(incognito_browser, &current_tab_title);
   EXPECT_EQ(u"about:blank", current_tab_title);
-  ASSERT_TRUE(IncognitoClearBrowsingDataDialog::IsShowing());
+  auto* coordinator = IncognitoClearBrowsingDataDialogCoordinator::FromBrowser(
+      incognito_browser);
+  ASSERT_TRUE(coordinator->IsShowing());
 }
 
-class IncognitoHistoryDisclaimerDialogBrowserTest
-    : public InProcessBrowserTest {
- public:
-  void OpenDialog() {
-    incognito_browser_ = CreateIncognitoBrowser(browser()->profile());
-    views::View* view = static_cast<views::View*>(
-        BrowserView::GetBrowserViewForBrowser(incognito_browser_)
-            ->toolbar_button_provider()
-            ->GetAvatarToolbarButton());
-    IncognitoClearBrowsingDataDialog::Show(
-        view, incognito_browser_->profile(),
-        IncognitoClearBrowsingDataDialog::Type::kHistoryDisclaimerBubble);
-    EXPECT_TRUE(IncognitoClearBrowsingDataDialog::IsShowing());
-  }
-
-  Browser* GetIncognitoBrowser() { return incognito_browser_; }
-
-  IncognitoClearBrowsingDataDialog* GetDialogView() {
-    return IncognitoClearBrowsingDataDialog::
-        GetIncognitoClearBrowsingDataDialogForTesting();
-  }
-
- private:
-  raw_ptr<Browser> incognito_browser_ = nullptr;
-};
-
-IN_PROC_BROWSER_TEST_F(IncognitoHistoryDisclaimerDialogBrowserTest,
-                       TestDialogIsShown) {
-  OpenDialog();
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
+                       TestDialogIsShown_HistoryDisclaimerBubble) {
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
+                 kHistoryDisclaimerBubble);
   auto* incognito_cbd_dialog_view = GetDialogView();
 
-  ASSERT_TRUE(IncognitoClearBrowsingDataDialog::IsShowing());
+  ASSERT_TRUE(GetCoordinator()->IsShowing());
   ASSERT_EQ(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
             incognito_cbd_dialog_view->GetDialogButtons());
   ASSERT_TRUE(
@@ -164,28 +165,25 @@
       ui::DIALOG_BUTTON_CANCEL));
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoHistoryDisclaimerDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                        TestCloseIncognitoButton) {
-  OpenDialog();
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
+                 kHistoryDisclaimerBubble);
 
   GetDialogView()->CancelDialog();
   ui_test_utils::WaitForBrowserToClose(GetIncognitoBrowser());
-  ASSERT_EQ(0UL, BrowserList::GetIncognitoBrowserCount());
-  ASSERT_TRUE(GetDialogView() == nullptr);
+  ASSERT_EQ(0u, BrowserList::GetIncognitoBrowserCount());
 }
 
-IN_PROC_BROWSER_TEST_F(IncognitoHistoryDisclaimerDialogBrowserTest,
-                       TestGotItButton) {
-  OpenDialog();
+IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest, TestGotItButton) {
+  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
+                 kHistoryDisclaimerBubble);
 
-  base::RunLoop run_loop;
-  GetDialogView()->SetDestructorCallbackForTesting(
-      base::BindLambdaForTesting([&]() {
-        run_loop.Quit();
-        ASSERT_FALSE(IncognitoClearBrowsingDataDialog::IsShowing());
-        ASSERT_TRUE(GetDialogView() == nullptr);
-      }));
+  views::test::WidgetDestroyedWaiter destroyed_waiter(GetDialogWidget());
 
   GetDialogView()->AcceptDialog();
-  run_loop.Run();
+  destroyed_waiter.Wait();
+
+  ASSERT_FALSE(GetCoordinator()->IsShowing());
+  ASSERT_FALSE(GetDialogView());
 }
diff --git a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.cc b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.cc
new file mode 100644
index 0000000..10c69e0
--- /dev/null
+++ b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h"
+
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog.h"
+#include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/view_utils.h"
+
+IncognitoClearBrowsingDataDialogCoordinator::
+    ~IncognitoClearBrowsingDataDialogCoordinator() {
+  // Forcefully close the Widget if it hasn't been closed by the time the
+  // browser is torn down to avoid dangling references.
+  if (IsShowing())
+    bubble_tracker_.view()->GetWidget()->CloseNow();
+}
+
+void IncognitoClearBrowsingDataDialogCoordinator::Show(
+    IncognitoClearBrowsingDataDialogInterface::Type type) {
+  auto* avatar_toolbar_button =
+      BrowserView::GetBrowserViewForBrowser(&GetBrowser())
+          ->toolbar_button_provider()
+          ->GetAvatarToolbarButton();
+
+  auto bubble = std::make_unique<IncognitoClearBrowsingDataDialog>(
+      avatar_toolbar_button, GetBrowser().profile(), type);
+  DCHECK_EQ(nullptr, bubble_tracker_.view());
+  bubble_tracker_.SetView(bubble.get());
+
+  auto* widget =
+      views::BubbleDialogDelegateView::CreateBubble(std::move(bubble));
+  widget->Show();
+}
+
+bool IncognitoClearBrowsingDataDialogCoordinator::IsShowing() const {
+  return bubble_tracker_.view() != nullptr;
+}
+
+IncognitoClearBrowsingDataDialog* IncognitoClearBrowsingDataDialogCoordinator::
+    GetIncognitoClearBrowsingDataDialogForTesting() {
+  return IsShowing() ? views::AsViewClass<IncognitoClearBrowsingDataDialog>(
+                           bubble_tracker_.view())
+                     : nullptr;
+}
+
+IncognitoClearBrowsingDataDialogCoordinator::
+    IncognitoClearBrowsingDataDialogCoordinator(Browser* browser)
+    : BrowserUserData<IncognitoClearBrowsingDataDialogCoordinator>(*browser) {}
+
+BROWSER_USER_DATA_KEY_IMPL(IncognitoClearBrowsingDataDialogCoordinator);
diff --git a/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h
new file mode 100644
index 0000000..836279a
--- /dev/null
+++ b/chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_COORDINATOR_H_
+#define CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_COORDINATOR_H_
+
+#include "chrome/browser/ui/browser_user_data.h"
+#include "chrome/browser/ui/incognito_clear_browsing_data_dialog_interface.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "ui/views/view_tracker.h"
+
+class IncognitoClearBrowsingDataDialog;
+
+// Handles the lifetime and showing/hidden state of the clear data dialog. Owned
+// by the associated incognito browser.
+class IncognitoClearBrowsingDataDialogCoordinator
+    : public BrowserUserData<IncognitoClearBrowsingDataDialogCoordinator> {
+ public:
+  ~IncognitoClearBrowsingDataDialogCoordinator() override;
+
+  // Shows the bubble for this browser anchored to the avatar toolbar button.
+  void Show(IncognitoClearBrowsingDataDialogInterface::Type type);
+
+  // Returns true if the bubble is currently showing for this window.
+  bool IsShowing() const;
+
+  IncognitoClearBrowsingDataDialog*
+  GetIncognitoClearBrowsingDataDialogForTesting();
+
+ private:
+  friend class BrowserUserData<IncognitoClearBrowsingDataDialogCoordinator>;
+
+  explicit IncognitoClearBrowsingDataDialogCoordinator(Browser* browser);
+
+  views::ViewTracker bubble_tracker_;
+
+  BROWSER_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_INCOGNITO_CLEAR_BROWSING_DATA_DIALOG_COORDINATOR_H_
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
index a4d0dcc..968b54c 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -28,6 +28,7 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/combobox_model.h"
+#include "ui/color/color_id.h"
 #include "ui/gfx/vector_icon_utils.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
@@ -503,6 +504,7 @@
                                /*adjust_height_for_width=*/false)
           .WithAlignment(views::LayoutAlignment::kStart));
   combobox->SetBorderColorId(ui::kColorSidePanelComboboxBorder);
+  combobox->SetBackgroundColorId(ui::kColorSidePanelComboboxBackground);
   combobox->SetEventHighlighting(true);
   combobox->SetSizeToLargestLabel(false);
   return combobox;
diff --git a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
index bf7e2ae..b1bfed2dc 100644
--- a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
+++ b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
@@ -90,6 +90,8 @@
       FeatureToValue(sandbox::policy::features::kNetworkServiceSandbox));
   features.Append(
       FeatureToValue(sandbox::policy::features::kRendererAppContainer));
+  features.Append(
+      FeatureToValue(sandbox::policy::features::kSharedSandboxPolicies));
   features.Append(FeatureToValue(
       sandbox::policy::features::kWinSboxDisableExtensionPoints));
   features.Append(
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 6bf3e92..4a47927 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1658512676-b6695eba9e88a97d4d2f04e188c7a0ca6614dade.profdata
+chrome-linux-main-1658552856-16a7c6b86bb3c6fb838478ef2a7012253c8101cf.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 4cb3436..e9f31df72 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1658491057-e72f9fce5fc943f682aae1d124bfb9a4f94b6bb9.profdata
+chrome-mac-arm-main-1658552856-28fc7c41b8c788990a760a9cce5365fff3ae9ab5.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index bcade26..96f4ae8 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1658491057-137863e05dfa84c00e67b2981b56cb9ef4e57418.profdata
+chrome-mac-main-1658552856-505b6fa3506e160dcda3182525181d00de377b40.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index fcaa60a2..e174854 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1658501847-d9359f678f2fa3ae7b392ca5025565064d5c703e.profdata
+chrome-win32-main-1658523536-d0ba88992dd0ac6cc790217b9d4f014c6628d8bb.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 39962a35..edc2e90 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1658512676-ca3c11dcbebd05c9b9635266f02762ad8243a3ec.profdata
+chrome-win64-main-1658552856-84ab64b259913e0d01bb935d056f4b61450434cc.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 20b8292..aa50e5bf 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -270,7 +270,7 @@
     deps += [ "//components/pdf/common" ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     public_deps += [ "//ppapi/shared_impl" ]
   }
 
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl
index bb9b782..96ed48a0 100644
--- a/chrome/common/extensions/api/developer_private.idl
+++ b/chrome/common/extensions/api/developer_private.idl
@@ -380,12 +380,26 @@
   };
 
   dictionary SiteInfo {
-    UserSiteSet siteList;
+    // The user specified site list that `site` belongs to, if any.
+    UserSiteSet? siteList;
+    // The number of extensions with access to `site`.
+    // TODO(crbug.com/1331137): A tricky edge case is when one extension
+    // specifies something like *.foo.com and another specifies foo.com.
+    // Patterns which match all subdomains should be represented differently.
+    long numExtensions;
+    // The site itself. This could either be a user specified site or an
+    // extension host permission pattern.
     DOMString site;
   };
 
   dictionary SiteGroup {
+    // The common effective top level domain plus one (eTLD+1) for all sites in
+    // `sites`.
     DOMString etldPlusOne;
+    // The number of extensions that can run on at least one site inside `sites`
+    // for this eTLD+1.
+    long numExtensions;
+    // The list of user or extension specified sites that share the same eTLD+1.
     SiteInfo[] sites;
   };
 
@@ -761,7 +775,6 @@
 
     // Returns all hosts specified by user site settings, grouped by each host's
     // eTLD+1.
-    // TODO(crbug.com/1331137): Get extension specified sites as well.
     [supportsPromises] static void getUserAndExtensionSitesByEtld(
         optional UserAndExtensionSitesByEtldCallback callback);
 
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 0ad8a99..f4d1346e 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -227,7 +227,9 @@
       deps += [ "//components/nacl/loader" ]
     }
   }
-  if (enable_plugins) {
+
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "pepper/chrome_renderer_pepper_host_factory.cc",
       "pepper/chrome_renderer_pepper_host_factory.h",
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7fdba4b..bbcc7e5 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -720,7 +720,7 @@
     public_deps += [ "//components/offline_pages/core:test_support" ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     public_deps += [ "//ppapi/shared_impl" ]
   }
 }
@@ -1486,7 +1486,6 @@
       "//rlz/buildflags",
       "//services/audio/public/cpp:test_support",
       "//services/device/public/cpp:test_support",
-      "//services/device/public/cpp/hid:test_support",
       "//services/device/public/mojom",
       "//services/image_annotation/public/cpp",
       "//services/network/public/cpp",
@@ -2734,7 +2733,8 @@
       sources += [ "../browser/webshare/share_service_browsertest.cc" ]
     }
 
-    if (enable_plugins) {
+    # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+    if (enable_ppapi) {
       sources += [
         "../browser/chrome_plugin_browsertest.cc",
         "../browser/plugins/plugin_response_interceptor_url_loader_throttle_browsertest.cc",
@@ -4221,7 +4221,6 @@
         "//mojo/core/embedder",
         "//services/audio/public/cpp",
         "//services/audio/public/cpp:test_support",
-        "//services/device/public/cpp/hid",
         "//services/media_session/public/mojom",
         "//services/network/public/mojom",
         "//services/preferences/public/cpp",
@@ -6946,7 +6945,6 @@
       "//components/user_notes:features",
       "//components/user_notes/browser",
       "//components/user_notes/interfaces",
-      "//services/device/hid:test_support",
       "//services/metrics/public/cpp:ukm_builders",
       "//services/network:test_support",
       "//third_party/libaddressinput",
@@ -7884,7 +7882,6 @@
       "//google_apis/common",
       "//google_apis/drive",
       "//media/cast:test_support",
-      "//services/device/public/cpp/hid:test_support",
       "//services/network/public/mojom",
 
       # This will add all of the unit tests for the schema compiler to this
@@ -8951,7 +8948,7 @@
       ]
     }
 
-    if (enable_plugins) {
+    if (enable_ppapi) {
       sources += [
         "ppapi/ppapi_test.cc",
         "ppapi/ppapi_test.h",
@@ -9319,7 +9316,7 @@
       "//third_party/mesa_headers",
       "//ui/resources:ui_test_pak_data",
     ]
-    if (enable_plugins) {
+    if (enable_ppapi) {
       sources += [ "ppapi/ppapi_interactive_browsertest.cc" ]
       data += [
         "//ppapi/tests/test_case.html",
diff --git a/chrome/test/data/extensions/blocked_actions/content_scripts/script.js b/chrome/test/data/extensions/blocked_actions/content_scripts/script.js
index c54b92c..0b0afee 100644
--- a/chrome/test/data/extensions/blocked_actions/content_scripts/script.js
+++ b/chrome/test/data/extensions/blocked_actions/content_scripts/script.js
@@ -4,6 +4,12 @@
 
 // If the script was really injected at document_start, then document.body will
 // be null. If it's not null, then we didn't inject at document_start.
-// Store the result in window.localStorage so that it's accessible from the
-// main world script context in the browsertest.
-window.localStorage.setItem('extResult', document.body ? 'failure' : 'success');
+var isDocumentStart = !document.body;
+
+// Set the title of the document to the success state (so that it's easily
+// readable from the C++ side). Of course, since this is (hopefully!)
+// document start, we need to wait for the page to load before setting
+// it. That's fine; we calculated the success value at the right moment.
+window.onload = () => {
+  document.title = isDocumentStart ? 'success' : 'failure';
+};
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
index 35d4b8b..8b51e33 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
@@ -67,7 +67,7 @@
     // Verify the i18n string is added.
     assertTrue(page.i18nExists('addFileLabel'));
     // Verify the replace file label is in the page.
-    assertEquals('Replace', getElementContent('#replaceFileLabel'));
+    assertEquals('Replace', getElementContent('#replaceFileButton'));
     // The addFileContainer should be visible when no file is selected.
     assertTrue(isVisible(getElement('#addFileContainer')));
     // The replaceFileContainer should be invisible when no file is selected.
@@ -101,11 +101,11 @@
 
   // Test that when the replace file label is clicked, the file dialog is
   // opened.
-  test('canOpenFileDialogByClickReplaceFileLabel', async () => {
+  test('canOpenFileDialogByClickReplaceFileButton', async () => {
     await initializePage();
     // Verify the add file label is in the page.
-    const replaceFileLabel = getElement('#replaceFileLabel');
-    assertTrue(!!replaceFileLabel);
+    const replaceFileButton = getElement('#replaceFileButton');
+    assertTrue(!!replaceFileButton);
     /**@type {!HTMLInputElement} */
     const fileDialog =
         /**@type {!HTMLInputElement} */ (getElement('#selectFileDialog'));
@@ -117,7 +117,7 @@
       fileDialogClicked = true;
     });
 
-    replaceFileLabel.click();
+    replaceFileButton.click();
 
     await fileDialogClickPromise;
     assertTrue(fileDialogClicked);
@@ -149,6 +149,10 @@
     assertFalse(isVisible(getElement('#addFileContainer')));
     // The replaceFileContainer should be visible.
     assertTrue(isVisible(getElement('#replaceFileContainer')));
+    // The aria label of the replace file button is set.
+    assertEquals('Replace file', getElement('#replaceFileButton').ariaLabel);
+    // Verify the i18n string is added.
+    assertTrue(page.i18nExists('replaceFileArialLabel'));
   });
 
   // Test that when there is not a file selected, getAttachedFile returns null.
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index c4c33dce..69dc2ed 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -115,6 +115,12 @@
     assertTrue(page.i18nExists('userEmailLabel'));
     assertEquals('Email', getElementContent('#userEmailLabel'));
 
+    // Verify the role and aria label of the user email dropdown.
+    const userEmailDropDown = getElement('#userEmailDropDown');
+    assertEquals('listbox', userEmailDropDown.role);
+    assertTrue(page.i18nExists('userEmailAriaLabel'));
+    assertEquals('Select email', userEmailDropDown.ariaLabel);
+
     // Verify don't include email address is in the page.
     assertTrue(page.i18nExists('anonymousUser'));
     assertEquals(
diff --git a/chrome/test/webapps/models.py b/chrome/test/webapps/models.py
index b4fc046..fe78a75 100755
--- a/chrome/test/webapps/models.py
+++ b/chrome/test/webapps/models.py
@@ -245,7 +245,8 @@
                              ) -> str:
         comments = [
             "Test contents are generated by script. Please do not modify!",
-            "See `chrome/test/webapps/README.md` for more info.",
+            "See `docs/webapps/why-is-this-test-failing.md` or",
+            "`docs/webaps/integration-testing-framework` for more info.",
             "Sheriffs: Disabling this test is supported."
         ]
         body = ''.join(["  // " + comment + "\n" for comment in comments])
diff --git a/chrome/test/webapps/test_data/expected_test_txt/tests_default.txt b/chrome/test/webapps/test_data/expected_test_txt/tests_default.txt
index d56e179..7e9c42eb 100644
--- a/chrome/test/webapps/test_data/expected_test_txt/tests_default.txt
+++ b/chrome/test/webapps/test_data/expected_test_txt/tests_default.txt
@@ -1,6 +1,7 @@
 IN_PROC_BROWSER_TEST_F(TestName, WebAppIntegration_3Chicken_1Chicken_2ChickenGreen) {
   // Test contents are generated by script. Please do not modify!
-  // See `chrome/test/webapps/README.md` for more info.
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webaps/integration-testing-framework` for more info.
   // Sheriffs: Disabling this test is supported.
   helper_.StateChangeA(Animal::kChicken);
   helper_.CheckA(Animal::kChicken);
diff --git a/chrome/test/webapps/test_data/expected_test_txt/tests_default_cros.txt b/chrome/test/webapps/test_data/expected_test_txt/tests_default_cros.txt
index 52973ac..24b9258 100644
--- a/chrome/test/webapps/test_data/expected_test_txt/tests_default_cros.txt
+++ b/chrome/test/webapps/test_data/expected_test_txt/tests_default_cros.txt
@@ -1,6 +1,7 @@
 IN_PROC_BROWSER_TEST_F(TestNameCros, WebAppIntegration_3Dog_2DogRed_3Chicken_2ChickenGreen) {
   // Test contents are generated by script. Please do not modify!
-  // See `chrome/test/webapps/README.md` for more info.
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webaps/integration-testing-framework` for more info.
   // Sheriffs: Disabling this test is supported.
   helper_.StateChangeA(Animal::kDog);
   helper_.CheckB(Animal::kDog, Color::kRed);
diff --git a/chrome/test/webapps/test_data/expected_test_txt/tests_default_mac_win_linux.txt b/chrome/test/webapps/test_data/expected_test_txt/tests_default_mac_win_linux.txt
index 731571d6..1a30ab01 100644
--- a/chrome/test/webapps/test_data/expected_test_txt/tests_default_mac_win_linux.txt
+++ b/chrome/test/webapps/test_data/expected_test_txt/tests_default_mac_win_linux.txt
@@ -1,6 +1,7 @@
 IN_PROC_BROWSER_TEST_F(TestNameMacWinLinux, WebAppIntegration_4ChickenGreen_1Chicken_2ChickenGreen) {
   // Test contents are generated by script. Please do not modify!
-  // See `chrome/test/webapps/README.md` for more info.
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webaps/integration-testing-framework` for more info.
   // Sheriffs: Disabling this test is supported.
   helper_.StateChangeB(AnimalLess::kChicken, Color::kGreen);
   helper_.CheckA(Animal::kChicken);
@@ -8,7 +9,8 @@
 }
 IN_PROC_BROWSER_TEST_F(TestNameMacWinLinux, WebAppIntegration_3Dog_2DogRed) {
   // Test contents are generated by script. Please do not modify!
-  // See `chrome/test/webapps/README.md` for more info.
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webaps/integration-testing-framework` for more info.
   // Sheriffs: Disabling this test is supported.
   helper_.StateChangeA(Animal::kDog);
   helper_.CheckB(Animal::kDog, Color::kRed);
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 84653c9..755e5f2 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -3422,6 +3422,9 @@
       <message name="IDS_FEEDBACK_TOOL_REPLACE_FILE_LABEL" desc="Label for replacing a file.">
         Replace
       </message>
+      <message name="IDS_FEEDBACK_TOOL_REPLACE_FILE_ARIA_LABEL" desc="Aria Label of the replace file button">
+        Replace file
+      </message>
       <message name="IDS_FEEDBACK_TOOL_ADD_FILE_LABEL" desc="Label for the field of adding a file">
         Add file
       </message>
@@ -3437,6 +3440,9 @@
       <message name="IDS_FEEDBACK_TOOL_USER_EMAIL_LABEL" desc="Label showing the e-mail address that will be reported.">
         Email
       </message>
+      <message name="IDS_FEEDBACK_TOOL_USER_EMAIL_ARIA_LABEL" desc="Aria Label of the e-mail dropdown listing e-mail addresses to be selected to report.">
+        Select email
+      </message>
       <message name="IDS_FEEDBACK_TOOL_SEND_BUTTON_LABEL" desc="label for the send button to send feedback.">
         Send
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_REPLACE_FILE_ARIA_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_REPLACE_FILE_ARIA_LABEL.png.sha1
new file mode 100644
index 0000000..c05c773
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_REPLACE_FILE_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@
+b6c67bd7f2be57a8632d56ef341cda6debacdb6e
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_EMAIL_ARIA_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_EMAIL_ARIA_LABEL.png.sha1
new file mode 100644
index 0000000..9bca4b2
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_EMAIL_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@
+6994f9b0f50e750bb862e85582394aa58a531d3f
\ No newline at end of file
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 5c5e76f3..d7bf4c5 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-105-5161.0-1658138909-benchmark-105.0.5195.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-105-5161.0-1658138909-benchmark-105.0.5195.3-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index b7f6cbf..68e0e59c 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-105-5161.0-1658139264-benchmark-105.0.5195.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-105-5161.0-1658139264-benchmark-105.0.5195.2-r1-redacted.afdo.xz
diff --git a/chromeos/ui/frame/caption_buttons/frame_size_button.cc b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
index 0b45f5a4..8f9b900 100644
--- a/chromeos/ui/frame/caption_buttons/frame_size_button.cc
+++ b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
@@ -237,21 +237,23 @@
   // `pie_animation_` below to come after `StateChanged()`.
   views::FrameCaptionButton::OnMousePressed(event);
 
-  // The minimize and close buttons are set to snap left and right when snapping
-  // is enabled. Do not enable snapping if the minimize button is not visible.
-  // The close button is always visible.
-  if (IsTriggerableEvent(event) && !in_snap_mode_ &&
-      delegate_->IsMinimizeButtonVisible() && delegate_->CanSnap()) {
-    StartSetButtonsToSnapModeTimer(event);
-
+  if (IsTriggerableEvent(event)) {
     // Add a visual indicator of when snap mode will get triggered.
     if (chromeos::wm::features::IsFloatWindowEnabled()) {
-      std::pair<base::OnceClosure, base::OnceClosure> split =
-          base::SplitOnceCallback(base::BindOnce(
-              &FrameSizeButton::DestroyPieAnimation, base::Unretained(this)));
+      base::OnceClosure cancel_animation = base::BindOnce(
+          &FrameSizeButton::DestroyPieAnimation, base::Unretained(this));
+      base::OnceClosure show_multitask_menu = base::BindOnce(
+          &FrameSizeButton::OnPieAnimationCompleted, base::Unretained(this));
       pie_animation_ = std::make_unique<PieAnimation>(
-          kPieAnimationPressDuration, std::move(split.first),
-          std::move(split.second), this);
+          kPieAnimationPressDuration, std::move(cancel_animation),
+          std::move(show_multitask_menu), this);
+    }
+    // The minimize and close buttons are set to snap left and right when
+    // snapping is enabled. Do not enable snapping if the minimize button is not
+    // visible. The close button is always visible.
+    if (!in_snap_mode_ && delegate_->CanSnap() &&
+        delegate_->IsMinimizeButtonVisible()) {
+      StartSetButtonsToSnapModeTimer(event);
     }
   }
 
@@ -337,13 +339,14 @@
     return;
 
   if (GetState() == views::Button::STATE_HOVERED) {
-    // TODO(shidi): On animation end we should show the multitask menu.
-    std::pair<base::OnceClosure, base::OnceClosure> split =
-        base::SplitOnceCallback(base::BindOnce(
-            &FrameSizeButton::DestroyPieAnimation, base::Unretained(this)));
+    base::OnceClosure cancel_animation = base::BindOnce(
+        &FrameSizeButton::DestroyPieAnimation, base::Unretained(this));
+    base::OnceClosure show_multitask_menu = base::BindOnce(
+        &FrameSizeButton::OnPieAnimationCompleted, base::Unretained(this));
     pie_animation_ = std::make_unique<PieAnimation>(
-        kPieAnimationHoverDuration, std::move(split.first),
-        std::move(split.second), this);
+        kPieAnimationHoverDuration, std::move(cancel_animation),
+        std::move(show_multitask_menu), this);
+    // On animation end we should show the multitask menu.
   } else if (old_state == views::Button::STATE_HOVERED) {
     pie_animation_.reset();
   }
@@ -409,7 +412,6 @@
                               views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
                               animate);
   }
-  ShowMultitaskMenu();
 }
 
 void FrameSizeButton::UpdateSnapPreview(const ui::LocatedEvent& event) {
@@ -498,6 +500,11 @@
   delegate_->SetButtonsToNormal(animate);
 }
 
+void FrameSizeButton::OnPieAnimationCompleted() {
+  ShowMultitaskMenu();
+  pie_animation_.reset();
+}
+
 void FrameSizeButton::DestroyPieAnimation() {
   pie_animation_.reset();
 }
diff --git a/chromeos/ui/frame/caption_buttons/frame_size_button.h b/chromeos/ui/frame/caption_buttons/frame_size_button.h
index 91342168..726e21e 100644
--- a/chromeos/ui/frame/caption_buttons/frame_size_button.h
+++ b/chromeos/ui/frame/caption_buttons/frame_size_button.h
@@ -91,6 +91,8 @@
   // whether the buttons should animate back to their original icons.
   void SetButtonsToNormalMode(FrameSizeButtonDelegate::Animate animate);
 
+  // Show Multitask Menu when pie animation is completed.
+  void OnPieAnimationCompleted();
   void DestroyPieAnimation();
 
   // Not owned.
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index 5f547ac..3f9ea80 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -1394,7 +1394,6 @@
   base::test::ScopedFeatureList feature;
   feature.InitAndEnableFeature(
       autofill::features::kAutofillUseAlternativeStateNameMap);
-
   autofill::test::ClearAlternativeStateNameMapForTesting();
   autofill::test::PopulateAlternativeStateNameMapForTesting(
       "DE", "RandomState",
@@ -1706,13 +1705,8 @@
 // Tests that the profiles are merged when they have common states.
 TEST_P(AutofillProfileComparatorTest, MergeProfilesBasedOnState) {
   base::test::ScopedFeatureList feature;
-  // The feature
-  // |autofill::features::kAutofillEnableSupportForMoreStructureInAddresses| is
-  // disabled since it is incompatible with the feature
-  // |autofill::features::kAutofillUseStateMappingCache|.
-  feature.InitWithFeatures(
-      {autofill::features::kAutofillUseAlternativeStateNameMap},
-      {autofill::features::kAutofillEnableSupportForMoreStructureInAddresses});
+  feature.InitAndEnableFeature(
+      autofill::features::kAutofillUseAlternativeStateNameMap);
 
   autofill::test::ClearAlternativeStateNameMapForTesting();
   autofill::test::PopulateAlternativeStateNameMapForTesting();
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.cc b/components/autofill/core/browser/data_model/autofill_structured_address.cc
index 6a289ce..b094a22 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.cc
@@ -6,21 +6,18 @@
 
 #include <utility>
 #include "base/containers/contains.h"
-#include "base/i18n/case_conversion.h"
-#include "base/strings/strcat.h"
+#include "base/feature_list.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/address_rewriter.h"
 #include "components/autofill/core/browser/autofill_type.h"
-#include "components/autofill/core/browser/data_model/autofill_structured_address_constants.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/geo/alternative_state_name_map.h"
+#include "components/autofill/core/common/autofill_features.h"
 
-namespace autofill {
-
-namespace structured_address {
+namespace autofill::structured_address {
 
 std::u16string AddressComponentWithRewriter::RewriteValue(
     const std::u16string& value,
@@ -316,10 +313,39 @@
     : AddressComponentWithRewriter(
           ADDRESS_HOME_STATE,
           parent,
-          MergeMode::kPickShorterIfOneContainsTheOther | kReplaceEmpty) {}
+          kPickShorterIfOneContainsTheOther |
+              (base::FeatureList::IsEnabled(
+                   features::kAutofillUseAlternativeStateNameMap)
+                   ? MergeMode::kMergeBasedOnCanonicalizedValues
+                   : 0) |
+              kReplaceEmpty) {}
 
 State::~State() = default;
 
+absl::optional<std::u16string> State::GetCanonicalizedValue() const {
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillUseAlternativeStateNameMap)) {
+    return absl::nullopt;
+  }
+
+  std::string country_code =
+      base::UTF16ToUTF8(GetRootNode().GetValueForType(ADDRESS_HOME_COUNTRY));
+
+  if (country_code.empty()) {
+    return absl::nullopt;
+  }
+
+  absl::optional<AlternativeStateNameMap::CanonicalStateName>
+      canonicalized_state_name = AlternativeStateNameMap::GetCanonicalStateName(
+          country_code, GetValue());
+
+  if (!canonicalized_state_name.has_value()) {
+    return absl::nullopt;
+  }
+
+  return canonicalized_state_name.value().value();
+}
+
 // Zips are mergeable when one is a substring of the other one.
 // For merging, the shorter substring is taken.
 PostalCode::PostalCode(AddressComponent* parent)
@@ -389,6 +415,4 @@
   }
 }
 
-}  // namespace structured_address
-
-}  // namespace autofill
+}  // namespace autofill::structured_address
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.h b/components/autofill/core/browser/data_model/autofill_structured_address.h
index 971d418..3765788 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.h
@@ -188,6 +188,10 @@
  public:
   explicit State(AddressComponent* parent);
   ~State() override;
+
+  // For states we use the AlternativeStateNameMap to offer canonicalized state
+  // names.
+  absl::optional<std::u16string> GetCanonicalizedValue() const override;
 };
 
 // Stores the postal code of an address.
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
index 54d5276b..ed64141 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
@@ -9,7 +9,6 @@
 #include <string>
 #include <utility>
 
-#include "base/feature_list.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
@@ -17,14 +16,10 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_type.h"
-#include "components/autofill/core/browser/data_model/autofill_structured_address_constants.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
 #include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/common/autofill_features.h"
 
-namespace autofill {
-
-namespace structured_address {
+namespace autofill::structured_address {
 
 bool IsLessSignificantVerificationStatus(VerificationStatus left,
                                          VerificationStatus right) {
@@ -217,6 +212,10 @@
   return base::EmptyString16();
 }
 
+absl::optional<std::u16string> AddressComponent::GetCanonicalizedValue() const {
+  return absl::nullopt;
+}
+
 bool AddressComponent::IsValueAssigned() const {
   return value_.has_value();
 }
@@ -808,8 +807,10 @@
 
 bool AddressComponent::IsMergeableWithComponent(
     const AddressComponent& newer_component) const {
-  const std::u16string value = ValueForComparison(newer_component);
-  const std::u16string value_newer = newer_component.ValueForComparison(*this);
+  const std::u16string older_comparison_value =
+      ValueForComparison(newer_component);
+  const std::u16string newer_comparison_value =
+      newer_component.ValueForComparison(*this);
 
   // If both components are the same, there is nothing to do.
   if (SameAs(newer_component))
@@ -820,18 +821,52 @@
     return true;
   }
 
-  if ((merge_mode_ & kReplaceEmpty) && (value.empty() || value_newer.empty())) {
+  if ((merge_mode_ & kReplaceEmpty) &&
+      (older_comparison_value.empty() || newer_comparison_value.empty())) {
     return true;
   }
 
-  if (merge_mode_ & kUseBetterOrNewerForSameValue) {
-    if (base::ToUpperASCII(value) == base::ToUpperASCII(value_newer)) {
+  SortedTokenComparisonResult token_comparison_result =
+      CompareSortedTokens(older_comparison_value, newer_comparison_value);
+
+  bool comparison_values_are_substrings_of_each_other =
+      (older_comparison_value.find(newer_comparison_value) !=
+           std::u16string::npos ||
+       newer_comparison_value.find(older_comparison_value) !=
+           std::u16string::npos);
+
+  if (merge_mode_ & kMergeBasedOnCanonicalizedValues) {
+    absl::optional<std::u16string> older_canonical_value =
+        GetCanonicalizedValue();
+    absl::optional<std::u16string> newer_canonical_value =
+        newer_component.GetCanonicalizedValue();
+
+    bool older_has_canonical_value = older_canonical_value.has_value();
+    bool newer_has_canonical_value = newer_canonical_value.has_value();
+
+    // If both have a canonical value and the value is the same, they are
+    // obviously mergeable.
+    if (older_has_canonical_value && newer_has_canonical_value &&
+        older_canonical_value == newer_canonical_value) {
+      return true;
+    }
+
+    // If one value does not have canonicalized representation but the actual
+    // values are substrings of each other, or the tokens contain each other we
+    // will merge by just using the one with the canonicalized name.
+    if (older_has_canonical_value != newer_has_canonical_value &&
+        (comparison_values_are_substrings_of_each_other ||
+         token_comparison_result.ContainEachOther())) {
       return true;
     }
   }
 
-  SortedTokenComparisonResult token_comparison_result =
-      CompareSortedTokens(value, value_newer);
+  if (merge_mode_ & kUseBetterOrNewerForSameValue) {
+    if (base::ToUpperASCII(older_comparison_value) ==
+        base::ToUpperASCII(newer_comparison_value)) {
+      return true;
+    }
+  }
 
   if ((merge_mode_ & (kRecursivelyMergeTokenEquivalentValues |
                       kRecursivelyMergeSingleTokenSubset)) &&
@@ -859,8 +894,7 @@
   // If the one value is a substring of the other, use the substring of the
   // corresponding mode is active.
   if ((merge_mode_ & kUseMostRecentSubstring) &&
-      (value.find(value_newer) != std::u16string::npos ||
-       value_newer.find(value) != std::u16string::npos)) {
+      comparison_values_are_substrings_of_each_other) {
     return true;
   }
 
@@ -893,6 +927,16 @@
 
   const std::u16string value = ValueForComparison(newer_component);
   const std::u16string value_newer = newer_component.ValueForComparison(*this);
+
+  bool newer_component_has_better_or_equal_status =
+      !IsLessSignificantVerificationStatus(
+          newer_component.GetVerificationStatus(), GetVerificationStatus());
+  bool components_have_the_same_status =
+      GetVerificationStatus() == newer_component.GetVerificationStatus();
+  bool newer_component_has_better_status =
+      newer_component_has_better_or_equal_status &&
+      !components_have_the_same_status;
+
   if (SameAs(newer_component))
     return true;
 
@@ -933,8 +977,7 @@
   // Replace the subset with the superset if the corresponding mode is active.
   if ((merge_mode_ & kReplaceSubset) && token_comparison_result.OneIsSubset()) {
     if (token_comparison_result.status == SUBSET &&
-        !IsLessSignificantVerificationStatus(
-            newer_component.GetVerificationStatus(), GetVerificationStatus())) {
+        newer_component_has_better_or_equal_status) {
       CopyFrom(newer_component);
     }
     return true;
@@ -952,8 +995,7 @@
   if ((merge_mode_ & (kReplaceSuperset | kReplaceSubset)) &&
       token_comparison_result.status == MATCH) {
     if (newer_was_more_recently_used &&
-        !IsLessSignificantVerificationStatus(
-            newer_component.GetVerificationStatus(), GetVerificationStatus())) {
+        newer_component_has_better_or_equal_status) {
       CopyFrom(newer_component);
     }
     return true;
@@ -988,12 +1030,59 @@
       (value.find(value_newer) != std::u16string::npos ||
        value_newer.find(value) != std::u16string::npos)) {
     if (newer_was_more_recently_used &&
-        !IsLessSignificantVerificationStatus(
-            newer_component.GetVerificationStatus(), GetVerificationStatus()))
+        newer_component_has_better_or_equal_status) {
       CopyFrom(newer_component);
+    }
     return true;
   }
 
+  bool comparison_values_are_substrings_of_each_other =
+      (value.find(value_newer) != std::u16string::npos ||
+       value_newer.find(value) != std::u16string::npos);
+
+  if (merge_mode_ & kMergeBasedOnCanonicalizedValues) {
+    absl::optional<std::u16string> canonical_value = GetCanonicalizedValue();
+    absl::optional<std::u16string> other_canonical_value =
+        newer_component.GetCanonicalizedValue();
+
+    bool this_has_canonical_value = canonical_value.has_value();
+    bool newer_has_canonical_value = other_canonical_value.has_value();
+
+    // When both have the same canonical value they are obviously mergeable.
+    if (canonical_value.has_value() && other_canonical_value.has_value() &&
+        *canonical_value == *other_canonical_value) {
+      // If the newer component has a better verification status use the newer
+      // one.
+      if (newer_component_has_better_status) {
+        CopyFrom(newer_component);
+      }
+      // If they have the same status use the shorter one.
+      if (components_have_the_same_status &&
+          newer_component.GetValue().size() <= GetValue().size()) {
+        CopyFrom(newer_component);
+      }
+      return true;
+    }
+
+    // If only one component has a canonicalized name but the actual values
+    // contain each other either tokens-wise or as substrings, use the component
+    // that has a canonicalized name unless the other component has a better
+    // verification status.
+    if (this_has_canonical_value != newer_has_canonical_value &&
+        (comparison_values_are_substrings_of_each_other ||
+         token_comparison_result.ContainEachOther())) {
+      // Copy the new component if it has a canoniscalized name and a status
+      // that is not worse of it if has a better stastus even if it is not
+      // canoniscalized.
+      if ((!this_has_canonical_value &&
+           newer_component_has_better_or_equal_status) ||
+          (this_has_canonical_value && newer_component_has_better_status)) {
+        CopyFrom(newer_component);
+      }
+      return true;
+    }
+  }
+
   if ((merge_mode_ & kPickShorterIfOneContainsTheOther) &&
       token_comparison_result.ContainEachOther()) {
     if (newer_component.GetValue().size() <= GetValue().size() &&
@@ -1307,6 +1396,4 @@
   return NormalizedValue();
 }
 
-}  // namespace structured_address
-
-}  // namespace autofill
+}  // namespace autofill::structured_address
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.h b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
index 5da2898..15e4ddf 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
@@ -84,15 +84,17 @@
   kRecursivelyMergeSingleTokenSubset = 1 << 6,
   // If one is a substring of the other use the most recent one.
   kUseMostRecentSubstring = 1 << 7,
-  // Merge the child nodes and reformat the node from its children after merge
-  // if the value has changed.
+  // If the tokens match or one is a subset of the other, pick the shorter one.
   kPickShorterIfOneContainsTheOther = 1 << 8,
   // If the normalized values are different, use the better one in terms
   // of verification score or the most recent one if both scores are the same.
   kUseBetterOrMostRecentIfDifferent = 1 << 9,
-  // Defines the default merging behavior.
+  // Merge the child nodes and reformat the node from its children after merge
+  // if the value has changed.
   kMergeChildrenAndReformatIfNeeded = 1 << 10,
-  // If the tokens match or one is a subset of the other, pick the shorter one.
+  // Make a merge decision based on canonicalized values.
+  kMergeBasedOnCanonicalizedValues = 1 << 11,
+  // Defines the default merging behavior.
   kDefault = kRecursivelyMergeTokenEquivalentValues
 };
 
@@ -182,6 +184,10 @@
   // assigned, an empty string is returned.
   const std::u16string& GetValue() const;
 
+  // Returns a canonicalized version of the value or absl::nullopt if
+  // canonicalization is not possible or not implemented.
+  virtual absl::optional<std::u16string> GetCanonicalizedValue() const;
+
   // Returns true if the value of this AddressComponent is assigned.
   bool IsValueAssigned() const;
 
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
index ec0fbf78..b37dc25 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
@@ -263,7 +263,7 @@
   TestAtomicFirstNameAddressComponent second_name_first_node_{this};
 };
 
-// Tests the merging of two atomic component with |type|, and vales
+// Tests the merging of two atomic component with |type|, and values
 // |older_values| and |newer_values| respectively, and |merge_modes|.
 // If |is_mergeable| it is expected that the two components are mergeable.
 // If |newer_was_more_recently_used| the newer component was also more recently
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc b/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc
index 9a5b1269..a2bf9284 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc
@@ -12,8 +12,11 @@
 
 #include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
+#include "components/autofill/core/browser/geo/alternative_state_name_map.h"
+#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -639,6 +642,133 @@
   EXPECT_EQ(country2.GetCommonCountryForMerge(country1), u"");
 }
 
+struct MergeStatesWithCanonicalNamesTestCase {
+  std::string older_state;
+  VerificationStatus older_status;
+  std::string newer_state;
+  VerificationStatus newer_status;
+  std::string expectation;
+  bool is_mergeable;
+};
+
+class MergeStatesWithCanonicalNamesTest
+    : public testing::Test,
+      public testing::WithParamInterface<
+          MergeStatesWithCanonicalNamesTestCase> {
+ private:
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(
+        autofill::features::kAutofillUseAlternativeStateNameMap);
+
+    AlternativeStateNameMap::GetInstance()
+        ->ClearAlternativeStateNameMapForTesting();
+
+    autofill::test::PopulateAlternativeStateNameMapForTesting(
+        "XX", "CS",
+        {{.canonical_name = "CanonicalState",
+          .abbreviations = {"AS"},
+          .alternative_names = {"CoolState"}}});
+    autofill::test::PopulateAlternativeStateNameMapForTesting(
+        "XX", "OS",
+        {{.canonical_name = "OtherState",
+          .abbreviations = {"OS"},
+          .alternative_names = {""}}});
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that the correct country for merging structured addresses is computed.
+TEST_P(MergeStatesWithCanonicalNamesTest, MergeTest) {
+  MergeStatesWithCanonicalNamesTestCase test_case = GetParam();
+
+  AddressComponentTestValues older_values = {
+      {.type = ADDRESS_HOME_COUNTRY,
+       .value = "XX",
+       .status = VerificationStatus::kUserVerified},
+      {.type = ADDRESS_HOME_STATE,
+       .value = test_case.older_state,
+       .status = test_case.older_status},
+  };
+
+  AddressComponentTestValues newer_values = {
+      {.type = ADDRESS_HOME_COUNTRY,
+       .value = "XX",
+       .status = VerificationStatus::kUserVerified},
+      {.type = ADDRESS_HOME_STATE,
+       .value = test_case.newer_state,
+       .status = test_case.newer_status},
+  };
+
+  // In the expectations it is already assumed that the higher
+  // verification status should always win.
+  AddressComponentTestValues expectation_values = {
+      {.type = ADDRESS_HOME_COUNTRY,
+       .value = "XX",
+       .status = VerificationStatus::kUserVerified},
+      {.type = ADDRESS_HOME_STATE,
+       .value = test_case.expectation,
+       .status = IsLessSignificantVerificationStatus(test_case.older_status,
+                                                     test_case.newer_status)
+                     ? test_case.newer_status
+                     : test_case.older_status},
+  };
+
+  Address older_address;
+  SetTestValues(&older_address, older_values);
+
+  Address newer_address;
+  SetTestValues(&newer_address, newer_values);
+
+  EXPECT_EQ(test_case.is_mergeable,
+            older_address.IsMergeableWithComponent(newer_address));
+
+  Address expectation_address;
+  SetTestValues(&expectation_address, expectation_values);
+
+  older_address.MergeWithComponent(newer_address);
+  EXPECT_TRUE(older_address.SameAs(expectation_address));
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    AutofillStructuredAddress,
+    MergeStatesWithCanonicalNamesTest,
+    ::testing::Values(
+
+        // Both have the same canonical name but the older one has the better
+        // status and should win in the merge.
+        MergeStatesWithCanonicalNamesTestCase{
+            "CanonicalState", VerificationStatus::kUserVerified, "CoolState",
+            VerificationStatus::kParsed, "CanonicalState", true},
+
+        // Both have the same canonical name but the newer one has the better
+        // status and should win in the merge.
+        MergeStatesWithCanonicalNamesTestCase{
+            "CanonicalState", VerificationStatus::kObserved, "CoolState",
+            VerificationStatus::kUserVerified, "CoolState", true},
+
+        // The newer one has no canonical name but the value is a substring of
+        // the older one. The older has a higher status and should win.
+        MergeStatesWithCanonicalNamesTestCase{
+            "CanonicalState", VerificationStatus::kUserVerified, "state",
+            VerificationStatus::kParsed, "CanonicalState", true},
+
+        // The other way round: Now the old one remains because it is a
+        // substring and has the better status.
+        MergeStatesWithCanonicalNamesTestCase{
+            "state", VerificationStatus::kUserVerified, "CanonicalState",
+            VerificationStatus::kParsed, "state", true},
+
+        // Those two are not mergeable but both have a canonical name.
+        MergeStatesWithCanonicalNamesTestCase{
+            "CanonicalState", VerificationStatus::kUserVerified, "OtherState",
+            VerificationStatus::kParsed, "CanonicalState", false},
+
+        // Here the newer one does not have a canonical test.
+        MergeStatesWithCanonicalNamesTestCase{
+            "CanonicalState", VerificationStatus::kUserVerified, "Random",
+            VerificationStatus::kParsed, "CanonicalState", false}));
+
 }  // namespace
 }  // namespace structured_address
 }  // namespace autofill
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
index 2e0d0e7..7af7266 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
@@ -12,6 +12,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -125,6 +126,11 @@
         return mRoundingOutline.getRadiusForTesting();
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public @NonNull TextView getTitleView() {
+        return mTitleView;
+    }
+
     @Override
     public void setSelected(boolean isSelected) {
         super.setSelected(isSelected);
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index e7766acd..5c50c76 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -182,6 +182,9 @@
 const char kBidiStreamDetectBrokenConnection[] =
     "bidi_stream_detect_broken_connection";
 
+const char kUseDnsHttpsSvcbFieldTrialName[] = "UseDnsHttpsSvcb";
+const char kUseDnsHttpsSvcbUseAlpn[] = "use_alpn";
+
 // "goaway_sessions_on_ip_change" is default on for iOS unless overridden via
 // experimental options explicitly.
 #if BUILDFLAG(IS_IOS)
@@ -407,6 +410,7 @@
   bool nel_enable = false;
   bool is_network_bound =
       bound_network != net::NetworkChangeNotifier::kInvalidNetworkHandle;
+  absl::optional<net::HostResolver::HttpsSvcbOptions> https_svcb_options;
 
   StaleHostResolver::StaleOptions stale_dns_options;
   const std::string* host_resolver_rules_string;
@@ -678,6 +682,18 @@
       host_resolver_rules_string =
           host_resolver_rules_args.FindString(kHostResolverRules);
       host_resolver_rules_enable = !!host_resolver_rules_string;
+    } else if (iter->first == kUseDnsHttpsSvcbFieldTrialName) {
+      if (!iter->second.is_dict()) {
+        LOG(ERROR) << "\"" << iter->first << "\" config params \""
+                   << iter->second << "\" is not a dictionary value";
+        effective_experimental_options.Remove(iter->first);
+        continue;
+      }
+      const base::Value::Dict& args = iter->second.GetDict();
+      https_svcb_options = net::HostResolver::HttpsSvcbOptions::FromDict(args);
+      session_params->use_dns_https_svcb_alpn =
+          args.FindBool(kUseDnsHttpsSvcbUseAlpn)
+              .value_or(session_params->use_dns_https_svcb_alpn);
     } else if (iter->first == kNetworkErrorLoggingFieldTrialName) {
       if (!iter->second.is_dict()) {
         LOG(ERROR) << "\"" << iter->first << "\" config params \""
@@ -759,11 +775,14 @@
   }
 
   if (async_dns_enable || stale_dns_enable || host_resolver_rules_enable ||
-      disable_ipv6_on_wifi || is_network_bound) {
+      disable_ipv6_on_wifi || is_network_bound || https_svcb_options) {
     net::HostResolver::ManagerOptions host_resolver_manager_options;
     host_resolver_manager_options.insecure_dns_client_enabled =
         async_dns_enable;
     host_resolver_manager_options.check_ipv6_on_wifi = !disable_ipv6_on_wifi;
+    if (https_svcb_options) {
+      host_resolver_manager_options.https_svcb_options = https_svcb_options;
+    }
 
     if (!is_network_bound) {
       std::unique_ptr<net::HostResolver> host_resolver;
diff --git a/components/cronet/url_request_context_config_unittest.cc b/components/cronet/url_request_context_config_unittest.cc
index cb3dfd4..0aacb91 100644
--- a/components/cronet/url_request_context_config_unittest.cc
+++ b/components/cronet/url_request_context_config_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/json/json_writer.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
@@ -17,6 +18,7 @@
 #include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "net/base/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/http_user_agent_settings.h"
 #include "net/base/net_errors.h"
@@ -308,6 +310,35 @@
   EXPECT_TRUE(config->network_thread_priority);
   EXPECT_EQ(42.0, config->network_thread_priority.value());
   EXPECT_FALSE(config->bidi_stream_detect_broken_connection);
+
+  // When UseDnsHttpsSvcb option is not set, the value of net::features are
+  // used.
+  const net::HostResolver::HttpsSvcbOptions& https_svcb_options =
+      context->host_resolver()
+          ->GetManagerForTesting()
+          ->https_svcb_options_for_testing();
+  EXPECT_EQ(base::FeatureList::IsEnabled(net::features::kUseDnsHttpsSvcb),
+            https_svcb_options.enable);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbEnableInsecure.Get(),
+            https_svcb_options.enable_insecure);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbInsecureExtraTimeMax.Get(),
+            https_svcb_options.insecure_extra_time_max);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbInsecureExtraTimePercent.Get(),
+            https_svcb_options.insecure_extra_time_percent);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbInsecureExtraTimeMin.Get(),
+            https_svcb_options.insecure_extra_time_min);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbSecureExtraTimeMax.Get(),
+            https_svcb_options.secure_extra_time_max);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbSecureExtraTimePercent.Get(),
+            https_svcb_options.secure_extra_time_percent);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbSecureExtraTimeMin.Get(),
+            https_svcb_options.secure_extra_time_min);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbExtraTimeAbsolute.Get(),
+            https_svcb_options.extra_time_absolute);
+  EXPECT_EQ(net::features::kUseDnsHttpsSvcbExtraTimePercent.Get(),
+            https_svcb_options.extra_time_percent);
+  EXPECT_EQ(base::FeatureList::IsEnabled(net::features::kUseDnsHttpsSvcbAlpn),
+            params->use_dns_https_svcb_alpn);
 }
 
 TEST(URLRequestContextConfigTest, SetSupportedQuicVersion) {
@@ -1717,6 +1748,82 @@
       "bidi_stream_detect_broken_connection"));
 }
 
+TEST(URLRequestContextConfigTest, HttpsSvcbOptions) {
+  base::test::TaskEnvironment task_environment_(
+      base::test::TaskEnvironment::MainThreadType::IO);
+  std::unique_ptr<URLRequestContextConfig> config =
+      URLRequestContextConfig::CreateURLRequestContextConfig(
+          // Enable QUIC.
+          true,
+          // QUIC User Agent ID.
+          "Default QUIC User Agent ID",
+          // Enable SPDY.
+          true,
+          // Enable Brotli.
+          false,
+          // Type of http cache.
+          URLRequestContextConfig::HttpCacheType::DISK,
+          // Max size of http cache in bytes.
+          1024000,
+          // Disable caching for HTTP responses. Other information may be stored
+          // in the cache.
+          false,
+          // Storage path for http cache and cookie storage.
+          "/data/data/org.chromium.net/app_cronet_test/test_storage",
+          // Accept-Language request header field.
+          "foreign-language",
+          // User-Agent request header field.
+          "fake agent",
+          // JSON encoded experimental options.
+          "{\"UseDnsHttpsSvcb\":{\"enable\":true,"
+          "\"enable_insecure\":true,"
+          "\"insecure_extra_time_max\":\"1ms\","
+          "\"insecure_extra_time_percent\":2,"
+          "\"insecure_extra_time_min\":\"3ms\","
+          "\"secure_extra_time_max\":\"4ms\","
+          "\"secure_extra_time_percent\":5,"
+          "\"secure_extra_time_min\":\"6ms\","
+          "\"extra_time_absolute\":\"7ms\","
+          "\"extra_time_percent\":8,"
+          "\"use_alpn\":true"
+          "}}",
+          // MockCertVerifier to use for testing purposes.
+          std::unique_ptr<net::CertVerifier>(),
+          // Enable network quality estimator.
+          false,
+          // Enable Public Key Pinning bypass for local trust anchors.
+          true,
+          // Optional network thread priority.
+          absl::optional<double>());
+
+  net::URLRequestContextBuilder builder;
+  config->ConfigureURLRequestContextBuilder(&builder);
+  // Set a ProxyConfigService to avoid DCHECK failure when building.
+  builder.set_proxy_config_service(
+      std::make_unique<net::ProxyConfigServiceFixed>(
+          net::ProxyConfigWithAnnotation::CreateDirect()));
+  std::unique_ptr<net::URLRequestContext> context(builder.Build());
+
+  const net::HostResolver::HttpsSvcbOptions& https_svcb_options =
+      context->host_resolver()
+          ->GetManagerForTesting()
+          ->https_svcb_options_for_testing();
+  EXPECT_TRUE(https_svcb_options.enable);
+  EXPECT_TRUE(https_svcb_options.enable_insecure);
+  EXPECT_EQ(base::Milliseconds(1), https_svcb_options.insecure_extra_time_max);
+  EXPECT_EQ(2, https_svcb_options.insecure_extra_time_percent);
+  EXPECT_EQ(base::Milliseconds(3), https_svcb_options.insecure_extra_time_min);
+  EXPECT_EQ(base::Milliseconds(4), https_svcb_options.secure_extra_time_max);
+  EXPECT_EQ(5, https_svcb_options.secure_extra_time_percent);
+  EXPECT_EQ(base::Milliseconds(6), https_svcb_options.secure_extra_time_min);
+  EXPECT_EQ(base::Milliseconds(7), https_svcb_options.extra_time_absolute);
+  EXPECT_EQ(8, https_svcb_options.extra_time_percent);
+
+  const net::HttpNetworkSessionParams* params =
+      context->GetNetworkSessionParams();
+  EXPECT_TRUE(params->use_dns_https_svcb_alpn);
+}
+
 // See stale_host_resolver_unittest.cc for test of StaleDNS options.
 
 }  // namespace cronet
diff --git a/components/embedder_support/user_agent_utils.cc b/components/embedder_support/user_agent_utils.cc
index 6eb48476..97ddfb06 100644
--- a/components/embedder_support/user_agent_utils.cc
+++ b/components/embedder_support/user_agent_utils.cc
@@ -193,9 +193,21 @@
 // reducing the user agent platform and oscpu.
 bool ShouldReduceUserAgentPlatformOsCpu(
     UserAgentReductionEnterprisePolicyState user_agent_reduction) {
+// For legacy windows, only reduce the user agent platform and oscpu when
+// kLegacyWindowsPlatform parameter set to true.
+#if BUILDFLAG(IS_WIN)
+  if (base::win::GetVersion() < base::win::Version::WIN10) {
+    return ShouldReduceUserAgentMinorVersion(user_agent_reduction) &&
+           base::FeatureList::IsEnabled(
+               blink::features::kReduceUserAgentPlatformOsCpu) &&
+           blink::features::kLegacyWindowsPlatform.Get();
+  }
+#endif
+
   return ShouldReduceUserAgentMinorVersion(user_agent_reduction) &&
          base::FeatureList::IsEnabled(
-             blink::features::kReduceUserAgentPlatformOsCpu);
+             blink::features::kReduceUserAgentPlatformOsCpu) &&
+         blink::features::kAllExceptLegacyWindowsPlatform.Get();
 }
 #endif
 
diff --git a/components/embedder_support/user_agent_utils_unittest.cc b/components/embedder_support/user_agent_utils_unittest.cc
index dcc5af1..236c22ef 100644
--- a/components/embedder_support/user_agent_utils_unittest.cc
+++ b/components/embedder_support/user_agent_utils_unittest.cc
@@ -666,6 +666,36 @@
               GetUserAgent());
   }
 
+  // Verify disable reduce legacy windows platform
+  scoped_feature_list.Reset();
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      {{blink::features::kReduceUserAgentMinorVersion, {}},
+       {blink::features::kReduceUserAgentPlatformOsCpu,
+        {{"all_except_legacy_windows_platform", "true"},
+         {"legacy_windows_platform", "false"}}}},
+      {});
+  {
+#if BUILDFLAG(IS_WIN)
+    if (base::win::GetVersion() < base::win::Version::WIN10) {
+      EXPECT_NE(base::StringPrintf(
+                    kDesktop, version_info::GetMajorVersionNumber().c_str()),
+                GetUserAgent());
+      EXPECT_NE("Windows NT 10.0; Win64; x64",
+                GetUserAgentPlatformOsCpu(GetUserAgent()));
+    } else {
+      EXPECT_EQ(base::StringPrintf(
+                    kDesktop, version_info::GetMajorVersionNumber().c_str()),
+                GetUserAgent());
+      EXPECT_EQ("Windows NT 10.0; Win64; x64",
+                GetUserAgentPlatformOsCpu(GetUserAgent()));
+    }
+#else
+    EXPECT_EQ(base::StringPrintf(kDesktop,
+                                 version_info::GetMajorVersionNumber().c_str()),
+              GetUserAgent());
+#endif
+  }
+
   // Verify that the reduced user agent string respects
   // --force-major-version-to-minor
   scoped_feature_list.Reset();
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index 9b69aa2..d31a731b 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -769,6 +769,8 @@
     private boolean preferToShowIntentPicker(ExternalNavigationParams params,
             boolean isExternalProtocol, boolean incomingIntentRedirect,
             boolean intentMatchesNonDefaultWebApk) {
+        if (RedirectHandler.isRefactoringEnabled()) return true;
+
         int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK;
         boolean isFormSubmit = pageTransitionCore == PageTransition.FORM_SUBMIT;
 
@@ -796,11 +798,9 @@
             return false;
         }
 
-        if (!RedirectHandler.isRefactoringEnabled()) {
-            if (isFormSubmit && !incomingIntentRedirect && !isRedirectFromFormSubmit) {
-                if (DEBUG) Log.i(TAG, "Direct form submission, not a redirect");
-                return false;
-            }
+        if (isFormSubmit && !incomingIntentRedirect && !isRedirectFromFormSubmit) {
+            if (DEBUG) Log.i(TAG, "Direct form submission, not a redirect");
+            return false;
         }
 
         // http://crbug/331571 : Do not override a navigation started from user typing.
@@ -1007,6 +1007,19 @@
             return true;
         }
 
+        // TODO(https://crbug.com/1346731): We only need to check isFromTyping because WebLayer's
+        // implementation of disabling intent processing is broken and doesn't actually disable
+        // intent processing, but to align with current weblayer behavior the first navigation has
+        // to be blocked even if the weblayer delegate tells us not to block embedder initiated
+        // navigations. See
+        // https://source.chromium.org/chromium/chromium/src/+/main:weblayer/browser/navigation_controller_impl.cc;drc=88d7b2e74349cbf8b3e15b61cc0663d65f9d1873;l=220
+        if (!initialState.isRendererInitiated && !initialState.isFromIntent
+                && (mDelegate.shouldEmbedderInitiatedNavigationsStayInBrowser()
+                        || initialState.isFromTyping)) {
+            if (DEBUG) Log.i(TAG, "Browser initiated navigation chain.");
+            return true;
+        }
+
         // If the intent targets the calling app, we can bypass the gesture requirements and any
         // signals from the initial intent that suggested the intent wanted to stay in Chrome.
         if (mDelegate.isIntentForTrustedCallingApp(targetIntent, resolvingInfos)) return false;
@@ -1051,6 +1064,26 @@
         return false;
     }
 
+    /*
+     * The initial navigation from an Intent should always stay in the browser as the sending app,
+     * or the user must have chosen the browser to do the navigation.
+     */
+    private boolean isDirectIntentNavigation(ExternalNavigationParams params,
+            boolean intentMatchesNonDefaultWebApk, boolean incomingIntentRedirect) {
+        if (!RedirectHandler.isRefactoringEnabled()) return false;
+
+        // S+ workaround for WebAPKs not being able to handle Intents.
+        if (intentMatchesNonDefaultWebApk) return false;
+
+        if (!params.isFromIntent()) return false;
+
+        // Redirects off of intents are still allowed to launch apps (eg. URL shorteners).
+        if (incomingIntentRedirect) return false;
+
+        if (DEBUG) Log.i(TAG, "Initial intent navigation.");
+        return true;
+    }
+
     /**
      * If the intent can't be resolved, we should fall back to the browserFallbackUrl, or try to
      * find the app on the market if no fallback is provided.
@@ -1604,6 +1637,10 @@
 
         boolean intentMatchesNonDefaultWebApk =
                 intentMatchesNonDefaultWebApk(params, resolvingInfos);
+        if (isDirectIntentNavigation(
+                    params, intentMatchesNonDefaultWebApk, incomingIntentRedirect)) {
+            return OverrideUrlLoadingResult.forNoOverride();
+        }
 
         boolean requiresPromptForExternalIntent = isNavigationChainExpired(params)
                 || redirectShouldStayInApp(params, isExternalProtocol, targetIntent, resolvingInfos)
@@ -2188,7 +2225,7 @@
                 OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH);
     }
 
-    private OverrideUrlLoadingResult maybeAskToLaunchApp(boolean isExternalProtocol,
+    protected OverrideUrlLoadingResult maybeAskToLaunchApp(boolean isExternalProtocol,
             Intent targetIntent, QueryIntentActivitiesSupplier resolvingInfos,
             ResolveActivitySupplier resolveActivity, GURL browserFallbackUrl) {
         // For URLs the browser supports, we shouldn't have reached here.
@@ -2203,6 +2240,7 @@
 
         // No app can resolve the intent, don't prompt.
         if (intentResolveInfo == null || intentResolveInfo.activityInfo == null) {
+            if (DEBUG) Log.i(TAG, "Message doesn't resolve to any app.");
             return OverrideUrlLoadingResult.forNoOverride();
         }
 
@@ -2214,6 +2252,7 @@
         // to block the navigation, and sites hoping to prompt the user when navigation fails should
         // make sure to correctly target their app.
         if (!resolversSubsetOf(Arrays.asList(intentResolveInfo), resolvingInfos.get())) {
+            if (DEBUG) Log.i(TAG, "Message resolves to multiple apps.");
             return OverrideUrlLoadingResult.forNoOverride();
         }
 
@@ -2221,6 +2260,7 @@
                 MessageDispatcherProvider.from(mDelegate.getWindowAndroid());
         WebContents webContents = mDelegate.getWebContents();
         if (messageDispatcher == null || webContents == null) {
+            if (DEBUG) Log.i(TAG, "No WebContents to show Message for.");
             return OverrideUrlLoadingResult.forNoOverride();
         }
 
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
index 87a55bc..c5718f7 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
@@ -349,6 +349,13 @@
     }
 
     /**
+     * @return whether we're on the first load in the current navigation chain.
+     */
+    public boolean isOnFirstLoadInNavigationChain() {
+        return mNavigationChainState.mIsOnFirstLoadInChain;
+    }
+
+    /**
      * @param hasExternalProtocol whether the destination URI has an external protocol or not.
      * @param isForTrustedCallingApp whether the app we would launch to is trusted and what launched
      *                               Chrome.
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index d2aaac4..1cc8b8c 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -415,17 +415,26 @@
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
                         START_OTHER_ACTIVITY);
 
+        RedirectHandler redirectHandler = RedirectHandler.create();
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
+
         // http://crbug.com/143118 - Don't show the picker for directly typed URLs, unless
         // the URL results in a redirect.
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(PageTransition.TYPED)
                 .withIsRendererInitiated(false)
+                .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
+        redirectHandler = RedirectHandler.create();
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.RELOAD, false, false, 0, 0, false, false);
+
         // http://crbug.com/162106 - Don't show the picker on reload.
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(PageTransition.RELOAD)
                 .withIsRendererInitiated(false)
+                .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
     }
 
@@ -490,6 +499,8 @@
     @Test
     @SmallTest
     public void testTypedRedirectToExternalProtocol() {
+        if (RedirectHandler.isRefactoringEnabled()) return;
+
         // http://crbug.com/169549
         checkUrl("market://1234")
                 .withPageTransition(PageTransition.TYPED)
@@ -507,10 +518,54 @@
                         START_OTHER_ACTIVITY);
 
         // http://crbug.com/143118
+        mUrlHandler.mExpectingMessage = true;
         checkUrl("market://1234")
                 .withPageTransition(PageTransition.TYPED)
                 .withIsRendererInitiated(false)
-                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION,
+                        OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH, IGNORE);
+    }
+
+    @Test
+    @SmallTest
+    public void testTypedRedirectToExternalProtocolWithRefactor() {
+        if (!RedirectHandler.isRefactoringEnabled()) return;
+        mUrlHandler.mExpectingMessage = true;
+
+        RedirectHandler redirectHandler = RedirectHandler.create();
+
+        // http://crbug.com/169549
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, true, false, 0, 0, false, false);
+        checkUrl("market://1234")
+                .withPageTransition(PageTransition.TYPED)
+                .withIsRendererInitiated(false)
+                .withIsRedirect(true)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION,
+                        OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH, IGNORE);
+
+        // http://crbug.com/709217
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.FROM_ADDRESS_BAR, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.FROM_ADDRESS_BAR, true, false, 0, 0, false, false);
+        checkUrl("market://1234")
+                .withPageTransition(PageTransition.FROM_ADDRESS_BAR)
+                .withIsRendererInitiated(false)
+                .withIsRedirect(true)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION,
+                        OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH, IGNORE);
+
+        // If a user types an external protocol, it may as well ask to leave Chrome.
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
+        checkUrl("market://1234")
+                .withPageTransition(PageTransition.TYPED)
+                .withIsRendererInitiated(false)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION,
+                        OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH, IGNORE);
     }
 
     @Test
@@ -2567,15 +2622,20 @@
     @SmallTest
     public void testEmbedderInitiatedNavigationsLeaveBrowser() {
         mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME));
+        RedirectHandler redirectHandler = RedirectHandler.create();
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.AUTO_BOOKMARK, false, false, 0, 0, false, false);
 
         checkUrl(YOUTUBE_URL)
                 .withIsRendererInitiated(false)
+                .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
         mDelegate.setShouldEmbedderInitiatedNavigationsStayInBrowser(false);
 
         checkUrl(YOUTUBE_URL)
                 .withIsRendererInitiated(false)
+                .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
                         START_OTHER_ACTIVITY);
     }
@@ -2805,6 +2865,7 @@
         public Intent mStartActivityIntent;
         public boolean mCalledWithProxy;
         private boolean mSendIntentsForReal;
+        public boolean mExpectingMessage;
 
         public ExternalNavigationHandlerForTesting(ExternalNavigationDelegate delegate) {
             super(delegate);
@@ -2905,6 +2966,19 @@
             mCalledWithProxy = false;
             mStartActivityIntent = null;
         }
+
+        @Override
+        protected OverrideUrlLoadingResult maybeAskToLaunchApp(boolean isExternalProtocol,
+                Intent targetIntent, QueryIntentActivitiesSupplier resolvingInfos,
+                ResolveActivitySupplier resolveActivity, GURL browserFallbackUrl) {
+            if (!browserFallbackUrl.isEmpty() || !isExternalProtocol) {
+                return super.maybeAskToLaunchApp(isExternalProtocol, targetIntent, resolvingInfos,
+                        resolveActivity, browserFallbackUrl);
+            }
+            Assert.assertTrue(mExpectingMessage);
+            return OverrideUrlLoadingResult.forAsyncAction(
+                    OverrideUrlLoadingAsyncActionType.UI_GATING_INTENT_LAUNCH);
+        }
     };
 
     private static class TestExternalNavigationDelegate implements ExternalNavigationDelegate {
diff --git a/components/feed/core/v2/api_test/feed_api_notice_card_unittest.cc b/components/feed/core/v2/api_test/feed_api_notice_card_unittest.cc
index 20774f3f..3faa35b 100644
--- a/components/feed/core/v2/api_test/feed_api_notice_card_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_notice_card_unittest.cc
@@ -52,7 +52,8 @@
   task_environment_.FastForwardBy(base::Hours(1));
   stream_->ReportSliceViewed(surface.GetSurfaceId(), surface.GetStreamType(),
                              slice_id);
-  stream_->ReportOpenAction(GURL(), surface.GetStreamType(), slice_id);
+  stream_->ReportOpenAction(GURL(), surface.GetStreamType(), slice_id,
+                            OpenActionType::kDefault);
 
   response_translator_.InjectResponse(model_generator_.MakeFirstPage());
   stream_->UnloadModel(surface.GetStreamType());
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
index 44830a1..bf88ad6 100644
--- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -1128,14 +1128,32 @@
 
   base::UserActionTester user_actions;
 
-  stream_->ReportOpenInNewTabAction(
+  stream_->ReportOpenAction(
       GURL(), surface.GetStreamType(),
-      surface.initial_state->updated_slices(1).slice().slice_id());
+      surface.initial_state->updated_slices(1).slice().slice_id(),
+      OpenActionType::kNewTab);
 
   EXPECT_EQ(1, user_actions.GetActionCount(
                    "ContentSuggestions.Feed.CardAction.OpenInNewTab"));
 }
 
+TEST_F(FeedApiTest, ReportOpenInNewTabInGroupAction) {
+  store_->OverwriteStream(kForYouStream, MakeTypicalInitialModelState(),
+                          base::DoNothing());
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+
+  base::UserActionTester user_actions;
+
+  stream_->ReportOpenAction(
+      GURL(), surface.GetStreamType(),
+      surface.initial_state->updated_slices(1).slice().slice_id(),
+      OpenActionType::kNewTabInGroup);
+
+  EXPECT_EQ(1, user_actions.GetActionCount(
+                   "ContentSuggestions.Feed.CardAction.OpenInNewTabInGroup"));
+}
+
 TEST_F(FeedApiTest, HasUnreadContentAfterLoadFromNetwork) {
   response_translator_.InjectResponse(MakeTypicalInitialModelState());
   TestUnreadContentObserver observer;
@@ -2637,8 +2655,10 @@
   EXPECT_FALSE(stream_->WasUrlRecentlyNavigatedFromFeed(url1));
   EXPECT_FALSE(stream_->WasUrlRecentlyNavigatedFromFeed(url2));
 
-  stream_->ReportOpenAction(url1, kForYouStream, "slice");
-  stream_->ReportOpenInNewTabAction(url2, kForYouStream, "slice");
+  stream_->ReportOpenAction(url1, kForYouStream, "slice",
+                            OpenActionType::kDefault);
+  stream_->ReportOpenAction(url2, kForYouStream, "slice",
+                            OpenActionType::kNewTab);
 
   EXPECT_TRUE(stream_->WasUrlRecentlyNavigatedFromFeed(url1));
   EXPECT_TRUE(stream_->WasUrlRecentlyNavigatedFromFeed(url2));
@@ -2651,7 +2671,8 @@
     urls.emplace_back("https://someurl" + base::NumberToString(i));
 
   for (const GURL& url : urls)
-    stream_->ReportOpenAction(url, kForYouStream, "slice");
+    stream_->ReportOpenAction(url, kForYouStream, "slice",
+                              OpenActionType::kDefault);
 
   EXPECT_FALSE(stream_->WasUrlRecentlyNavigatedFromFeed(urls[0]));
   for (size_t i = 1; i < urls.size(); ++i) {
@@ -3344,7 +3365,8 @@
   WaitForIdleTaskQueue();
 
   // Opening should cause a refresh to be scheduled.
-  stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "");
+  stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "",
+                            OpenActionType::kDefault);
   EXPECT_EQ(base::Minutes(30),
             refresh_scheduler_
                 .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
@@ -3361,8 +3383,8 @@
   WaitForIdleTaskQueue();
 
   // Should cause a refresh to be scheduled.
-  stream_->ReportOpenInNewTabAction(GURL("http://example.com"), kForYouStream,
-                                    "");
+  stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "",
+                            OpenActionType::kNewTab);
   EXPECT_EQ(base::Minutes(30),
             refresh_scheduler_
                 .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
@@ -3547,7 +3569,8 @@
   WaitForIdleTaskQueue();
 
   // Opening should cause a refresh to be scheduled.
-  stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "");
+  stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "",
+                            OpenActionType::kDefault);
   EXPECT_EQ(base::Minutes(30),
             refresh_scheduler_
                 .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index 633ec74..8ecc0b0 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -1256,7 +1256,8 @@
 
 void FeedStream::ReportOpenAction(const GURL& url,
                                   const StreamType& stream_type,
-                                  const std::string& slice_id) {
+                                  const std::string& slice_id,
+                                  OpenActionType action_type) {
   recent_feed_navigations_.insert(recent_feed_navigations_.begin(), url);
   recent_feed_navigations_.resize(
       std::min(kMaxRecentFeedNavigations, recent_feed_navigations_.size()));
@@ -1266,7 +1267,7 @@
   int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id);
   if (index < 0)
     index = MetricsReporter::kUnknownCardIndex;
-  metrics_reporter_->OpenAction(stream_type, index);
+  metrics_reporter_->OpenAction(stream_type, index, action_type);
 
   if (stream.model) {
     privacy_notice_card_tracker_.OnOpenAction(
@@ -1274,28 +1275,10 @@
   }
   ScheduleFeedCloseRefreshOnInteraction(stream_type);
 }
+
 void FeedStream::ReportOpenVisitComplete(base::TimeDelta visit_time) {
   metrics_reporter_->OpenVisitComplete(visit_time);
 }
-void FeedStream::ReportOpenInNewTabAction(const GURL& url,
-                                          const StreamType& stream_type,
-                                          const std::string& slice_id) {
-  recent_feed_navigations_.insert(recent_feed_navigations_.begin(), url);
-  recent_feed_navigations_.resize(
-      std::min(kMaxRecentFeedNavigations, recent_feed_navigations_.size()));
-
-  Stream& stream = GetStream(stream_type);
-  int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id);
-  if (index < 0)
-    index = MetricsReporter::kUnknownCardIndex;
-  metrics_reporter_->OpenInNewTabAction(stream_type, index);
-
-  if (stream.model) {
-    privacy_notice_card_tracker_.OnOpenAction(
-        stream.model->FindContentId(ToContentRevision(slice_id)));
-  }
-  ScheduleFeedCloseRefreshOnInteraction(stream_type);
-}
 
 void FeedStream::ReportSliceViewed(SurfaceId surface_id,
                                    const StreamType& stream_type,
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h
index 149ca04..4f5bbf5 100644
--- a/components/feed/core/v2/feed_stream.h
+++ b/components/feed/core/v2/feed_stream.h
@@ -159,11 +159,9 @@
   void ReportPageLoaded() override;
   void ReportOpenAction(const GURL& url,
                         const StreamType& stream_type,
-                        const std::string& slice_id) override;
+                        const std::string& slice_id,
+                        OpenActionType action_type) override;
   void ReportOpenVisitComplete(base::TimeDelta visit_time) override;
-  void ReportOpenInNewTabAction(const GURL& url,
-                                const StreamType& stream_type,
-                                const std::string& slice_id) override;
   void ReportStreamScrolled(const StreamType& stream_type,
                             int distance_dp) override;
   void ReportStreamScrollStart() override;
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc
index 3423d41..ba0da4c 100644
--- a/components/feed/core/v2/metrics_reporter.cc
+++ b/components/feed/core/v2/metrics_reporter.cc
@@ -465,11 +465,26 @@
 }
 
 void MetricsReporter::OpenAction(const StreamType& stream_type,
-                                 int index_in_stream) {
+                                 int index_in_stream,
+                                 OpenActionType action_type) {
   CardOpenBegin(stream_type);
-  ReportUserActionHistogram(FeedUserActionType::kTappedOnCard);
-  base::RecordAction(
-      base::UserMetricsAction("ContentSuggestions.Feed.CardAction.Open"));
+  switch (action_type) {
+    case OpenActionType::kDefault:
+      ReportUserActionHistogram(FeedUserActionType::kTappedOnCard);
+      base::RecordAction(
+          base::UserMetricsAction("ContentSuggestions.Feed.CardAction.Open"));
+      break;
+    case OpenActionType::kNewTab:
+      ReportUserActionHistogram(FeedUserActionType::kTappedOpenInNewTab);
+      base::RecordAction(base::UserMetricsAction(
+          "ContentSuggestions.Feed.CardAction.OpenInNewTab"));
+      break;
+    case OpenActionType::kNewTabInGroup:
+      ReportUserActionHistogram(FeedUserActionType::kTappedOpenInNewTabInGroup);
+      base::RecordAction(base::UserMetricsAction(
+          "ContentSuggestions.Feed.CardAction.OpenInNewTabInGroup"));
+      break;
+  }
   ReportContentSuggestionsOpened(stream_type, index_in_stream);
   RecordInteraction(stream_type);
 }
@@ -479,16 +494,6 @@
                               visit_time);
 }
 
-void MetricsReporter::OpenInNewTabAction(const StreamType& stream_type,
-                                         int index_in_stream) {
-  CardOpenBegin(stream_type);
-  ReportUserActionHistogram(FeedUserActionType::kTappedOpenInNewTab);
-  base::RecordAction(base::UserMetricsAction(
-      "ContentSuggestions.Feed.CardAction.OpenInNewTab"));
-  ReportContentSuggestionsOpened(stream_type, index_in_stream);
-  RecordInteraction(stream_type);
-}
-
 void MetricsReporter::PageLoaded() {
   ReportCardOpenEndIfNeeded(true);
 }
@@ -500,15 +505,13 @@
   ReportUserActionHistogram(action_type);
   switch (action_type) {
     case FeedUserActionType::kTappedOnCard:
+    case FeedUserActionType::kTappedOpenInNewTab:
+    case FeedUserActionType::kTappedOpenInNewTabInGroup:
       DCHECK(false) << "This should be reported with OpenAction() instead";
       break;
     case FeedUserActionType::kShownCard_DEPRECATED:
       DCHECK(false) << "deprecated";
       break;
-    case FeedUserActionType::kTappedOpenInNewTab:
-      DCHECK(false)
-          << "This should be reported with OpenInNewTabAction() instead";
-      break;
     case FeedUserActionType::kOpenedFeedSurface:
       DCHECK(false) << "This should be reported with SurfaceOpened() instead";
       break;
diff --git a/components/feed/core/v2/metrics_reporter.h b/components/feed/core/v2/metrics_reporter.h
index 21cf62d..54322128 100644
--- a/components/feed/core/v2/metrics_reporter.h
+++ b/components/feed/core/v2/metrics_reporter.h
@@ -66,9 +66,10 @@
                                   int index_in_stream,
                                   int stream_slice_count);
   void FeedViewed(SurfaceId surface_id);
-  void OpenAction(const StreamType& stream_type, int index_in_stream);
+  void OpenAction(const StreamType& stream_type,
+                  int index_in_stream,
+                  OpenActionType action_type);
   void OpenVisitComplete(base::TimeDelta visit_time);
-  void OpenInNewTabAction(const StreamType& stream_type, int index_in_stream);
   void PageLoaded();
   void OtherUserAction(const StreamType& stream_type,
                        FeedUserActionType action_type);
diff --git a/components/feed/core/v2/metrics_reporter_unittest.cc b/components/feed/core/v2/metrics_reporter_unittest.cc
index a2c3613..62871f3 100644
--- a/components/feed/core/v2/metrics_reporter_unittest.cc
+++ b/components/feed/core/v2/metrics_reporter_unittest.cc
@@ -159,7 +159,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpeningContentIsInteracting) {
-  reporter_->OpenAction(kForYouStream, 5);
+  reporter_->OpenAction(kForYouStream, 5, OpenActionType::kDefault);
 
   std::map<FeedEngagementType, int> want({
       {FeedEngagementType::kFeedEngaged, 1},
@@ -212,7 +212,7 @@
 TEST_F(MetricsReporterTest, VisitsCanLastMoreThanFiveMinutes) {
   reporter_->StreamScrolled(kForYouStream, 1);
   task_environment_.FastForwardBy(base::Minutes(5) - kEpsilon);
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Minutes(5) - kEpsilon);
   reporter_->StreamScrolled(kForYouStream, 1);
 
@@ -227,10 +227,10 @@
 }
 
 TEST_F(MetricsReporterTest, NewVisitAfterInactivity) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   reporter_->StreamScrolled(kForYouStream, 1);
   task_environment_.FastForwardBy(base::Minutes(5) + kEpsilon);
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   reporter_->StreamScrolled(kForYouStream, 1);
 
   std::map<FeedEngagementType, int> want({
@@ -245,9 +245,9 @@
 
 TEST_F(MetricsReporterTest, InteractedWithBothFeeds) {
   reporter_->StreamScrolled(kForYouStream, 1);
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   reporter_->StreamScrolled(kWebFeedStream, 1);
-  reporter_->OpenAction(kWebFeedStream, 0);
+  reporter_->OpenAction(kWebFeedStream, 0, OpenActionType::kDefault);
 
   std::map<FeedEngagementType, int> want_1({
       {FeedEngagementType::kFeedEngaged, 1},
@@ -278,7 +278,7 @@
       1);
 
   task_environment_.FastForwardBy(base::Minutes(5) + kEpsilon);
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   reporter_->StreamScrolled(kForYouStream, 1);
 
   EXPECT_EQ(want_1, ReportedEngagementType(kWebFeedStream));
@@ -576,7 +576,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenAction) {
-  reporter_->OpenAction(kForYouStream, 5);
+  reporter_->OpenAction(kForYouStream, 5, OpenActionType::kDefault);
 
   std::map<FeedEngagementType, int> want({
       {FeedEngagementType::kFeedEngaged, 1},
@@ -603,7 +603,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenActionWebFeed) {
-  reporter_->OpenAction(kWebFeedStream, 5);
+  reporter_->OpenAction(kWebFeedStream, 5, OpenActionType::kDefault);
 
   std::map<FeedEngagementType, int> want({
       {FeedEngagementType::kFeedEngaged, 1},
@@ -631,7 +631,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenInNewTabAction) {
-  reporter_->OpenInNewTabAction(kForYouStream, 5);
+  reporter_->OpenAction(kForYouStream, 5, OpenActionType::kNewTab);
 
   std::map<FeedEngagementType, int> want({
       {FeedEngagementType::kFeedEngaged, 1},
@@ -647,6 +647,24 @@
   histogram_.ExpectUniqueSample("NewTabPage.ContentSuggestions.Opened", 5, 1);
 }
 
+TEST_F(MetricsReporterTest, OpenInNewTabInGroupAction) {
+  reporter_->OpenAction(kForYouStream, 5, OpenActionType::kNewTabInGroup);
+
+  std::map<FeedEngagementType, int> want({
+      {FeedEngagementType::kFeedEngaged, 1},
+      {FeedEngagementType::kFeedInteracted, 1},
+      {FeedEngagementType::kFeedEngagedSimple, 1},
+  });
+  EXPECT_EQ(want, ReportedEngagementType(kForYouStream));
+  EXPECT_EQ(want, ReportedEngagementType(kCombinedStreams));
+  EXPECT_EQ(1, user_actions_.GetActionCount(
+                   "ContentSuggestions.Feed.CardAction.OpenInNewTabInGroup"));
+  histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserActions",
+                                FeedUserActionType::kTappedOpenInNewTabInGroup,
+                                1);
+  histogram_.ExpectUniqueSample("NewTabPage.ContentSuggestions.Opened", 5, 1);
+}
+
 TEST_F(MetricsReporterTest, OpenInNewIncognitoTabAction) {
   reporter_->OtherUserAction(kForYouStream,
                              FeedUserActionType::kTappedOpenInNewIncognitoTab);
@@ -860,7 +878,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenCardSuccessDuration) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(19));
   reporter_->PageLoaded();
 
@@ -870,7 +888,7 @@
 }
 
 TEST_F(MetricsReporterTest, WebFeed_OpenCardSuccessDuration) {
-  reporter_->OpenAction(kWebFeedStream, 0);
+  reporter_->OpenAction(kWebFeedStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(19));
   reporter_->PageLoaded();
 
@@ -880,7 +898,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenCardTimeout) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(21));
   reporter_->PageLoaded();
 
@@ -891,7 +909,7 @@
 }
 
 TEST_F(MetricsReporterTest, WebFeed_OpenCardTimeout) {
-  reporter_->OpenAction(kWebFeedStream, 0);
+  reporter_->OpenAction(kWebFeedStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(21));
   reporter_->PageLoaded();
 
@@ -903,9 +921,9 @@
 }
 
 TEST_F(MetricsReporterTest, OpenCardFailureTwiceAndThenSucceed) {
-  reporter_->OpenAction(kForYouStream, 0);
-  reporter_->OpenAction(kForYouStream, 1);
-  reporter_->OpenAction(kForYouStream, 2);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
+  reporter_->OpenAction(kForYouStream, 1, OpenActionType::kDefault);
+  reporter_->OpenAction(kForYouStream, 2, OpenActionType::kDefault);
   reporter_->PageLoaded();
 
   histogram_.ExpectUniqueSample(
@@ -915,9 +933,9 @@
 }
 
 TEST_F(MetricsReporterTest, WebFeed_OpenCardFailureTwiceAndThenSucceed) {
-  reporter_->OpenAction(kWebFeedStream, 0);
-  reporter_->OpenAction(kWebFeedStream, 1);
-  reporter_->OpenAction(kWebFeedStream, 2);
+  reporter_->OpenAction(kWebFeedStream, 0, OpenActionType::kDefault);
+  reporter_->OpenAction(kWebFeedStream, 1, OpenActionType::kDefault);
+  reporter_->OpenAction(kWebFeedStream, 2, OpenActionType::kDefault);
   reporter_->PageLoaded();
 
   histogram_.ExpectUniqueSample(
@@ -928,7 +946,7 @@
 }
 
 TEST_F(MetricsReporterTest, OpenCardCloseChromeFailure) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   reporter_->OnEnterBackground();
 
   histogram_.ExpectUniqueSample(
@@ -938,11 +956,11 @@
 }
 
 TEST_F(MetricsReporterTest, TimeSpentInFeedCountsOnlyForegroundTime) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(1));
   reporter_->OnEnterBackground();
   task_environment_.FastForwardBy(base::Seconds(2));
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(3));
   reporter_->OnEnterBackground();
 
@@ -955,7 +973,7 @@
 }
 
 TEST_F(MetricsReporterTest, TimeSpentInFeedLimitsIdleTime) {
-  reporter_->OpenAction(kForYouStream, 0);
+  reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
   task_environment_.FastForwardBy(base::Seconds(31));
   reporter_->OnEnterBackground();
 
@@ -972,7 +990,7 @@
   // interaction due to the interaction timeout. The 49th |OpenAction()| call
   // triggers reporting the UMA for the previous day.
   for (int i = 0; i < 49; ++i) {
-    reporter_->OpenAction(kForYouStream, 0);
+    reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
     task_environment_.FastForwardBy(base::Hours(1));
   }
 
@@ -985,7 +1003,7 @@
   // destroyed and recreated. The 49th |OpenAction()| call triggers reporting
   // the UMA for the previous day.
   for (int i = 0; i < 49; ++i) {
-    reporter_->OpenAction(kForYouStream, 0);
+    reporter_->OpenAction(kForYouStream, 0, OpenActionType::kDefault);
     task_environment_.FastForwardBy(base::Hours(1));
     reporter_->OnEnterBackground();
     RecreateMetricsReporter();
diff --git a/components/feed/core/v2/public/common_enums.cc b/components/feed/core/v2/public/common_enums.cc
index 5eef401..71872c1d 100644
--- a/components/feed/core/v2/public/common_enums.cc
+++ b/components/feed/core/v2/public/common_enums.cc
@@ -126,6 +126,8 @@
       return out << "kFirstFollowSheetTappedGotIt";
     case FeedUserActionType::kFollowRecommendationIPHShown:
       return out << "kFollowRecommendationIPHShown";
+    case FeedUserActionType::kTappedOpenInNewTabInGroup:
+      return out << "kTappedOpenInNewTabInGroup";
   }
 }
 
diff --git a/components/feed/core/v2/public/common_enums.h b/components/feed/core/v2/public/common_enums.h
index 3d74f37..04aa37c1 100644
--- a/components/feed/core/v2/public/common_enums.h
+++ b/components/feed/core/v2/public/common_enums.h
@@ -158,8 +158,10 @@
   // Page load caused a Follow Recommendation IPH to be shown. User action not
   // reported here. iOS only.
   kFollowRecommendationIPHShown = 57,
+  // User opened the article in a new tab in group from the back of card menu.
+  kTappedOpenInNewTabInGroup = 58,
 
-  kMaxValue = kFollowRecommendationIPHShown,
+  kMaxValue = kTappedOpenInNewTabInGroup,
 };
 
 // For testing and debugging only.
diff --git a/components/feed/core/v2/public/feed_api.h b/components/feed/core/v2/public/feed_api.h
index 0fee014..1d33715 100644
--- a/components/feed/core/v2/public/feed_api.h
+++ b/components/feed/core/v2/public/feed_api.h
@@ -168,20 +168,18 @@
                                 SurfaceId surface_id) = 0;
   // A web page was loaded in response to opening a link from the Feed.
   virtual void ReportPageLoaded() = 0;
-  // The user triggered the default open action, usually by tapping the card.
+  // The user triggered the open action which can be caused by tapping the card,
+  // or selecting 'Open in new tab' menu item. The open action to trigger is
+  // providen in `action_type`.
   // Remembers the URL for later calls to `WasUrlRecentlyNavigatedFromFeed()`.
   virtual void ReportOpenAction(const GURL& url,
                                 const StreamType& stream_type,
-                                const std::string& slice_id) = 0;
+                                const std::string& slice_id,
+                                OpenActionType action_type) = 0;
   // The user triggered an open action, visited a web page, and then navigated
   // away or backgrouded the tab. |visit_time| is a measure of how long the
   // visited page was foregrounded.
   virtual void ReportOpenVisitComplete(base::TimeDelta visit_time) = 0;
-  // The user triggered the 'open in new tab' action.
-  // Remembers the URL for later calls to `WasUrlRecentlyNavigatedFromFeed()`.
-  virtual void ReportOpenInNewTabAction(const GURL& url,
-                                        const StreamType& stream_type,
-                                        const std::string& slice_id) = 0;
   // The user scrolled the feed by |distance_dp| and then stopped.
   virtual void ReportStreamScrolled(const StreamType& stream_type,
                                     int distance_dp) = 0;
diff --git a/components/feed/core/v2/public/test/stub_feed_api.h b/components/feed/core/v2/public/test/stub_feed_api.h
index 46ba9e7..69fa6e9 100644
--- a/components/feed/core/v2/public/test/stub_feed_api.h
+++ b/components/feed/core/v2/public/test/stub_feed_api.h
@@ -80,11 +80,9 @@
   void ReportPageLoaded() override {}
   void ReportOpenAction(const GURL& url,
                         const StreamType& stream_type,
-                        const std::string& slice_id) override {}
+                        const std::string& slice_id,
+                        OpenActionType action_type) override {}
   void ReportOpenVisitComplete(base::TimeDelta visit_time) override {}
-  void ReportOpenInNewTabAction(const GURL& url,
-                                const StreamType& stream_type,
-                                const std::string& slice_id) override {}
   void ReportStreamScrolled(const StreamType& stream_type,
                             int distance_dp) override {}
   void ReportStreamScrollStart() override {}
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index a5dc2f6..36d230a7 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -255,6 +255,17 @@
 std::ostream& operator<<(std::ostream& out,
                          WebFeedPageInformationRequestReason value);
 
+// Used to tell how to open an URL.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.feed
+enum class OpenActionType : int {
+  // The default open action.
+  kDefault = 0,
+  // "Open in new tab" action.
+  kNewTab = 1,
+  // "Open in new tab in group" action.
+  kNewTabInGroup = 2,
+};
+
 }  // namespace feed
 
 #endif  // COMPONENTS_FEED_CORE_V2_PUBLIC_TYPES_H_
diff --git a/components/nacl/broker/nacl_broker_listener.cc b/components/nacl/broker/nacl_broker_listener.cc
index 6111d75..45b0ae1 100644
--- a/components/nacl/broker/nacl_broker_listener.cc
+++ b/components/nacl/broker/nacl_broker_listener.cc
@@ -29,6 +29,7 @@
 #include "mojo/public/cpp/system/invitation.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "sandbox/policy/mojom/sandbox.mojom.h"
+#include "sandbox/policy/win/sandbox_win.h"
 #include "sandbox/win/src/sandbox_policy.h"
 
 #include <windows.h>
@@ -58,6 +59,11 @@
   return sandbox::mojom::Sandbox::kPpapi;
 }
 
+std::string NaClBrokerListener::GetSandboxTag() {
+  return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+      "nacl-broker-listener", GetSandboxType());
+}
+
 void NaClBrokerListener::OnChannelConnected(int32_t peer_pid) {
   browser_process_ = base::Process::OpenWithExtraPrivileges(peer_pid);
   CHECK(browser_process_.IsValid());
diff --git a/components/nacl/broker/nacl_broker_listener.h b/components/nacl/broker/nacl_broker_listener.h
index 6da43b59..f821257 100644
--- a/components/nacl/broker/nacl_broker_listener.h
+++ b/components/nacl/broker/nacl_broker_listener.h
@@ -35,6 +35,7 @@
 
   // content::SandboxedProcessLauncherDelegate implementation:
   sandbox::mojom::Sandbox GetSandboxType() override;
+  std::string GetSandboxTag() override;
 
   // IPC::Listener implementation.
   void OnChannelConnected(int32_t peer_pid) override;
diff --git a/components/nacl/browser/nacl_broker_host_win.cc b/components/nacl/browser/nacl_broker_host_win.cc
index e944754..95a9efb9d 100644
--- a/components/nacl/browser/nacl_broker_host_win.cc
+++ b/components/nacl/browser/nacl_broker_host_win.cc
@@ -40,6 +40,12 @@
   sandbox::mojom::Sandbox GetSandboxType() override {
     return sandbox::mojom::Sandbox::kNoSandbox;
   }
+
+  std::string GetSandboxTag() override {
+    // kNoSandbox does not use a TargetPolicy, if the sandbox type is changed
+    // then provide a unique tag here.
+    return "";
+  }
 };
 }  // namespace
 
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc
index 3763d11..802efd6 100644
--- a/components/nacl/browser/nacl_process_host.cc
+++ b/components/nacl/browser/nacl_process_host.cc
@@ -85,6 +85,7 @@
 #include "base/win/windows_version.h"
 #include "components/nacl/browser/nacl_broker_service_win.h"
 #include "components/nacl/common/nacl_debug_exception_handler_win.h"
+#include "sandbox/policy/win/sandbox_win.h"
 #endif
 
 using content::BrowserThread;
@@ -181,6 +182,11 @@
     }
   }
 
+  std::string GetSandboxTag() override {
+    return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+        "nacl-process-host", GetSandboxType());
+  }
+
   bool CetCompatible() override {
     // Disable CET for NaCl loader processes as x86 NaCl sandboxes are not CET
     // compatible. NaCl untrusted code is allowed to switch stacks within the
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index bcccd23..e4817680 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -31,6 +31,7 @@
     "observers/prerender_page_load_metrics_observer.cc",
     "observers/prerender_page_load_metrics_observer.h",
     "observers/use_counter/ukm_features.cc",
+    "observers/use_counter/webdev_metrics_ukm_features.cc",
     "observers/use_counter_page_load_metrics_observer.cc",
     "observers/use_counter_page_load_metrics_observer.h",
     "page_load_metrics_embedder_base.cc",
diff --git a/components/page_load_metrics/browser/observers/use_counter/OWNERS b/components/page_load_metrics/browser/observers/use_counter/OWNERS
index ac7cb0b..ea0ffd1 100644
--- a/components/page_load_metrics/browser/observers/use_counter/OWNERS
+++ b/components/page_load_metrics/browser/observers/use_counter/OWNERS
@@ -2,3 +2,7 @@
 # Must satisfy UKM privacy requirements.
 per-file ukm_features.*=file://tools/metrics/ukm/PRIVACY_OWNERS
 per-file ukm_features.*=set noparent
+per-file webdev_metrics_ukm_features.*=file://tools/metrics/ukm/PRIVACY_OWNERS
+per-file webdev_metrics_ukm_features.*=schenney@chromium.org
+per-file webdev_metrics_ukm_features.*=dgrogan@chromium.org
+per-file webdev_metrics_ukm_features.*=set noparent
diff --git a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index e3ce030..cc0f000 100644
--- a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -15,6 +15,13 @@
 // data shows a behaviour that is rare but too common to blindly change.
 // UKM-based UseCounter would allow us to find specific pages to reason about
 // whether a breaking change is acceptable or not.
+//
+// This event is emitted for every page load and is not sub-sampled by the UKM
+// system. The WebFeature::kPageVisits is always present in the event, so
+// it is valid to take the ratio of events with your feature to kPageVisit to
+// estimate "percentage of page views using a feature". Note that the lack of
+// sub-sampling is the reason why this event must only be used for rare
+// features.
 
 using WebFeature = blink::mojom::WebFeature;
 
diff --git a/components/page_load_metrics/browser/observers/use_counter/webdev_metrics_ukm_features.cc b/components/page_load_metrics/browser/observers/use_counter/webdev_metrics_ukm_features.cc
new file mode 100644
index 0000000..bbc3182b
--- /dev/null
+++ b/components/page_load_metrics/browser/observers/use_counter/webdev_metrics_ukm_features.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
+
+#include "base/no_destructor.h"
+
+// This file defines a list of UseCounter WebFeature measured in the
+// Web Developer Metric UKM UseCounter. Features must all satisfy UKM
+// privacy requirements (see go/ukm). In addition, features should only be added
+// if the expected frequency is less than 5% of page loads.
+//
+// The Web Developer Metrics UKM UseCounter is intended for features that are
+// launching or may be subject to spec revisions. The UKM data is used to tie
+// feature usage by a site to performance, devtools usage or other aspects of
+// the site's structure. The event may also be used for investigations into
+// features with unknown or unexpected UseCounter data in order to identify
+// sites for further attention.
+//
+// Do NOT use this list for features that are being considered for removal or
+// deprecation where percentage of page views or URLs is important. This event
+// may be subsampled by the UKM system (some proportion of events may be dropped
+// before being uploaded from the client) in order to balance the resources of
+// the UKM system at large. As a result, any feature usage counts or
+// percentages aggregated across all page views or URLs will be inaccurate.
+// For such cases use the Blink.UseCounter allow list found in
+// components/page_load_metrics/browser/observers/use_counter/ukm_features.cc.
+
+using WebFeature = blink::mojom::WebFeature;
+
+// UKM-based UseCounter features (WebFeature) should be defined in
+// opt_in_features list.
+const UseCounterPageLoadMetricsObserver::UkmFeatureList&
+UseCounterPageLoadMetricsObserver::GetAllowedWebDevMetricsUkmFeatures() {
+  static base::NoDestructor<UseCounterPageLoadMetricsObserver::UkmFeatureList>
+      // We explicitly use an std::initializer_list below to work around GCC
+      // bug 84849, which causes having a base::NoDestructor<T<U>> and passing
+      // an initializer list of Us does not work.
+      opt_in_features(std::initializer_list<WebFeature>(
+          {WebFeature::kCSSCascadeLayers,
+           WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody,
+           WebFeature::kFontBuilderCSSFontFamilyWebKitPrefixBody,
+           WebFeature::kScrollTimelineConstructor,
+           WebFeature::kCSSAtRuleScrollTimeline}));
+  return *opt_in_features;
+}
diff --git a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
index 225db50..da669e0 100644
--- a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
@@ -88,6 +88,7 @@
   DCHECK_EQ(features_recorded_.count(), 0ul);
   DCHECK_EQ(main_frame_features_recorded_.count(), 0ul);
   DCHECK_EQ(ukm_features_recorded_.count(), 0ul);
+  DCHECK_EQ(webdev_metrics_ukm_features_recorded_.count(), 0ul);
   DCHECK_EQ(css_properties_recorded_.count(), 0ul);
   DCHECK_EQ(animated_css_properties_recorded_.count(), 0ul);
   DCHECK_EQ(violated_permissions_policy_features_recorded_.count(), 0ul);
@@ -275,4 +276,20 @@
             main_frame_features_recorded_.test(feature_enum_value))
         .Record(ukm::UkmRecorder::Get());
   }
+  for (WebFeature web_feature : GetAllowedWebDevMetricsUkmFeatures()) {
+    auto feature_enum_value =
+        static_cast<blink::UseCounterFeature::EnumValue>(web_feature);
+    if (!features_recorded_.test(feature_enum_value))
+      continue;
+
+    if (TestAndSet(webdev_metrics_ukm_features_recorded_, feature_enum_value))
+      continue;
+
+    ukm::builders::Blink_DeveloperMetricsRare(
+        GetDelegate().GetPageUkmSourceId())
+        .SetFeature(feature_enum_value)
+        .SetIsMainFrameFeature(
+            main_frame_features_recorded_.test(feature_enum_value))
+        .Record(ukm::UkmRecorder::Get());
+  }
 }
diff --git a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
index 0d6405e..8f28b5a 100644
--- a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
@@ -55,6 +55,9 @@
   // Returns a list of opt-in UKM features for use counter.
   static const UkmFeatureList& GetAllowedUkmFeatures();
 
+  // Returns a list of opt-in UKM features for the Web Dev Metrics use counter.
+  static const UkmFeatureList& GetAllowedWebDevMetricsUkmFeatures();
+
   // Records an `UseCounterFeature` through UMA_HISTOGRAM_ENUMERATION if
   // the feature has not been recorded before.
   void RecordUseCounterFeature(content::RenderFrameHost*,
@@ -83,6 +86,8 @@
       animated_css_properties_recorded_;
   std::bitset<static_cast<size_t>(blink::mojom::WebFeature::kNumberOfFeatures)>
       ukm_features_recorded_;
+  std::bitset<static_cast<size_t>(blink::mojom::WebFeature::kNumberOfFeatures)>
+      webdev_metrics_ukm_features_recorded_;
   std::bitset<static_cast<size_t>(
                   blink::mojom::PermissionsPolicyFeature::kMaxValue) +
               1>
diff --git a/content/BUILD.gn b/content/BUILD.gn
index 62467fb..dc8fafb 100644
--- a/content/BUILD.gn
+++ b/content/BUILD.gn
@@ -69,7 +69,7 @@
   "//content/public/utility:utility_sources",
 ]
 
-if (enable_plugins) {
+if (enable_ppapi) {
   content_shared_components += [ "//content/ppapi_plugin:ppapi_plugin_sources" ]
 }
 
diff --git a/content/app/BUILD.gn b/content/app/BUILD.gn
index 0061cf5..e31c615 100644
--- a/content/app/BUILD.gn
+++ b/content/app/BUILD.gn
@@ -52,7 +52,7 @@
   ]
 }
 
-if (enable_plugins) {
+if (enable_ppapi) {
   content_app_deps += [ "//content/ppapi_plugin:ppapi_plugin_sources" ]
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e7351dc8..797b066 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2689,7 +2689,8 @@
     ]
   }
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "../public/browser/plugin_service.h",
       "media/session/pepper_playback_observer.cc",
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 9b29ab96d..f4f51ae 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -364,6 +364,11 @@
 #if BUILDFLAG(IS_WIN)
   bool DisableDefaultPolicy() override { return true; }
 
+  std::string GetSandboxTag() override {
+    return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+        "gpu", GetSandboxType());
+  }
+
   enum GPUAppContainerEnableState{
       AC_ENABLED = 0,
       AC_DISABLED_GL = 1,
diff --git a/content/browser/hid/hid_browsertest.cc b/content/browser/hid/hid_browsertest.cc
index 7c96fd0..6624579d89 100644
--- a/content/browser/hid/hid_browsertest.cc
+++ b/content/browser/hid/hid_browsertest.cc
@@ -20,7 +20,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/fenced_frame_test_util.h"
 #include "content/shell/browser/shell.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/hid/hid_service_unittest.cc b/content/browser/hid/hid_service_unittest.cc
index ae689105..ee2759b 100644
--- a/content/browser/hid/hid_service_unittest.cc
+++ b/content/browser/hid/hid_service_unittest.cc
@@ -25,9 +25,9 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "services/device/hid/test_report_descriptors.h"
-#include "services/device/hid/test_util.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
+#include "services/device/public/cpp/test/hid_test_util.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/hid/hid.mojom.h"
diff --git a/content/browser/loader/navigation_early_hints_manager.cc b/content/browser/loader/navigation_early_hints_manager.cc
index 4f39e857..325fd8c 100644
--- a/content/browser/loader/navigation_early_hints_manager.cc
+++ b/content/browser/loader/navigation_early_hints_manager.cc
@@ -31,6 +31,7 @@
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/blink/public/common/loader/network_utils.h"
 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@@ -553,6 +554,8 @@
   request.mode = CalculateRequestMode(link);
   request.credentials_mode = CalculateCredentialsMode(link);
 
+  blink::network_utils::SetAcceptHeader(request.headers, request.destination);
+
   std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
       CreateContentBrowserURLLoaderThrottles(
           request, &browser_context_,
diff --git a/content/browser/loader/navigation_early_hints_manager_unittest.cc b/content/browser/loader/navigation_early_hints_manager_unittest.cc
index 0e905a3..a05de05 100644
--- a/content/browser/loader/navigation_early_hints_manager_unittest.cc
+++ b/content/browser/loader/navigation_early_hints_manager_unittest.cc
@@ -17,6 +17,8 @@
 #include "content/public/test/test_storage_partition.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/http/http_request_headers.h"
+#include "services/network/public/cpp/constants.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/early_hints.mojom.h"
@@ -176,6 +178,14 @@
   loader_factory().AddResponse(GURL(kPreloadPath), std::move(head),
                                kPreloadBody, status);
 
+  loader_factory().SetInterceptor(base::BindLambdaForTesting(
+      [&](const network::ResourceRequest& resource_request) {
+        std::string accept_value;
+        ASSERT_TRUE(resource_request.headers.GetHeader(
+            net::HttpRequestHeaders::kAccept, &accept_value));
+        EXPECT_EQ(accept_value, network::kDefaultAcceptHeaderValue);
+      }));
+
   early_hints_manager().HandleEarlyHints(CreateEarlyHintWithPreload(),
                                          CreateNavigationResourceRequest());
 
diff --git a/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.cc b/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.cc
index 6e1d729..04c23a4 100644
--- a/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.cc
+++ b/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.cc
@@ -20,6 +20,11 @@
 
 namespace content {
 #if BUILDFLAG(IS_WIN)
+std::string PpapiPluginSandboxedProcessLauncherDelegate::GetSandboxTag() {
+  return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+      "ppapi", GetSandboxType());
+}
+
 bool PpapiPluginSandboxedProcessLauncherDelegate::PreSpawnTarget(
     sandbox::TargetPolicy* policy) {
   // The Pepper process is as locked-down as a renderer except that it can
diff --git a/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.h b/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.h
index 7cbf14c..680a572f 100644
--- a/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.h
+++ b/content/browser/ppapi_plugin_sandboxed_process_launcher_delegate.h
@@ -29,6 +29,7 @@
   ~PpapiPluginSandboxedProcessLauncherDelegate() override = default;
 
 #if BUILDFLAG(IS_WIN)
+  std::string GetSandboxTag() override;
   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override;
 #endif  // BUILDFLAG(IS_WIN)
 
diff --git a/content/browser/renderer_host/frame_tree_browsertest.cc b/content/browser/renderer_host/frame_tree_browsertest.cc
index bc0063b3..15d2f90 100644
--- a/content/browser/renderer_host/frame_tree_browsertest.cc
+++ b/content/browser/renderer_host/frame_tree_browsertest.cc
@@ -3503,6 +3503,9 @@
   // First, we navigate the fenced frame to a new URL.
   // Second, we call reportEvent and validate the results.
   struct Step {
+    // Whether the navigation should target a nested iframe rather than the
+    // fenced frame root.
+    bool is_target_nested_iframe = false;
     // Whether the navigation should be embedder-initiated or fenced-frame
     // initiated.
     bool is_embedder_initiated = false;
@@ -3510,18 +3513,18 @@
     // (This should always be false when `!is_embedder_initiated`.
     bool is_opaque = false;
 
-    struct Target {
+    struct Destination {
       // The origin for the navigation.
       std::string origin;
       // The path for the resource to load.
       std::string path;
     };
 
-    // The initial navigation target (may be redirected).
-    Target target;
+    // The initial navigation destination (may be redirected).
+    Destination destination;
     // A list of redirects that the navigation should take. The last redirect
-    // target will be the ultimate destination of the navigation.
-    std::vector<Target> redirects;
+    // destination will be the ultimate destination of the navigation.
+    std::vector<Destination> redirects;
 
     // Whether the reportEvent should succeed.
     bool should_have_metadata = false;
@@ -3550,19 +3553,23 @@
     {
       std::set<std::string> paths;
       for (auto& step : steps) {
-        ASSERT_FALSE(step.target.origin.empty());
-        ASSERT_FALSE(step.target.path.empty());
+        if (step.is_target_nested_iframe) {
+          ASSERT_FALSE(step.is_embedder_initiated);
+          ASSERT_FALSE(step.is_opaque);
+        }
+        ASSERT_FALSE(step.destination.origin.empty());
+        ASSERT_FALSE(step.destination.path.empty());
         int redirect_index = 0;
-        for (auto& redirect_target : step.redirects) {
-          ASSERT_TRUE(paths.find(redirect_target.path) == paths.end());
-          ASSERT_FALSE(redirect_target.origin.empty());
-          ASSERT_FALSE(redirect_target.path.empty());
-          paths.insert(redirect_target.path);
+        for (auto& redirect_destination : step.redirects) {
+          ASSERT_TRUE(paths.find(redirect_destination.path) == paths.end());
+          ASSERT_FALSE(redirect_destination.origin.empty());
+          ASSERT_FALSE(redirect_destination.path.empty());
+          paths.insert(redirect_destination.path);
 
-          // Intercept the previous navigation target in the chain.
+          // Intercept the previous navigation destination in the chain.
           std::string previous_path =
               redirect_index ? step.redirects[redirect_index - 1].path
-                             : step.target.path;
+                             : step.destination.path;
           redirects.emplace_back(
               std::make_unique<net::test_server::ControllableHttpResponse>(
                   https_server(), previous_path));
@@ -3599,13 +3606,16 @@
     FencedFrameURLMapping& url_mapping =
         root->current_frame_host()->GetPage().fenced_frame_urls_map();
 
+    // Create a holder for a nested iframe.
+    absl::optional<FrameTreeNode*> nested_iframe_node = absl::nullopt;
+
     int navigation_index = 0;
     int response_index = 0;
     int redirect_index = 0;
     for (auto& step : steps) {
       // Configure the navigation.
-      GURL navigate_url =
-          https_server()->GetURL(step.target.origin, step.target.path);
+      GURL navigate_url = https_server()->GetURL(step.destination.origin,
+                                                 step.destination.path);
       GURL expect_url = navigate_url;
       if (step.is_opaque) {
         GURL urn_uuid =
@@ -3613,10 +3623,31 @@
         EXPECT_TRUE(urn_uuid.is_valid());
         navigate_url = urn_uuid;
       }
+      FrameTreeNode* navigation_target_node = fenced_frame_root_node;
+
+      // Add a nested iframe inside the fenced frame if necessary (or clear the
+      // handle to it, if the navigation will remove it).
+      if (step.is_target_nested_iframe) {
+        if (!nested_iframe_node) {
+          EXPECT_TRUE(
+              ExecJs(fenced_frame_root_node,
+                     "var iframe_within_ff = document.createElement('iframe');"
+                     "document.body.appendChild(iframe_within_ff);"));
+          EXPECT_EQ(1U, fenced_frame_root_node->child_count());
+          nested_iframe_node = fenced_frame_root_node->child_at(0);
+        }
+        navigation_target_node = *nested_iframe_node;
+      } else {
+        nested_iframe_node = absl::nullopt;
+      }
 
       // Initiate the navigation.
-      TestFrameNavigationObserver observer(fenced_frame_root_node);
-      if (step.is_embedder_initiated) {
+      TestFrameNavigationObserver observer(navigation_target_node);
+      if (step.is_target_nested_iframe) {
+        EXPECT_TRUE(
+            ExecJs(fenced_frame_root_node,
+                   JsReplace("iframe_within_ff.src = $1", navigate_url)));
+      } else if (step.is_embedder_initiated) {
         EXPECT_TRUE(ExecJs(root, JsReplace("f.src = $1", navigate_url)));
       } else {
         EXPECT_TRUE(ExecJs(fenced_frame_root_node,
@@ -3624,9 +3655,9 @@
       }
 
       // Redirect the navigation if relevant.
-      for (auto& redirect_target : step.redirects) {
-        GURL redirect_url = https_server()->GetURL(redirect_target.origin,
-                                                   redirect_target.path);
+      for (auto& redirect_destination : step.redirects) {
+        GURL redirect_url = https_server()->GetURL(redirect_destination.origin,
+                                                   redirect_destination.path);
         expect_url = redirect_url;
         auto& redirect = *redirects[redirect_index];
         redirect.WaitForRequest();
@@ -3642,9 +3673,9 @@
       observer.WaitForCommit();
       EXPECT_EQ(
           expect_url,
-          fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
+          navigation_target_node->current_frame_host()->GetLastCommittedURL());
       EXPECT_EQ(url::Origin::Create(expect_url),
-                fenced_frame_root_node->current_frame_host()
+                navigation_target_node->current_frame_host()
                     ->GetLastCommittedOrigin());
       navigation_index++;
 
@@ -3656,7 +3687,7 @@
           destination: ['buyer'],
         });
       )";
-      EXPECT_TRUE(ExecJs(fenced_frame_root_node,
+      EXPECT_TRUE(ExecJs(navigation_target_node,
                          JsReplace(report_event_script, navigation_index)));
 
       // If relevant, check that the event report succeeded.
@@ -3689,13 +3720,52 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
   };
   RunTest(config);
 }
 
+// reportEvent should work in same-origin subframes.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventNestedIframeSameOriginNavigation) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .destination = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .is_target_nested_iframe = true,
+          .destination = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
+
+// reportEvent shouldn't work in cross-origin subframes.
+IN_PROC_BROWSER_TEST_P(
+    FencedFrameReportEventBrowserTest,
+    FencedFrameReportEventNestedIframeCrossOriginNavigation) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .destination = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .is_target_nested_iframe = true,
+          .destination = {"b.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = false,
+      },
+  };
+  RunTest(config);
+}
+
 // Reporting metadata should persist across FF-initiated same-origin
 // navigations.
 IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
@@ -3704,11 +3774,11 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
       {
-          .target = {"a.test", "/fenced_frames/title1.html?foo"},
+          .destination = {"a.test", "/fenced_frames/title1.html?foo"},
           .should_have_metadata = true,
       },
   };
@@ -3723,17 +3793,17 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
       {
-          .target = {"b.test", "/fenced_frames/title1.html"},
+          .destination = {"b.test", "/fenced_frames/title1.html"},
           .should_have_metadata = false,
       },
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
   };
@@ -3756,13 +3826,13 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
       {
           .is_embedder_initiated = true,
           .is_opaque = false,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = false,
       },
   };
@@ -3777,7 +3847,7 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .destination = {"a.test", "/fenced_frames/redirect1.html"},
           .redirects =
               {
                   {"a.test", "/fenced_frames/redirect2.html"},
@@ -3797,7 +3867,7 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .destination = {"a.test", "/fenced_frames/redirect1.html"},
           .redirects =
               {
                   {"b.test", "/fenced_frames/redirect2.html"},
@@ -3817,11 +3887,11 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
       {
-          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .destination = {"a.test", "/fenced_frames/redirect1.html"},
           .redirects =
               {
                   {"a.test", "/fenced_frames/redirect2.html"},
@@ -3841,11 +3911,11 @@
       {
           .is_embedder_initiated = true,
           .is_opaque = true,
-          .target = {"a.test", "/fenced_frames/title1.html"},
+          .destination = {"a.test", "/fenced_frames/title1.html"},
           .should_have_metadata = true,
       },
       {
-          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .destination = {"a.test", "/fenced_frames/redirect1.html"},
           .redirects =
               {
                   {"b.test", "/fenced_frames/redirect2.html"},
@@ -3928,70 +3998,6 @@
   EXPECT_EQ(response.http_request()->content, event_data);
 }
 
-IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
-                       NestedIframeReportEvent) {
-  net::test_server::ControllableHttpResponse response(https_server(),
-                                                      "/title2.html");
-  ASSERT_TRUE(https_server()->Start());
-
-  GURL main_url = https_server()->GetURL("b.test", "/hello.html");
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  EXPECT_TRUE(ExecJs(root,
-                     "var f = document.createElement('fencedframe');"
-                     "f.mode = 'opaque-ads';"
-                     "document.body.appendChild(f);"));
-  EXPECT_EQ(1U, root->child_count());
-  FrameTreeNode* fenced_frame_root_node =
-      GetFencedFrameRootNode(root->child_at(0));
-
-  EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
-  EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
-
-  // Add reporting metadata.
-  ReportingMetadata fenced_frame_reporting;
-  GURL reporting_url(https_server()->GetURL("c.test", "/title2.html"));
-  fenced_frame_reporting.metadata[blink::mojom::ReportingDestination::kBuyer]
-                                 ["mouse interaction"] = reporting_url;
-
-  GURL https_url(
-      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
-  FencedFrameURLMapping& url_mapping =
-      root->current_frame_host()->GetPage().fenced_frame_urls_map();
-  GURL urn_uuid =
-      url_mapping.AddFencedFrameURL(https_url, fenced_frame_reporting);
-  EXPECT_TRUE(urn_uuid.is_valid());
-
-  // Navigate the fenced frame.
-  std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid);
-  NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
-      fenced_frame_root_node, urn_uuid, navigate_urn_script);
-
-  // Add a nested iframe inside the fenced frame and navigate.
-  AddIframeInFencedFrame(fenced_frame_root_node, 0);
-  EXPECT_EQ(1U, fenced_frame_root_node->child_count());
-  FrameTreeNode* nested_iframe_node = fenced_frame_root_node->child_at(0);
-
-  GURL iframe_url(
-      https_server()->GetURL("a.test", "/fenced_frames/title0.html"));
-  NavigateIframeInFencedFrame(nested_iframe_node, iframe_url);
-
-  std::string event_data = "this is a click";
-  EXPECT_TRUE(
-      ExecJs(nested_iframe_node, JsReplace("window.fence.reportEvent({"
-                                           "  eventType: 'mouse interaction',"
-                                           "  eventData: $1,"
-                                           "  destination: ['buyer']});",
-                                           event_data)));
-
-  response.WaitForRequest();
-  EXPECT_EQ(response.http_request()->content, event_data);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     All,
     FencedFrameReportEventBrowserTest,
diff --git a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.cc b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.cc
index 8ec0f24f..71bbf3608 100644
--- a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.cc
+++ b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.cc
@@ -80,6 +80,13 @@
   }
 }
 
+std::string RendererSandboxedProcessLauncherDelegateWin::GetSandboxTag() {
+  // PDF renderers may have jit disabled while normal renderers will not.
+  return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+      dynamic_code_can_be_disabled_ ? "renderer-jitless" : "renderer",
+      GetSandboxType());
+}
+
 bool RendererSandboxedProcessLauncherDelegateWin::PreSpawnTarget(
     sandbox::TargetPolicy* policy) {
   sandbox::policy::SandboxWin::AddBaseHandleClosePolicy(policy);
@@ -149,4 +156,4 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.h b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.h
index 3a0e25b..d820cb4 100644
--- a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.h
+++ b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.h
@@ -39,6 +39,8 @@
   RendererSandboxedProcessLauncherDelegateWin(base::CommandLine* cmd_line,
                                               bool is_jit_disabled);
 
+  std::string GetSandboxTag() override;
+
   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override;
   void PostSpawnTarget(base::ProcessHandle process) override;
 
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 4aeaaf7..f48d0901 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -784,9 +784,8 @@
     : public network::SharedURLLoaderFactory {
  public:
   explicit URLLoaderFactoryForBrowserProcess(
-      StoragePartitionImpl* storage_partition,
-      bool corb_enabled)
-      : storage_partition_(storage_partition), corb_enabled_(corb_enabled) {}
+      StoragePartitionImpl* storage_partition)
+      : storage_partition_(storage_partition) {}
 
   URLLoaderFactoryForBrowserProcess(const URLLoaderFactoryForBrowserProcess&) =
       delete;
@@ -806,8 +805,7 @@
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     if (!storage_partition_)
       return;
-    storage_partition_
-        ->GetURLLoaderFactoryForBrowserProcessInternal(corb_enabled_)
+    storage_partition_->GetURLLoaderFactoryForBrowserProcessInternal()
         ->CreateLoaderAndStart(std::move(receiver), request_id, options,
                                url_request, std::move(client),
                                traffic_annotation);
@@ -817,9 +815,8 @@
       override {
     if (!storage_partition_)
       return;
-    storage_partition_
-        ->GetURLLoaderFactoryForBrowserProcessInternal(corb_enabled_)
-        ->Clone(std::move(receiver));
+    storage_partition_->GetURLLoaderFactoryForBrowserProcessInternal()->Clone(
+        std::move(receiver));
   }
 
   // SharedURLLoaderFactory implementation:
@@ -836,7 +833,6 @@
   ~URLLoaderFactoryForBrowserProcess() override = default;
 
   raw_ptr<StoragePartitionImpl> storage_partition_;
-  const bool corb_enabled_;
 };
 
 // Static.
@@ -1108,9 +1104,6 @@
   if (shared_url_loader_factory_for_browser_process_) {
     shared_url_loader_factory_for_browser_process_->Shutdown();
   }
-  if (shared_url_loader_factory_for_browser_process_with_corb_) {
-    shared_url_loader_factory_for_browser_process_with_corb_->Shutdown();
-  }
 
   scoped_refptr<storage::DatabaseTracker> database_tracker(
       GetDatabaseTracker());
@@ -1430,21 +1423,11 @@
   DCHECK(initialized_);
   if (!shared_url_loader_factory_for_browser_process_) {
     shared_url_loader_factory_for_browser_process_ =
-        new URLLoaderFactoryForBrowserProcess(this, false /* corb_enabled */);
+        new URLLoaderFactoryForBrowserProcess(this);
   }
   return shared_url_loader_factory_for_browser_process_;
 }
 
-scoped_refptr<network::SharedURLLoaderFactory>
-StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessWithCORBEnabled() {
-  DCHECK(initialized_);
-  if (!shared_url_loader_factory_for_browser_process_with_corb_) {
-    shared_url_loader_factory_for_browser_process_with_corb_ =
-        new URLLoaderFactoryForBrowserProcess(this, true /* corb_enabled */);
-  }
-  return shared_url_loader_factory_for_browser_process_with_corb_;
-}
-
 std::unique_ptr<network::PendingSharedURLLoaderFactory>
 StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessIOThread() {
   DCHECK(initialized_);
@@ -2651,7 +2634,6 @@
   DCHECK(initialized_);
   GetNetworkContext()->ResetURLLoaderFactories();
   url_loader_factory_for_browser_process_.reset();
-  url_loader_factory_for_browser_process_with_corb_.reset();
   url_loader_factory_getter_->Initialize(this);
 }
 
@@ -2688,8 +2670,6 @@
   network_context_.FlushForTesting();
   if (url_loader_factory_for_browser_process_)
     url_loader_factory_for_browser_process_.FlushForTesting();
-  if (url_loader_factory_for_browser_process_with_corb_)
-    url_loader_factory_for_browser_process_with_corb_.FlushForTesting();
   if (cookie_manager_for_browser_process_)
     cookie_manager_for_browser_process_.FlushForTesting();
 }
@@ -2909,50 +2889,43 @@
 }
 
 network::mojom::URLLoaderFactory*
-StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessInternal(
-    bool corb_enabled) {
-  auto& url_loader_factory =
-      corb_enabled ? url_loader_factory_for_browser_process_with_corb_
-                   : url_loader_factory_for_browser_process_;
-  auto& is_test_url_loader_factory =
-      corb_enabled ? is_test_url_loader_factory_for_browser_process_with_corb_
-                   : is_test_url_loader_factory_for_browser_process_;
-
+StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessInternal() {
   // Create the URLLoaderFactory as needed, but make sure not to reuse a
   // previously created one if the test override has changed.
-  if (url_loader_factory && url_loader_factory.is_connected() &&
-      is_test_url_loader_factory != !GetCreateURLLoaderFactoryCallback()) {
-    return url_loader_factory.get();
+  if (url_loader_factory_for_browser_process_ &&
+      url_loader_factory_for_browser_process_.is_connected() &&
+      is_test_url_loader_factory_for_browser_process_ !=
+          !GetCreateURLLoaderFactoryCallback()) {
+    return url_loader_factory_for_browser_process_.get();
   }
 
   network::mojom::URLLoaderFactoryParamsPtr params =
       network::mojom::URLLoaderFactoryParams::New();
   params->process_id = network::mojom::kBrowserProcessId;
   params->automatically_assign_isolation_info = true;
-  params->is_corb_enabled = corb_enabled;
-  // Corb requests are likely made on behalf of untrusted renderers.
-  if (!corb_enabled)
-    params->is_trusted = true;
+  params->is_corb_enabled = false;
+  params->is_trusted = true;
   params->url_loader_network_observer =
       CreateAuthCertObserverForServiceWorker();
   params->disable_web_security =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableWebSecurity);
-  url_loader_factory.reset();
+  url_loader_factory_for_browser_process_.reset();
   if (!GetCreateURLLoaderFactoryCallback()) {
     GetNetworkContext()->CreateURLLoaderFactory(
-        url_loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
-    is_test_url_loader_factory = false;
-    return url_loader_factory.get();
+        url_loader_factory_for_browser_process_.BindNewPipeAndPassReceiver(),
+        std::move(params));
+    is_test_url_loader_factory_for_browser_process_ = false;
+    return url_loader_factory_for_browser_process_.get();
   }
 
   mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory;
   GetNetworkContext()->CreateURLLoaderFactory(
       original_factory.InitWithNewPipeAndPassReceiver(), std::move(params));
-  url_loader_factory.Bind(
+  url_loader_factory_for_browser_process_.Bind(
       GetCreateURLLoaderFactoryCallback().Run(std::move(original_factory)));
-  is_test_url_loader_factory = true;
-  return url_loader_factory.get();
+  is_test_url_loader_factory_for_browser_process_ = true;
+  return url_loader_factory_for_browser_process_.get();
 }
 
 void StoragePartition::SetDefaultQuotaSettingsForTesting(
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 46da9dc2b..34fd7b9 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -153,8 +153,6 @@
   network::mojom::NetworkContext* GetNetworkContext() override;
   scoped_refptr<network::SharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcess() override;
-  scoped_refptr<network::SharedURLLoaderFactory>
-  GetURLLoaderFactoryForBrowserProcessWithCORBEnabled() override;
   std::unique_ptr<network::PendingSharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcessIOThread() override;
   network::mojom::CookieManager* GetCookieManagerForBrowserProcess() override;
@@ -582,7 +580,7 @@
   bool is_in_memory() { return config_.in_memory(); }
 
   network::mojom::URLLoaderFactory*
-  GetURLLoaderFactoryForBrowserProcessInternal(bool corb_enabled);
+  GetURLLoaderFactoryForBrowserProcessInternal();
 
   // If `local_trust_token_fulfiller_` is bound, returns immediately.
   //
@@ -695,8 +693,6 @@
 
   scoped_refptr<URLLoaderFactoryForBrowserProcess>
       shared_url_loader_factory_for_browser_process_;
-  scoped_refptr<URLLoaderFactoryForBrowserProcess>
-      shared_url_loader_factory_for_browser_process_with_corb_;
 
   // URLLoaderFactory/CookieManager for use in the browser process only.
   // See the method comment for
@@ -705,9 +701,6 @@
   mojo::Remote<network::mojom::URLLoaderFactory>
       url_loader_factory_for_browser_process_;
   bool is_test_url_loader_factory_for_browser_process_ = false;
-  mojo::Remote<network::mojom::URLLoaderFactory>
-      url_loader_factory_for_browser_process_with_corb_;
-  bool is_test_url_loader_factory_for_browser_process_with_corb_ = false;
   mojo::Remote<network::mojom::CookieManager>
       cookie_manager_for_browser_process_;
 
diff --git a/content/browser/utility_sandbox_delegate.h b/content/browser/utility_sandbox_delegate.h
index ee6ec3a9..d64f68c8 100644
--- a/content/browser/utility_sandbox_delegate.h
+++ b/content/browser/utility_sandbox_delegate.h
@@ -32,6 +32,7 @@
   sandbox::mojom::Sandbox GetSandboxType() override;
 
 #if BUILDFLAG(IS_WIN)
+  std::string GetSandboxTag() override;
   bool GetAppContainerId(std::string* appcontainer_id) override;
   bool DisableDefaultPolicy() override;
   bool ShouldLaunchElevated() override;
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc
index e3c1b29..56a6f2c4 100644
--- a/content/browser/utility_sandbox_delegate_win.cc
+++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -107,6 +107,11 @@
 }
 }  // namespace
 
+std::string UtilitySandboxedProcessLauncherDelegate::GetSandboxTag() {
+  return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
+      "utility", GetSandboxType());
+}
+
 bool UtilitySandboxedProcessLauncherDelegate::GetAppContainerId(
     std::string* appcontainer_id) {
   if (sandbox_type_ == sandbox::mojom::Sandbox::kNetwork) {
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 39264fc..fc36081 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -147,7 +147,7 @@
     ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     deps += [
       "//ppapi/proxy",
       "//ppapi/shared_impl",
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index a34397c8..bd84802 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -393,7 +393,6 @@
            kSetOnlyIfOverridden},
           {"TouchActionEffectiveAtPointerDown",
            features::kVirtualKeyboardMultitouch},
-          {"TrustedDOMTypes", features::kTrustedDOMTypes},
           {"UserAgentClientHint", blink::features::kUserAgentClientHint},
           {"ViewportHeightClientHintHeader",
            blink::features::kViewportHeightClientHintHeader},
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 1ac4a63..c6f685a 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -357,7 +357,7 @@
     sources -= [ "font_list_fontconfig.cc" ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     deps += [
       "//ppapi/proxy:ipc",
       "//ppapi/shared_impl",
diff --git a/content/ppapi_plugin/BUILD.gn b/content/ppapi_plugin/BUILD.gn
index 2a46848..eef1558 100644
--- a/content/ppapi_plugin/BUILD.gn
+++ b/content/ppapi_plugin/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//ppapi/buildflags/buildflags.gni")
 
-assert(enable_plugins, "PPAPI plugins must be enabled.")
+assert(enable_ppapi, "PPAPI plugins must be enabled.")
 
 group("ppapi_plugin") {
   visibility = [ "//content/*" ]  # This is an internal content API.
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 59b64c98..ced577d3 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -523,7 +523,8 @@
     "//content/browser",
   ]
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "browser_ppapi_host.h",
       "pepper_vpn_provider_resource_host_proxy.h",
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 275d9b0..b891926 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -113,8 +113,6 @@
   // from RenderFrameHost::CreateNetworkServiceDefaultFactory).
   virtual scoped_refptr<network::SharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcess() = 0;
-  virtual scoped_refptr<network::SharedURLLoaderFactory>
-  GetURLLoaderFactoryForBrowserProcessWithCORBEnabled() = 0;
   virtual std::unique_ptr<network::PendingSharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcessIOThread() = 0;
   virtual network::mojom::CookieManager*
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 2eee13b..8bb2620 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -133,8 +133,6 @@
     "page_type.h",
     "page_visibility_state.h",
     "page_zoom.h",
-    "pepper_plugin_info.cc",
-    "pepper_plugin_info.h",
     "persistent_notification_status.h",
     "process_type.h",
     "profiling.cc",
@@ -228,7 +226,6 @@
     "//media",
     "//media/mojo/mojom",
     "//ppapi/buildflags",
-    "//ppapi/c",
     "//services/network/public/cpp",
     "//services/service_manager/public/cpp",
     "//skia",
@@ -284,12 +281,12 @@
     "//content/common:mojo_bindings",
   ]
 
-  if (!enable_plugins) {
-    sources -= [
+  if (enable_ppapi) {
+    sources += [
       "pepper_plugin_info.cc",
       "pepper_plugin_info.h",
     ]
-    deps -= [ "//ppapi/c" ]
+    deps += [ "//ppapi/c" ]
   }
 
   if (use_clang_profiling_inside_sandbox) {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index bf77771..8047c40 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1012,10 +1012,6 @@
 const base::Feature kTreatBootstrapAsDefault{"TreatBootstrapAsDefault",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Controls whether the Trusted Types API is available.
-const base::Feature kTrustedDOMTypes{"TrustedDOMTypes",
-                                     base::FEATURE_ENABLED_BY_DEFAULT};
-
 // This feature is for a reverse Origin Trial, enabling SharedArrayBuffer for
 // sites as they migrate towards requiring cross-origin isolation for these
 // features.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index ac96ec2..1f597bc 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -264,7 +264,6 @@
 CONTENT_EXPORT extern const base::Feature kTouchpadAsyncPinchEvents;
 CONTENT_EXPORT extern const base::Feature kTouchpadOverscrollHistoryNavigation;
 CONTENT_EXPORT extern const base::Feature kTreatBootstrapAsDefault;
-CONTENT_EXPORT extern const base::Feature kTrustedDOMTypes;
 CONTENT_EXPORT extern const base::Feature kUnrestrictedSharedArrayBuffer;
 CONTENT_EXPORT extern const base::Feature kUserActivationSameOriginVisibility;
 CONTENT_EXPORT extern const base::Feature kVerifyDidCommitParams;
diff --git a/content/public/common/sandboxed_process_launcher_delegate.cc b/content/public/common/sandboxed_process_launcher_delegate.cc
index a3773d6..ef86cae 100644
--- a/content/public/common/sandboxed_process_launcher_delegate.cc
+++ b/content/public/common/sandboxed_process_launcher_delegate.cc
@@ -10,6 +10,11 @@
 namespace content {
 
 #if BUILDFLAG(IS_WIN)
+std::string SandboxedProcessLauncherDelegate::GetSandboxTag() {
+  // This implies that policies will not share backing data.
+  return "";
+}
+
 bool SandboxedProcessLauncherDelegate::DisableDefaultPolicy() {
   return false;
 }
diff --git a/content/public/common/sandboxed_process_launcher_delegate.h b/content/public/common/sandboxed_process_launcher_delegate.h
index 4176cae..71b4369 100644
--- a/content/public/common/sandboxed_process_launcher_delegate.h
+++ b/content/public/common/sandboxed_process_launcher_delegate.h
@@ -30,6 +30,7 @@
 
 #if BUILDFLAG(IS_WIN)
   // SandboxDelegate:
+  std::string GetSandboxTag() override;
   bool DisableDefaultPolicy() override;
   bool GetAppContainerId(std::string* appcontainer_id) override;
   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override;
diff --git a/content/public/renderer/BUILD.gn b/content/public/renderer/BUILD.gn
index 5a9b007..2f5967c 100644
--- a/content/public/renderer/BUILD.gn
+++ b/content/public/renderer/BUILD.gn
@@ -82,7 +82,8 @@
     "//content/renderer",
   ]
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "pepper_plugin_instance.h",
       "plugin_ax_tree_source.h",
diff --git a/content/public/test/test_storage_partition.cc b/content/public/test/test_storage_partition.cc
index 890ee7f0..6f28a65b 100644
--- a/content/public/test/test_storage_partition.cc
+++ b/content/public/test/test_storage_partition.cc
@@ -28,11 +28,6 @@
   return nullptr;
 }
 
-scoped_refptr<network::SharedURLLoaderFactory>
-TestStoragePartition::GetURLLoaderFactoryForBrowserProcessWithCORBEnabled() {
-  return nullptr;
-}
-
 std::unique_ptr<network::PendingSharedURLLoaderFactory>
 TestStoragePartition::GetURLLoaderFactoryForBrowserProcessIOThread() {
   return nullptr;
diff --git a/content/public/test/test_storage_partition.h b/content/public/test/test_storage_partition.h
index c27b7a4..bed1cb1e 100644
--- a/content/public/test/test_storage_partition.h
+++ b/content/public/test/test_storage_partition.h
@@ -62,9 +62,6 @@
   scoped_refptr<network::SharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcess() override;
 
-  scoped_refptr<network::SharedURLLoaderFactory>
-  GetURLLoaderFactoryForBrowserProcessWithCORBEnabled() override;
-
   std::unique_ptr<network::PendingSharedURLLoaderFactory>
   GetURLLoaderFactoryForBrowserProcessIOThread() override;
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index f96fab5b..37f21bf 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -384,18 +384,7 @@
     }
   }
 
-  if (enable_plugins) {
-    sources += [
-      "pepper/pepper_media_stream_audio_track_host.cc",
-      "pepper/pepper_media_stream_audio_track_host.h",
-      "pepper/pepper_media_stream_track_host_base.cc",
-      "pepper/pepper_media_stream_track_host_base.h",
-      "pepper/pepper_media_stream_video_track_host.cc",
-      "pepper/pepper_media_stream_video_track_host.h",
-    ]
-  }
-
-  if (enable_plugins) {
+  if (enable_ppapi) {
     sources += [
       "pepper/audio_helper.cc",
       "pepper/audio_helper.h",
@@ -443,6 +432,12 @@
       "pepper/pepper_in_process_router.h",
       "pepper/pepper_media_device_manager.cc",
       "pepper/pepper_media_device_manager.h",
+      "pepper/pepper_media_stream_audio_track_host.cc",
+      "pepper/pepper_media_stream_audio_track_host.h",
+      "pepper/pepper_media_stream_track_host_base.cc",
+      "pepper/pepper_media_stream_track_host_base.h",
+      "pepper/pepper_media_stream_video_track_host.cc",
+      "pepper/pepper_media_stream_video_track_host.h",
       "pepper/pepper_platform_audio_input.cc",
       "pepper/pepper_platform_audio_input.h",
       "pepper/pepper_platform_audio_output.cc",
diff --git a/content/renderer/accessibility/ax_tree_distiller.cc b/content/renderer/accessibility/ax_tree_distiller.cc
index 7a89986..9002748 100644
--- a/content/renderer/accessibility/ax_tree_distiller.cc
+++ b/content/renderer/accessibility/ax_tree_distiller.cc
@@ -125,9 +125,9 @@
 }
 
 void AXTreeDistiller::SnapshotAXTree() {
-  // If snapshot_ is already cached, do nothing.
-  if (snapshot_)
-    return;
+  // TODO(crbug.com/1266555): Consider doing nothing if |snapshot_| is already
+  // cached. We are disabling caching while the feature is still in development
+  // to ease debugging.
   snapshot_ = std::make_unique<ui::AXTreeUpdate>();
 
   // Get page contents (via snapshot of a11y tree) for reader generation.
@@ -142,11 +142,9 @@
 }
 
 void AXTreeDistiller::DistillAXTree() {
-  // If content_node_ids_ is already cached, finish and run the callback.
-  if (content_node_ids_) {
-    RunCallback();
-    return;
-  }
+  // TODO(crbug.com/1266555): Consider finishing and running the callback if
+  // |content_node_ids_| is already cached. We are disabling caching while the
+  // feature is still in development to ease debugging.
 
   // If Read Anything with Screen 2x is enabled, kick off Screen 2x run, which
   // distills the AXTree in the utility process using ML.
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 0322e7a..fe77cb5 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -288,7 +288,8 @@
     ]
   }
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "browser/shell_plugin_service_filter.cc",
       "browser/shell_plugin_service_filter.h",
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 8afc899..cdc0d6286 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -627,7 +627,7 @@
     ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     sources += [
       "../public/test/ppapi_test_utils.cc",
       "../public/test/ppapi_test_utils.h",
@@ -1659,7 +1659,8 @@
     "//third_party/mesa_headers",
   ]
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1306610): Split out files that only need `enable_plugins`.
+  if (enable_ppapi) {
     sources += [
       "../browser/plugin_service_impl_browsertest.cc",
       "../renderer/pepper/fake_pepper_plugin_instance.cc",
@@ -1878,7 +1879,6 @@
       "//device/fido:fido",
       "//device/fido:mocks",
       "//device/fido:test_support",
-      "//services/device/public/cpp/hid:test_support",
     ]
   }
 
@@ -2777,7 +2777,7 @@
     ]
   }
 
-  if (enable_plugins) {
+  if (enable_ppapi) {
     sources += [
       "../browser/ppapi_plugin_sandboxed_process_launcher_delegate_unittest.cc",
       "../browser/renderer_host/pepper/browser_ppapi_host_test.cc",
@@ -2958,8 +2958,6 @@
       "//device/fido:cablev2_test_util",
       "//device/fido:mocks",
       "//device/fido:test_support",
-      "//services/device/hid:test_support",
-      "//services/device/public/cpp/hid:test_support",
     ]
     if (is_chromeos) {
       deps += [
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index f7ddb4f..7f068578 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -486,7 +486,7 @@
 crbug.com/1338004 [ mac nvidia-0xfe9 passthrough ] deqp/functional/gles3/multisample/fbo_4_samples.html [ RetryOnFailure ]
 
 
-# Mac AMD
+# Mac AMD Retina
 # AMD Radeon HD 8870M (1002:6821)
 # TODO(kbr): uncomment the following two exepectations after test
 # has been made more robust.
@@ -512,6 +512,19 @@
 
 crbug.com/1343793 [ mac amd-0x6821 angle-opengl ] deqp/functional/gles3/texturefiltering/3d_combinations_24.html [ RetryOnFailure ]
 
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/shaderoperator/binary_operator_14.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/3d_sizes_00.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_formats_05.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_formats_06.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_no_edges_visible.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/basic_teximage3d_2d_array_00.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/basic_texsubimage2d_cube_04.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage2d_depth.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_00.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage3d_depth.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/texstorage2d_format_cube_01.html [ RetryOnFailure ]
+crbug.com/1345782 [ mac amd-0x6821 angle-opengl passthrough ] deqp/functional/gles3/texturespecification/texstorage3d_format_depth_stencil.html [ RetryOnFailure ]
+
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
@@ -618,6 +631,19 @@
 # AMD Radeon HD 7800 GPU (1002:679e)
 crbug.com/1276186 [ mac amd-0x679e angle-opengl passthrough ] conformance/rendering/clear-default-framebuffer-with-scissor-test.html [ Failure ]
 crbug.com/1240443 [ mac amd-0x679e angle-opengl passthrough ] conformance/renderbuffers/stencil-renderbuffer-initialization.html [ Failure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/shaderoperator/binary_operator_14.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/3d_combinations_24.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/3d_sizes_00.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_formats_05.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_formats_06.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturefiltering/cube_no_edges_visible.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/basic_teximage3d_2d_array_00.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/basic_texsubimage2d_cube_04.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage2d_depth.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_00.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/teximage3d_depth.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/texstorage2d_format_cube_01.html [ RetryOnFailure ]
+crbug.com/1345755 [ mac amd-0x679e angle-opengl passthrough ] deqp/functional/gles3/texturespecification/texstorage3d_format_depth_stencil.html [ RetryOnFailure ]
 
 # Mac / M1 / OpenGL
 crbug.com/1130112 [ mac apple-apple-m1 passthrough angle-opengl ] deqp/functional/gles3/texturefiltering/cube_combinations_00.html [ Failure ]
diff --git a/content/web_test/BUILD.gn b/content/web_test/BUILD.gn
index 9deef6c..99c4aa8c 100644
--- a/content/web_test/BUILD.gn
+++ b/content/web_test/BUILD.gn
@@ -203,7 +203,8 @@
     "//url",
   ]
 
-  if (enable_plugins) {
+  # TODO(crbug.com/1103882): Blink test plugin must be migrated from PPAPI.
+  if (enable_ppapi) {
     deps += [
       "//content/ppapi_plugin",
       "//ppapi:blink_deprecated_test_plugin",
diff --git a/device/BUILD.gn b/device/BUILD.gn
index ff1b87b..3a405cd 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -223,8 +223,7 @@
     ]
     deps += [
       "//device/fido:test_support",
-      "//services/device/public/cpp/hid",
-      "//services/device/public/mojom",
+      "//services/device/public/cpp:test_support",
     ]
   }
 
@@ -424,7 +423,7 @@
     deps += [
       "//services/device:test_support",
       "//services/device/hid",
-      "//services/device/hid:test_support",
+      "//services/device/public/cpp:test_support",
     ]
   }
 
diff --git a/device/gamepad/nintendo_data_fetcher_unittest.cc b/device/gamepad/nintendo_data_fetcher_unittest.cc
index 0be61da..3e9ecad3 100644
--- a/device/gamepad/nintendo_data_fetcher_unittest.cc
+++ b/device/gamepad/nintendo_data_fetcher_unittest.cc
@@ -15,7 +15,7 @@
 #include "device/gamepad/gamepad_service.h"
 #include "services/device/device_service_test_base.h"
 #include "services/device/hid/hid_manager_impl.h"
-#include "services/device/hid/mock_hid_service.h"
+#include "services/device/public/cpp/test/mock_hid_service.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/docs/webapps/integration-testing-framework.md b/docs/webapps/integration-testing-framework.md
index 8b79183..54080b57 100644
--- a/docs/webapps/integration-testing-framework.md
+++ b/docs/webapps/integration-testing-framework.md
@@ -12,9 +12,10 @@
 3. [Tests][default-tests] generated by the script that use the [`WebAppIntegrationTestDriver`][test-driver] to execute actions.
 4. [Coverage][coverage-win] information about what percentage of the critical user journeys are covered.
 
-How-tos:
+How-tos / guides:
 * [How to create WebApp Integration Tests][how-to-create]
 * [Disabling a test](#disabling-a-test)
+* [Why is this test failing?][why-is-this-test-failing]
 
 Related:
   * [WebAppProvider README.md](/chrome/browser/web_applications/README.md)
@@ -306,3 +307,4 @@
 [coverage-win]: /chrome/test/webapps/coverage/coverage_win.tsv
 [framework-supported-actions]: /chrome/test/webapps/data/framework_supported_actions.csv
 [sync-test-base]: chrome/browser/sync/test/integration/sync_test.h
+[why-is-this-test-failing]: why-is-this-test-failing.md
diff --git a/docs/webapps/why-is-this-test-failing.md b/docs/webapps/why-is-this-test-failing.md
new file mode 100644
index 0000000..2489dd3
--- /dev/null
+++ b/docs/webapps/why-is-this-test-failing.md
@@ -0,0 +1,38 @@
+# Why are these WebAppIntegration_* tests failing?
+
+The integration tests for the WebAppProvider system test as much of the system as possible from user actions, and it can sometimes be difficult to categorize why the failure is occurring.
+
+## What do I do?
+
+1. Is this a flaky test that is showing up on the flakiness dashboard with a score > 200? It is reasonable to disable this test. See the [flakiness](#flakiness) section, and the [disabling tests section](#disabling-tests) about how to disable the test.
+2. Are a huge number of tests suddenly failing? This is probably because behavior was changed that broke the critical user journeys that these tests come from. Either:
+    1. A regression was introduced in a CL that needs to be reverted. If this is the case, that CL should probably to be found and reverted.
+    2. The current CL changed the way the system works which conflicts with the critical user journeys. If this is the case, the critical user journeys need to be updated. Please contact the team at  pwa-dev@chromium.org and/or post on #pwas on Chromium Slack, as we can help update the framework & guide this process.
+
+### Disabling tests
+
+Tests can be disabled in the same manner that other integration/browser tests are disabled, using macros. See [on disabling tests](/docs/testing/on_disabling_tests.md) for more information.
+
+## Why are they failing?
+
+### System behavior is changing
+
+Tests are expected to fail when the WebAppProvider behavior or user actions are changed. To fix this, the critical user journeys need to be updated and the tests regenerated to modify the expected behavior. See the [docs](integration-testing-framework.md) or the ["How to create WebappIntegration Tests"](how-to-create-webapp-integration-tests.md) page for more info. Please contact the team at  pwa-dev@chromium.org and/or post on #pwas on Chromium Slack, as we can help update the framework & guide this process.
+
+This failure **should** occur in trybots on the change, and not the CQ, although we do have a history of having browser_tests turned off for Mac trybots and sometimes things are only caught on the CQ.
+
+### Flakiness
+
+Due to how much of Chrome this is testing, a baseline of flakes is unfortunately somewhat common. But if they occur frequently, they often point to real bugs that are flaky in production as well.
+
+Disabling the browsertest for flakiness resembles disabling any other browsertest, by having a `DISABLED_` prefix. When disabling, please try to target it to the affected platform.
+
+It can be useful to check the [bugs](https://bugs.chromium.org/p/chromium/issues/list?q=component%3APlatform%3EWebAppProvider%3EIntegrationTesting&can=2) in the component and the test log to see if there is already a bug open for the given problem or test. If specific to the web app system, then it is useful to add new logs to the bug.
+
+Sometimes global problems affect the tests (and all other browser test) and it is nice to make sure this is not the case before disabling. Examples:
+- `processor_entity.cc(263): Check failed: commit_only || data.response_version > metadata_.server_version()`, which is a sync system flake ([bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1299874&q=component%3APlatform%3EWebAppProvider%3EIntegrationTesting&can=2)).
+- Renderer crashes failing tests w/o stack traces ([bug](https://crbug.com/1329854#c31)).
+
+### A user action was refactored and our driver is out of date.
+
+In more rare scenarios, a user action (like how a user installs a web app, or launches from the intent picker, etc) changes in production code but the method in the `WebAppIntegrationTestDriver` is not updated to reflect the "new way". This would then cause all tests that use that action to fail. This should be caught in trybots, and if so, that action must be updated in the driver to match the new version.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 030df1e..67b2da63 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -723,8 +723,6 @@
     "//extensions/shell:browser_tests",
     "//net:test_support",
     "//services/device/public/cpp:test_support",
-    "//services/device/public/cpp/hid",
-    "//services/device/public/cpp/hid:test_support",
     "//services/device/public/mojom",
     "//services/network:test_support",
     "//services/service_manager/public/cpp",
diff --git a/extensions/browser/api/hid/hid_apitest.cc b/extensions/browser/api/hid/hid_apitest.cc
index c95a26ee..810cac5 100644
--- a/extensions/browser/api/hid/hid_apitest.cc
+++ b/extensions/browser/api/hid/hid_apitest.cc
@@ -18,8 +18,8 @@
 #include "extensions/shell/browser/shell_extensions_api_client.h"
 #include "extensions/shell/test/shell_apitest.h"
 #include "extensions/test/extension_test_message_listener.h"
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
 #include "services/device/public/cpp/hid/hid_report_descriptor.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/ios/chrome/browser/follow/follow_tab_helper.mm b/ios/chrome/browser/follow/follow_tab_helper.mm
index d55544c..baa4d5f66 100644
--- a/ios/chrome/browser/follow/follow_tab_helper.mm
+++ b/ios/chrome/browser/follow/follow_tab_helper.mm
@@ -158,6 +158,13 @@
   }
 
   // Show follow in-product help (IPH) if eligible.
+
+  // Don't show follow in-product help (IPH) if there's no presenter. Ex.
+  // follow_iph_presenter_ is nil when link preview page is loaded.
+  if (!follow_iph_presenter_) {
+    return;
+  }
+
   feature_engagement::Tracker* feature_engagement_tracker =
       feature_engagement::TrackerFactory::GetForBrowserState(
           ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState()));
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index 036a1b6..76e459d 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -36,8 +36,85 @@
   *flags |= (condition ? mask : 0);
 }
 
-// Section 5.9.11. Loop filter params syntax in AV1 spec.
+inline void conditionally_set_u32_flags(__u32* flags,
+                                        const bool condition,
+                                        const bool mask) {
+  *flags |= (condition ? mask : 0);
+}
+
+// Section 5.5. Sequence header OBU syntax in the AV1 spec.
 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf
+void FillSequenceParams(
+    struct v4l2_ctrl_av1_sequence* v4l2_seq_params,
+    const absl::optional<libgav1::ObuSequenceHeader>& seq_header) {
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->still_picture,
+                              V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->use_128x128_superblock,
+                              V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_filter_intra,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_intra_edge_filter,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER);
+  conditionally_set_u32_flags(
+      &v4l2_seq_params->flags, seq_header->enable_interintra_compound,
+      V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_masked_compound,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_warped_motion,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_dual_filter,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_order_hint,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_jnt_comp,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_ref_frame_mvs,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_superres,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags, seq_header->enable_cdef,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->enable_restoration,
+                              V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->color_config.is_monochrome,
+                              V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->color_config.color_range,
+                              V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->color_config.subsampling_x,
+                              V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->color_config.subsampling_y,
+                              V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->film_grain_params_present,
+                              V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT);
+  conditionally_set_u32_flags(&v4l2_seq_params->flags,
+                              seq_header->color_config.separate_uv_delta_q,
+                              V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q);
+
+  v4l2_seq_params->seq_profile = seq_header->profile;
+  v4l2_seq_params->order_hint_bits = seq_header->order_hint_bits;
+  v4l2_seq_params->bit_depth = seq_header->color_config.bitdepth;
+  v4l2_seq_params->max_frame_width_minus_1 = seq_header->max_frame_width - 1;
+  v4l2_seq_params->max_frame_height_minus_1 = seq_header->max_frame_height - 1;
+}
+
+// Section 5.9.11. Loop filter params syntax.
 // Note that |update_ref_delta| and |update_mode_delta| flags in the spec
 // are not needed for V4L2 AV1 API.
 // TODO(stevecho): sanity check data structures in libgav1 against the AV1 spec.
@@ -617,7 +694,15 @@
     ANALYZER_ALLOW_UNUSED(reference_id);
   }
 
-  // TODO(b/239618516): add ext_ctrl for V4L2_CID_STATELESS_AV1_SEQUENCE
+  std::vector<struct v4l2_ext_control> ext_ctrl_vectors;
+
+  struct v4l2_ctrl_av1_sequence v4l2_seq_params = {};
+
+  FillSequenceParams(&v4l2_seq_params, current_sequence_header_);
+
+  ext_ctrl_vectors.push_back({.id = V4L2_CID_STATELESS_AV1_SEQUENCE,
+                              .size = sizeof(v4l2_seq_params),
+                              .ptr = &v4l2_seq_params});
 
   struct v4l2_ctrl_av1_frame_header v4l2_frame_params = {};
 
@@ -650,11 +735,12 @@
 
   // TODO(stevecho): V4L2_CID_STATELESS_AV1_FRAME_HEADER is trending to be
   // changed to V4L2_CID_STATELESS_AV1_FRAME
-  struct v4l2_ext_control ext_ctrl = {.id = V4L2_CID_STATELESS_AV1_FRAME_HEADER,
-                                      .size = sizeof(v4l2_frame_params),
-                                      .ptr = &v4l2_frame_params};
+  ext_ctrl_vectors.push_back({.id = V4L2_CID_STATELESS_AV1_FRAME_HEADER,
+                              .size = sizeof(v4l2_frame_params),
+                              .ptr = &v4l2_frame_params});
 
-  struct v4l2_ext_controls ext_ctrls = {.count = 1, .controls = &ext_ctrl};
+  struct v4l2_ext_controls ext_ctrls = {.count = base::checked_cast<__u32>(ext_ctrl_vectors.size()),
+                                        .controls = &ext_ctrl_vectors[0]};
 
   if (!v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, &ext_ctrls))
     LOG(FATAL) << "VIDIOC_S_EXT_CTRLS failed.";
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 1d50f16..b5c522cb 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -231,9 +231,14 @@
     CERT_VERIFY_PROC_IOS
 #elif BUILDFLAG(IS_MAC)
     CERT_VERIFY_PROC_MAC, CERT_VERIFY_PROC_BUILTIN,
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
     CERT_VERIFY_PROC_BUILTIN_CHROME_ROOTS
+#endif
 #elif BUILDFLAG(IS_WIN)
-    CERT_VERIFY_PROC_WIN, CERT_VERIFY_PROC_BUILTIN_CHROME_ROOTS
+    CERT_VERIFY_PROC_WIN,
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+    CERT_VERIFY_PROC_BUILTIN_CHROME_ROOTS
+#endif
 #elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     CERT_VERIFY_PROC_BUILTIN
 #else
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc
index 9f9250d..fc21d363 100644
--- a/net/cert/internal/system_trust_store.cc
+++ b/net/cert/internal/system_trust_store.cc
@@ -341,18 +341,23 @@
 #endif  // CHROME_ROOT_STORE_SUPPORTED
 
 void InitializeTrustStoreMacCache() {
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   if (base::FeatureList::IsEnabled(net::features::kChromeRootStoreUsed)) {
     base::ThreadPool::PostTask(
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(&InitializeTrustCacheForCRSOnWorkerThread));
-  } else if (base::FeatureList::IsEnabled(
-                 net::features::kCertVerifierBuiltinFeature)) {
+    return;
+  }
+#endif  // CHROME_ROOT_STORE_SUPPORTED
+  if (base::FeatureList::IsEnabled(
+          net::features::kCertVerifierBuiltinFeature)) {
     base::ThreadPool::PostTask(
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(
             &SystemTrustStoreMac::InitializeTrustCacheOnWorkerThread));
+    return;
   }
 }
 
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc
index 390cfae..561cde82 100644
--- a/net/dns/host_resolver.cc
+++ b/net/dns/host_resolver.cc
@@ -15,8 +15,10 @@
 #include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/time_delta_from_string.h"
 #include "base/values.h"
 #include "net/base/address_list.h"
+#include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/dns/context_host_resolver.h"
 #include "net/dns/dns_client.h"
@@ -37,6 +39,21 @@
 
 namespace {
 
+// The experiment settings of features::kUseDnsHttpsSvcb. See the comments in
+// net/base/features.h for more details.
+const char kUseDnsHttpsSvcbEnable[] = "enable";
+const char kUseDnsHttpsSvcbEnableInsecure[] = "enable_insecure";
+const char kUseDnsHttpsSvcbInsecureExtraTimeMax[] = "insecure_extra_time_max";
+const char kUseDnsHttpsSvcbInsecureExtraTimePercent[] =
+    "insecure_extra_time_percent";
+const char kUseDnsHttpsSvcbInsecureExtraTimeMin[] = "insecure_extra_time_min";
+const char kUseDnsHttpsSvcbSecureExtraTimeMax[] = "secure_extra_time_max";
+const char kUseDnsHttpsSvcbSecureExtraTimePercent[] =
+    "secure_extra_time_percent";
+const char kUseDnsHttpsSvcbSecureExtraTimeMin[] = "secure_extra_time_min";
+const char kUseDnsHttpsSvcbExtraTimeAbsolute[] = "extra_time_absolute";
+const char kUseDnsHttpsSvcbExtraTimePercent[] = "extra_time_percent";
+
 class FailingRequestImpl : public HostResolver::ResolveHostRequest,
                            public HostResolver::ProbeRequest {
  public:
@@ -92,8 +109,90 @@
   return result.metadata.supported_protocol_alpns.empty();
 }
 
+void GetTimeDeltaFromDictString(const base::Value::Dict& args,
+                                base::StringPiece key,
+                                base::TimeDelta* out) {
+  const std::string* value_string = args.FindString(key);
+  if (!value_string)
+    return;
+  *out = base::TimeDeltaFromString(*value_string).value_or(*out);
+}
+
 }  // namespace
 
+HostResolver::HttpsSvcbOptions::HttpsSvcbOptions() = default;
+
+HostResolver::HttpsSvcbOptions::HttpsSvcbOptions(
+    const HttpsSvcbOptions& other) = default;
+HostResolver::HttpsSvcbOptions::HttpsSvcbOptions(HttpsSvcbOptions&& other) =
+    default;
+
+HostResolver::HttpsSvcbOptions::~HttpsSvcbOptions() = default;
+
+// static
+HostResolver::HttpsSvcbOptions HostResolver::HttpsSvcbOptions::FromDict(
+    const base::Value::Dict& dict) {
+  net::HostResolver::HttpsSvcbOptions options;
+  options.enable =
+      dict.FindBool(kUseDnsHttpsSvcbEnable).value_or(options.enable);
+  options.enable_insecure = dict.FindBool(kUseDnsHttpsSvcbEnableInsecure)
+                                .value_or(options.enable_insecure);
+  GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbInsecureExtraTimeMax,
+                             &options.insecure_extra_time_max);
+
+  options.insecure_extra_time_percent =
+      dict.FindInt(kUseDnsHttpsSvcbInsecureExtraTimePercent)
+          .value_or(options.insecure_extra_time_percent);
+  GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbInsecureExtraTimeMin,
+                             &options.insecure_extra_time_min);
+
+  GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbSecureExtraTimeMax,
+                             &options.secure_extra_time_max);
+
+  options.secure_extra_time_percent =
+      dict.FindInt(kUseDnsHttpsSvcbSecureExtraTimePercent)
+          .value_or(options.secure_extra_time_percent);
+  GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbSecureExtraTimeMin,
+                             &options.secure_extra_time_min);
+
+  GetTimeDeltaFromDictString(dict, kUseDnsHttpsSvcbExtraTimeAbsolute,
+                             &options.extra_time_absolute);
+  options.extra_time_percent = dict.FindInt(kUseDnsHttpsSvcbExtraTimePercent)
+                                   .value_or(options.extra_time_percent);
+  return options;
+}
+
+// static
+HostResolver::HttpsSvcbOptions HostResolver::HttpsSvcbOptions::FromFeatures() {
+  net::HostResolver::HttpsSvcbOptions options;
+  options.enable = base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb);
+  options.enable_insecure = features::kUseDnsHttpsSvcbEnableInsecure.Get();
+  options.insecure_extra_time_max =
+      features::kUseDnsHttpsSvcbInsecureExtraTimeMax.Get();
+  options.insecure_extra_time_percent =
+      features::kUseDnsHttpsSvcbInsecureExtraTimePercent.Get();
+  options.insecure_extra_time_min =
+      features::kUseDnsHttpsSvcbInsecureExtraTimeMin.Get();
+  options.secure_extra_time_max =
+      features::kUseDnsHttpsSvcbSecureExtraTimeMax.Get();
+  options.secure_extra_time_percent =
+      features::kUseDnsHttpsSvcbSecureExtraTimePercent.Get();
+  options.secure_extra_time_min =
+      features::kUseDnsHttpsSvcbSecureExtraTimeMin.Get();
+  options.extra_time_absolute =
+      features::kUseDnsHttpsSvcbExtraTimeAbsolute.Get();
+  options.extra_time_percent = features::kUseDnsHttpsSvcbExtraTimePercent.Get();
+  return options;
+}
+
+HostResolver::ManagerOptions::ManagerOptions() = default;
+
+HostResolver::ManagerOptions::ManagerOptions(const ManagerOptions& other) =
+    default;
+HostResolver::ManagerOptions::ManagerOptions(ManagerOptions&& other) = default;
+
+HostResolver::ManagerOptions::~ManagerOptions() = default;
+
 const std::vector<bool>*
 HostResolver::ResolveHostRequest::GetExperimentalResultsForTesting() const {
   NOTREACHED();
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 0a9a4363..b9ca88b 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -172,9 +172,41 @@
     virtual int Start() = 0;
   };
 
+  // The options for features::kUseDnsHttpsSvcb experiment. See the comments
+  // in net/base/features.h for more details.
+  struct NET_EXPORT HttpsSvcbOptions {
+    HttpsSvcbOptions();
+    HttpsSvcbOptions(const HttpsSvcbOptions&);
+    HttpsSvcbOptions(HttpsSvcbOptions&&);
+    HttpsSvcbOptions& operator=(const HttpsSvcbOptions&) = default;
+    HttpsSvcbOptions& operator=(HttpsSvcbOptions&&) = default;
+    ~HttpsSvcbOptions();
+
+    static HttpsSvcbOptions FromDict(const base::Value::Dict& dict);
+    static HttpsSvcbOptions FromFeatures();
+
+    bool enable = false;
+    bool enable_insecure = false;
+    base::TimeDelta insecure_extra_time_max;
+    int insecure_extra_time_percent = 0;
+    base::TimeDelta insecure_extra_time_min;
+    base::TimeDelta secure_extra_time_max;
+    int secure_extra_time_percent = 0;
+    base::TimeDelta secure_extra_time_min;
+    base::TimeDelta extra_time_absolute;
+    int extra_time_percent = 0;
+  };
+
   // Parameter-grouping struct for additional optional parameters for creation
   // of HostResolverManagers and stand-alone HostResolvers.
   struct NET_EXPORT ManagerOptions {
+    ManagerOptions();
+    ManagerOptions(const ManagerOptions&);
+    ManagerOptions(ManagerOptions&&);
+    ManagerOptions& operator=(const ManagerOptions&) = default;
+    ManagerOptions& operator=(ManagerOptions&&) = default;
+    ~ManagerOptions();
+
     // Set |max_concurrent_resolves| to this to select a default level
     // of concurrency.
     static const size_t kDefaultParallelism = 0;
@@ -208,6 +240,10 @@
     // unreachable without actually checking. See https://crbug.com/696569 for
     // further context.
     bool check_ipv6_on_wifi = true;
+
+    // An experimental options for features::kUseDnsHttpsSvcb
+    // and features::kUseDnsHttpsSvcbAlpn.
+    absl::optional<HostResolver::HttpsSvcbOptions> https_svcb_options;
   };
 
   // Factory class. Useful for classes that need to inject and override resolver
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 7a9432a6..e7604c4 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -528,14 +528,14 @@
   return absl::get<HostPortPair>(host).port();
 }
 
-// Only use scheme/port in JobKey if `features::kUseDnsHttpsSvcb` is enabled
+// Only use scheme/port in JobKey if `https_svcb_options_enabled` is true
 // (or the query is explicitly for HTTPS). Otherwise DNS will not give different
 // results for the same hostname.
 absl::variant<url::SchemeHostPort, std::string> CreateHostForJobKey(
     const HostResolver::Host& input,
-    DnsQueryType query_type) {
-  if ((base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb) ||
-       query_type == DnsQueryType::HTTPS) &&
+    DnsQueryType query_type,
+    bool https_svcb_options_enabled) {
+  if ((https_svcb_options_enabled || query_type == DnsQueryType::HTTPS) &&
       absl::holds_alternative<url::SchemeHostPort>(input)) {
     return absl::get<url::SchemeHostPort>(input);
   }
@@ -615,7 +615,7 @@
         request_host_(std::move(request_host)),
         network_isolation_key_(
             base::FeatureList::IsEnabled(
-                net::features::kSplitHostCacheByNetworkIsolationKey)
+                features::kSplitHostCacheByNetworkIsolationKey)
                 ? std::move(network_isolation_key)
                 : NetworkIsolationKey()),
         parameters_(optional_parameters ? std::move(optional_parameters).value()
@@ -1258,7 +1258,8 @@
           Delegate* delegate,
           const NetLogWithSource& job_net_log,
           const base::TickClock* tick_clock,
-          bool fallback_available)
+          bool fallback_available,
+          const HostResolver::HttpsSvcbOptions& https_svcb_options)
       : client_(client),
         host_(std::move(host)),
         resolve_context_(resolve_context->AsSafeRef()),
@@ -1268,7 +1269,8 @@
         net_log_(job_net_log),
         tick_clock_(tick_clock),
         task_start_time_(tick_clock_->NowTicks()),
-        fallback_available_(fallback_available) {
+        fallback_available_(fallback_available),
+        https_svcb_options_(https_svcb_options) {
     DCHECK(client_);
     DCHECK(delegate_);
 
@@ -1404,7 +1406,7 @@
       return types;
 
     if (types.Has(DnsQueryType::HTTPS)) {
-      if (!secure_ && (!features::kUseDnsHttpsSvcbEnableInsecure.Get() ||
+      if (!secure_ && (!https_svcb_options_.enable_insecure ||
                        !client_->CanQueryAdditionalTypesViaInsecureDns())) {
         types.Remove(DnsQueryType::HTTPS);
       } else {
@@ -1951,24 +1953,22 @@
     base::TimeDelta timeout_min;
 
     if (AnyOfTypeTransactionsRemain({DnsQueryType::HTTPS})) {
-      DCHECK(base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb));
+      DCHECK(https_svcb_options_.enable);
 
       if (secure_) {
-        timeout_max = features::kUseDnsHttpsSvcbSecureExtraTimeMax.Get();
-        extra_time_percent =
-            features::kUseDnsHttpsSvcbSecureExtraTimePercent.Get();
-        timeout_min = features::kUseDnsHttpsSvcbSecureExtraTimeMin.Get();
+        timeout_max = https_svcb_options_.secure_extra_time_max;
+        extra_time_percent = https_svcb_options_.secure_extra_time_percent;
+        timeout_min = https_svcb_options_.secure_extra_time_min;
       } else {
-        timeout_max = features::kUseDnsHttpsSvcbInsecureExtraTimeMax.Get();
-        extra_time_percent =
-            features::kUseDnsHttpsSvcbInsecureExtraTimePercent.Get();
-        timeout_min = features::kUseDnsHttpsSvcbInsecureExtraTimeMin.Get();
+        timeout_max = https_svcb_options_.insecure_extra_time_max;
+        extra_time_percent = https_svcb_options_.insecure_extra_time_percent;
+        timeout_min = https_svcb_options_.insecure_extra_time_min;
       }
 
       if (timeout_max.is_zero() && extra_time_percent == 0 &&
           timeout_min.is_zero()) {
-        timeout_max = features::kUseDnsHttpsSvcbExtraTimeAbsolute.Get();
-        extra_time_percent = features::kUseDnsHttpsSvcbExtraTimePercent.Get();
+        timeout_max = https_svcb_options_.extra_time_absolute;
+        extra_time_percent = https_svcb_options_.extra_time_percent;
       }
 
       // Skip timeout for secure requests if the timeout would be a fatal
@@ -2103,6 +2103,8 @@
   // task completes unsuccessfully. Used as a signal that underlying
   // transactions should timeout more quickly.
   bool fallback_available_;
+
+  const HostResolver::HttpsSvcbOptions https_svcb_options_;
 };
 
 //-----------------------------------------------------------------------------
@@ -2175,7 +2177,8 @@
       RequestPriority priority,
       scoped_refptr<base::TaskRunner> proc_task_runner,
       const NetLogWithSource& source_net_log,
-      const base::TickClock* tick_clock)
+      const base::TickClock* tick_clock,
+      const HostResolver::HttpsSvcbOptions& https_svcb_options)
       : resolver_(resolver),
         key_(std::move(key)),
         cache_usage_(cache_usage),
@@ -2184,6 +2187,7 @@
         priority_tracker_(priority),
         proc_task_runner_(std::move(proc_task_runner)),
         tick_clock_(tick_clock),
+        https_svcb_options_(https_svcb_options),
         net_log_(
             NetLogWithSource::Make(source_net_log.net_log(),
                                    NetLogSourceType::HOST_RESOLVER_IMPL_JOB)) {
@@ -2671,7 +2675,8 @@
     dns_task_ = std::make_unique<DnsTask>(
         resolver_->dns_client_.get(), key_.host, key_.query_types,
         &*key_.resolve_context, secure, key_.secure_dns_mode, this, net_log_,
-        tick_clock_, !tasks_.empty() /* fallback_available */);
+        tick_clock_, !tasks_.empty() /* fallback_available */,
+        https_svcb_options_);
     dns_task_->StartNextTransaction();
     // Schedule a second transaction, if needed. DoH queries can bypass the
     // dispatcher and start all of their transactions immediately.
@@ -3072,6 +3077,8 @@
   raw_ptr<const base::TickClock> tick_clock_;
   base::TimeTicks start_time_;
 
+  HostResolver::HttpsSvcbOptions https_svcb_options_;
+
   NetLogWithSource net_log_;
 
   // Resolves the host using a HostResolverProc.
@@ -3120,7 +3127,11 @@
       system_dns_config_notifier_(system_dns_config_notifier),
       target_network_(target_network),
       check_ipv6_on_wifi_(options.check_ipv6_on_wifi),
-      tick_clock_(base::DefaultTickClock::GetInstance()) {
+      tick_clock_(base::DefaultTickClock::GetInstance()),
+      https_svcb_options_(
+          options.https_svcb_options
+              ? *options.https_svcb_options
+              : HostResolver::HttpsSvcbOptions::FromFeatures()) {
   PrioritizedDispatcher::Limits job_limits = GetDispatcherLimits(options);
   dispatcher_ = std::make_unique<PrioritizedDispatcher>(job_limits);
   max_queued_jobs_ = job_limits.total_jobs * 100u;
@@ -3412,7 +3423,8 @@
   const auto& parameters = request->parameters();
   JobKey job_key(request->resolve_context());
   job_key.host =
-      CreateHostForJobKey(request->request_host(), parameters.dns_query_type);
+      CreateHostForJobKey(request->request_host(), parameters.dns_query_type,
+                          https_svcb_options_.enable);
   job_key.network_isolation_key = request->network_isolation_key();
   job_key.source = parameters.source;
 
@@ -3580,10 +3592,10 @@
     std::deque<TaskType> tasks,
     RequestPriority priority,
     const NetLogWithSource& source_net_log) {
-  auto new_job =
-      std::make_unique<Job>(weak_ptr_factory_.GetWeakPtr(), key, cache_usage,
-                            host_cache, std::move(tasks), priority,
-                            proc_task_runner_, source_net_log, tick_clock_);
+  auto new_job = std::make_unique<Job>(
+      weak_ptr_factory_.GetWeakPtr(), key, cache_usage, host_cache,
+      std::move(tasks), priority, proc_task_runner_, source_net_log,
+      tick_clock_, https_svcb_options_);
   auto insert_result = jobs_.emplace(std::move(key), std::move(new_job));
   auto& iterator = insert_result.first;
   bool is_new = insert_result.second;
@@ -4047,9 +4059,9 @@
   // Optimistically enable feature-controlled queries. These queries may be
   // skipped at a later point.
 
-  // `features::kUseDnsHttpsSvcb` has precedence, so if enabled, ignore any
+  // `https_svcb_options_.enable` has precedence, so if enabled, ignore any
   // other related features.
-  if (base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb)) {
+  if (https_svcb_options_.enable) {
     static const char* const kSchemesForHttpsQuery[] = {
         url::kHttpScheme, url::kHttpsScheme, url::kWsScheme, url::kWssScheme};
     if (base::Contains(kSchemesForHttpsQuery, GetScheme(host)))
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 602a18b2..6e83437a 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -241,6 +241,10 @@
     return target_network_;
   }
 
+  const HostResolver::HttpsSvcbOptions& https_svcb_options_for_testing() const {
+    return https_svcb_options_;
+  }
+
   // Public to be called from std::make_unique. Not to be called directly.
   HostResolverManager(base::PassKey<HostResolverManager>,
                       const HostResolver::ManagerOptions& options,
@@ -545,6 +549,9 @@
   // Helper for metrics associated with `features::kDnsHttpssvc`.
   HttpssvcExperimentDomainCache httpssvc_domain_cache_;
 
+  // An experimental flag for features::kUseDnsHttpsSvcb.
+  HostResolver::HttpsSvcbOptions https_svcb_options_;
+
   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 5bc8d10..1654539 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -15026,16 +15026,16 @@
   using MockResult = MockDnsClientRule::ResultType;
 
   void SetUp() override {
+    // The request host scheme and port are only preserved if the SVCB feature
+    // is enabled.
+    features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
+
     HostResolverManagerDnsTest::SetUp();
 
     // MockHostResolverProc only returns failure if there is at least one
     // non-matching rule.
     proc_->AddRuleForAllFamilies("other_name", {});
     proc_->SignalMultiple(1u);  // Allow up to one proc query.
-
-    // The request host scheme and port are only preserved if the SVCB feature
-    // is enabled.
-    features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
   }
 
   const NetworkIsolationKey kIsolationKey;
diff --git a/net/dns/httpssvc_metrics.cc b/net/dns/httpssvc_metrics.cc
index c29e6a8..13430c99 100644
--- a/net/dns/httpssvc_metrics.cc
+++ b/net/dns/httpssvc_metrics.cc
@@ -150,12 +150,6 @@
 }
 
 void HttpssvcMetrics::RecordMetrics() {
-  // The HTTPSSVC experiment and its feature param indicating INTEGRITY must
-  // both be enabled.
-  DCHECK(features::kDnsHttpssvcUseIntegrity.Get() ||
-         features::kDnsHttpssvcUseHttpssvc.Get() ||
-         base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcb));
-
   DCHECK(!already_recorded_);
   already_recorded_ = true;
 
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index c07c77a..df0ebf2e 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "net/http/http_network_session.h"
 #include "net/http/http_raw_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_body_drainer.h"
@@ -185,8 +186,8 @@
 }
 
 void HttpBasicStream::Drain(HttpNetworkSession* session) {
-  HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(this);
-  drainer->Start(session);
+  session->StartResponseDrainer(
+      std::make_unique<HttpResponseBodyDrainer>(this));
   // |drainer| will delete itself.
 }
 
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 457bb67..8409687 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -83,6 +83,8 @@
       time_func(&base::TimeTicks::Now) {
   enable_early_data =
       base::FeatureList::IsEnabled(features::kEnableTLS13EarlyData);
+  use_dns_https_svcb_alpn =
+      base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcbAlpn);
 }
 
 HttpNetworkSessionParams::HttpNetworkSessionParams(
@@ -228,18 +230,19 @@
   spdy_session_pool_.CloseAllSessions();
 }
 
-void HttpNetworkSession::AddResponseDrainer(
+void HttpNetworkSession::StartResponseDrainer(
     std::unique_ptr<HttpResponseBodyDrainer> drainer) {
   DCHECK(!base::Contains(response_drainers_, drainer.get()));
   HttpResponseBodyDrainer* drainer_ptr = drainer.get();
-  response_drainers_[drainer_ptr] = std::move(drainer);
+  response_drainers_.insert(std::move(drainer));
+  drainer_ptr->Start(this);
 }
 
 void HttpNetworkSession::RemoveResponseDrainer(
     HttpResponseBodyDrainer* drainer) {
   DCHECK(base::Contains(response_drainers_, drainer));
-  response_drainers_[drainer].release();
-  response_drainers_.erase(drainer);
+
+  response_drainers_.erase(response_drainers_.find(drainer));
 }
 
 ClientSocketPool* HttpNetworkSession::GetSocketPool(
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index e963d52..30e2b90 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -17,6 +17,7 @@
 
 #include "base/bind.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/memory/memory_pressure_monitor.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
@@ -178,6 +179,9 @@
   // default network does (hence underlying objects should not drop their
   // state).
   bool ignore_ip_address_changes = false;
+
+  // Whether to use the ALPN information in the DNS HTTPS record.
+  bool use_dns_https_svcb_alpn = false;
 };
 
   // Structure with pointers to the dependencies of the HttpNetworkSession.
@@ -228,9 +232,9 @@
   HttpAuthCache* http_auth_cache() { return &http_auth_cache_; }
   SSLClientContext* ssl_client_context() { return &ssl_client_context_; }
 
-  void AddResponseDrainer(std::unique_ptr<HttpResponseBodyDrainer> drainer);
+  void StartResponseDrainer(std::unique_ptr<HttpResponseBodyDrainer> drainer);
 
-  // Removes the drainer from the session. Does not dispose of it.
+  // Removes the drainer from the session.
   void RemoveResponseDrainer(HttpResponseBodyDrainer* drainer);
 
   // Returns the socket pool of the given type for use with the specified
@@ -346,7 +350,7 @@
   QuicStreamFactory quic_stream_factory_;
   SpdySessionPool spdy_session_pool_;
   std::unique_ptr<HttpStreamFactory> http_stream_factory_;
-  std::map<HttpResponseBodyDrainer*, std::unique_ptr<HttpResponseBodyDrainer>>
+  std::set<std::unique_ptr<HttpResponseBodyDrainer>, base::UniquePtrComparator>
       response_drainers_;
   NextProtoVector next_protos_;
   SSLConfig::ApplicationSettings application_settings_;
diff --git a/net/http/http_response_body_drainer.cc b/net/http/http_response_body_drainer.cc
index 9591c58c..0be2c28 100644
--- a/net/http/http_response_body_drainer.cc
+++ b/net/http/http_response_body_drainer.cc
@@ -25,6 +25,7 @@
 HttpResponseBodyDrainer::~HttpResponseBodyDrainer() = default;
 
 void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
+  session_ = session;
   read_buf_ = base::MakeRefCounted<IOBuffer>(kDrainBodyBufferSize);
   next_state_ = STATE_DRAIN_RESPONSE_BODY;
   int rv = DoLoop(OK);
@@ -32,8 +33,6 @@
   if (rv == ERR_IO_PENDING) {
     timer_.Start(FROM_HERE, base::Seconds(kTimeoutInSeconds), this,
                  &HttpResponseBodyDrainer::OnTimerFired);
-    session_ = session;
-    session->AddResponseDrainer(base::WrapUnique(this));
     return;
   }
 
@@ -110,9 +109,6 @@
 void HttpResponseBodyDrainer::Finish(int result) {
   DCHECK_NE(ERR_IO_PENDING, result);
 
-  if (session_)
-    session_->RemoveResponseDrainer(this);
-
   if (result < 0 || !stream_->CanReuseConnection()) {
     stream_->Close(true /* no keep-alive */);
   } else {
@@ -120,7 +116,7 @@
     stream_->Close(false /* keep-alive */);
   }
 
-  delete this;
+  session_->RemoveResponseDrainer(this);
 }
 
 }  // namespace net
diff --git a/net/http/http_response_body_drainer.h b/net/http/http_response_body_drainer.h
index 5ba92fa..c1007ab0 100644
--- a/net/http/http_response_body_drainer.h
+++ b/net/http/http_response_body_drainer.h
@@ -33,8 +33,8 @@
   ~HttpResponseBodyDrainer();
 
   // Starts reading the body until completion, or we hit the buffer limit, or we
-  // timeout.  After Start(), |this| will eventually delete itself.  If it
-  // doesn't complete immediately, it will add itself to |session|.
+  // timeout.  After Start(), |this| will eventually delete itself via
+  // HttpNetworkSession::RemoveResponseDrainer().
   void Start(HttpNetworkSession* session);
 
  private:
diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc
index 009dc91..d4bc1bde 100644
--- a/net/http/http_response_body_drainer_unittest.cc
+++ b/net/http/http_response_body_drainer_unittest.cc
@@ -241,8 +241,9 @@
         ssl_config_service_(std::make_unique<SSLConfigServiceDefaults>()),
         http_server_properties_(std::make_unique<HttpServerProperties>()),
         session_(CreateNetworkSession()),
-        mock_stream_(new MockHttpStream(&result_waiter_)),
-        drainer_(new HttpResponseBodyDrainer(mock_stream_)) {}
+        mock_stream_(new MockHttpStream(&result_waiter_)) {
+    drainer_ = std::make_unique<HttpResponseBodyDrainer>(mock_stream_);
+  }
 
   ~HttpResponseBodyDrainerTest() override = default;
 
@@ -270,27 +271,27 @@
   MockClientSocketFactory socket_factory_;
   const std::unique_ptr<HttpNetworkSession> session_;
   CloseResultWaiter result_waiter_;
-  const raw_ptr<MockHttpStream> mock_stream_;       // Owned by |drainer_|.
-  const raw_ptr<HttpResponseBodyDrainer> drainer_;  // Deletes itself.
+  const raw_ptr<MockHttpStream> mock_stream_;  // Owned by |drainer_|.
+  std::unique_ptr<HttpResponseBodyDrainer> drainer_;
 };
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) {
   mock_stream_->set_num_chunks(1);
   mock_stream_->set_sync();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) {
   mock_stream_->set_num_chunks(3);
   mock_stream_->set_sync();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) {
   mock_stream_->set_num_chunks(3);
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
@@ -300,7 +301,7 @@
 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncEmptyChunk) {
   mock_stream_->set_num_chunks(4);
   mock_stream_->set_is_last_chunk_zero_size();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
@@ -308,28 +309,28 @@
   mock_stream_->set_num_chunks(4);
   mock_stream_->set_sync();
   mock_stream_->set_is_last_chunk_zero_size();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
   mock_stream_->set_num_chunks(
       HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize);
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_FALSE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) {
   mock_stream_->set_num_chunks(2);
   mock_stream_->set_stall_reads_forever();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_TRUE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) {
   mock_stream_->set_num_chunks(2);
   mock_stream_->set_stall_reads_forever();
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   // HttpNetworkSession should delete |drainer_|.
 }
 
@@ -339,14 +340,14 @@
   too_many_chunks += 1;  // Now it's too large.
 
   mock_stream_->set_num_chunks(too_many_chunks);
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_TRUE(result_waiter_.WaitForResult());
 }
 
 TEST_F(HttpResponseBodyDrainerTest, DrainBodyCantReuse) {
   mock_stream_->set_num_chunks(1);
   mock_stream_->set_can_reuse_connection(false);
-  drainer_->Start(session_.get());
+  session_->StartResponseDrainer(std::move(drainer_));
   EXPECT_TRUE(result_waiter_.WaitForResult());
 }
 
diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc
index 41a36d4..ac1af7c 100644
--- a/net/http/http_stream_factory_job_controller.cc
+++ b/net/http/http_stream_factory_job_controller.cc
@@ -815,7 +815,7 @@
     DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported());
   }
   const bool dns_alpn_h3_job_enabled =
-      base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcbAlpn) &&
+      session_->params().use_dns_https_svcb_alpn &&
       base::EqualsCaseInsensitiveASCII(origin_url.scheme(),
                                        url::kHttpsScheme) &&
       session_->IsQuicEnabled() && proxy_info_.is_direct() &&
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index 9b1716d..3cfa06d 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -370,6 +370,7 @@
   base::test::ScopedFeatureList features;
   features.InitAndEnableFeatureWithParameters(
       features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
+  ResetContext();
 
   GURL https_url = https_server_.GetURL(kHostname, "/test");
   EXPECT_TRUE(https_url.SchemeIs(url::kHttpsScheme));
@@ -413,6 +414,7 @@
   base::test::ScopedFeatureList features;
   features.InitAndEnableFeatureWithParameters(
       features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
+  ResetContext();
 
   GURL main_url = https_server_.GetURL(kHostname, "/test");
   EXPECT_TRUE(main_url.SchemeIs(url::kHttpsScheme));
@@ -514,6 +516,7 @@
       /*enabled_features=*/{features::kEncryptedClientHello,
                             features::kUseDnsHttpsSvcb},
       /*disabled_features=*/{});
+  ResetContext();
 
   static constexpr char kRealNameStale[] = "secret1.example";
   static constexpr char kRealNameWrongPublicName[] = "secret2.example";
@@ -594,6 +597,7 @@
       /*enabled_features=*/{features::kEncryptedClientHello,
                             features::kUseDnsHttpsSvcb},
       /*disabled_features=*/{});
+  ResetContext();
 
   static constexpr char kRealNameStale[] = "secret1.example";
   static constexpr char kRealNameWrongPublicName[] = "secret2.example";
@@ -664,6 +668,7 @@
       /*enabled_features=*/{features::kEncryptedClientHello,
                             features::kUseDnsHttpsSvcb},
       /*disabled_features=*/{});
+  ResetContext();
 
   static constexpr char kRealNameStale[] = "secret1.example";
   static constexpr char kRealNameWrongPublicName[] = "secret2.example";
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index 2f1ebc35..c727c14 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -23,6 +23,7 @@
 #include "crypto/random.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_endpoint.h"
+#include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_body_drainer.h"
@@ -375,8 +376,8 @@
 }
 
 void WebSocketBasicHandshakeStream::Drain(HttpNetworkSession* session) {
-  HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(this);
-  drainer->Start(session);
+  session->StartResponseDrainer(
+      std::make_unique<HttpResponseBodyDrainer>(this));
   // |drainer| will delete itself.
 }
 
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index 323b4e3c..09dffb5 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -39,6 +39,9 @@
 const base::Feature kRendererAppContainer{"RendererAppContainer",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables shared/fixed policy for Windows sandbox policies.
+const base::Feature kSharedSandboxPolicies{"SharedSandboxPolicies",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(IS_WIN)
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index 56a7944..03ae0f9 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -25,6 +25,7 @@
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuAppContainer;
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC;
 SANDBOX_POLICY_EXPORT extern const base::Feature kRendererAppContainer;
+SANDBOX_POLICY_EXPORT extern const base::Feature kSharedSandboxPolicies;
 #endif  // BUILDFLAG(IS_WIN)
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/sandbox/policy/sandbox_delegate.h b/sandbox/policy/sandbox_delegate.h
index 46913ae0..74cb8e4 100644
--- a/sandbox/policy/sandbox_delegate.h
+++ b/sandbox/policy/sandbox_delegate.h
@@ -28,6 +28,13 @@
   virtual sandbox::mojom::Sandbox GetSandboxType() = 0;
 
 #if BUILDFLAG(IS_WIN)
+  // Returns a tag for the sandbox. All targets with the same tag will share
+  // their FixedPolicy configuration - the delegate can call
+  // FixedPolicy::IsFixed() to skip setting this configuration after the first
+  // such policy has been configured. Provide an empty string to force every
+  // policy to be unique.
+  virtual std::string GetSandboxTag() = 0;
+
   // Whether to disable the default policy specified in
   // AddPolicyForSandboxedProcess.
   virtual bool DisableDefaultPolicy() = 0;
@@ -37,6 +44,8 @@
   virtual bool GetAppContainerId(std::string* appcontainer_id) = 0;
 
   // Called right before spawning the process. Returns false on failure.
+  // Methods in FixedPolicy only need to be called if IsFixed() returns
+  // false.
   virtual bool PreSpawnTarget(TargetPolicy* policy) = 0;
 
   // Called right after the process is launched, but before its thread is run.
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 7246e7e..607ecb26 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -1128,19 +1129,25 @@
     SandboxDelegate* delegate,
     base::Process* process) {
   const base::ElapsedTimer timer;
-  auto policy = g_broker_services->CreatePolicy();
-  auto time_policy_created = timer.Elapsed();
 
-  ResultCode result = GeneratePolicyForSandboxedProcess(
-      cmd_line, process_type, handles_to_inherit, delegate, policy.get());
-  auto time_policy_generated = timer.Elapsed();
-
-  if (ResultCode::SBOX_ERROR_UNSANDBOXED_PROCESS == result) {
+  // Avoid making a policy if we won't use it.
+  if (IsUnsandboxedProcess(delegate->GetSandboxType(), cmd_line,
+                           *base::CommandLine::ForCurrentProcess())) {
     return LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate,
                                 process);
   }
+
+  std::string tag;
+  if (base::FeatureList::IsEnabled(features::kSharedSandboxPolicies))
+    tag = delegate->GetSandboxTag();
+
+  auto policy = g_broker_services->CreatePolicy(tag);
+  auto time_policy_created = timer.Elapsed();
+  ResultCode result = GeneratePolicyForSandboxedProcess(
+      cmd_line, process_type, handles_to_inherit, delegate, policy.get());
   if (SBOX_ALL_OK != result)
     return result;
+  auto time_policy_generated = timer.Elapsed();
 
   TRACE_EVENT_BEGIN0("startup", "StartProcessWithAccess::LAUNCHPROCESS");
 
@@ -1274,5 +1281,15 @@
   }
 }
 
+// static
+std::string SandboxWin::GetSandboxTagForDelegate(
+    base::StringPiece prefix,
+    sandbox::mojom::Sandbox sandbox_type) {
+  // sandbox.mojom.Sandbox has an operator << we can use for non-human values.
+  std::ostringstream stream;
+  stream << prefix << "!" << sandbox_type;
+  return stream.str();
+}
+
 }  // namespace policy
 }  // namespace sandbox
diff --git a/sandbox/policy/win/sandbox_win.h b/sandbox/policy/win/sandbox_win.h
index e702734..ec9cb8b1 100644
--- a/sandbox/policy/win/sandbox_win.h
+++ b/sandbox/policy/win/sandbox_win.h
@@ -113,6 +113,11 @@
   // Provides a friendly name for the sandbox for chrome://sandbox and tracing.
   static std::string GetSandboxTypeInEnglish(
       sandbox::mojom::Sandbox sandbox_type);
+
+  // Helper for sandbox delegates to generate a SandboxTag
+  static std::string GetSandboxTagForDelegate(
+      base::StringPiece prefix,
+      sandbox::mojom::Sandbox sandbox_type);
 };
 
 SANDBOX_POLICY_EXPORT
diff --git a/sandbox/policy/win/sandbox_win_unittest.cc b/sandbox/policy/win/sandbox_win_unittest.cc
index 6bdd710..b77358b9 100644
--- a/sandbox/policy/win/sandbox_win_unittest.cc
+++ b/sandbox/policy/win/sandbox_win_unittest.cc
@@ -369,6 +369,7 @@
 
   MOCK_METHOD1(PreSpawnTarget, bool(TargetPolicy* policy));
 
+  std::string GetSandboxTag() override { return std::string(); }
   void PostSpawnTarget(base::ProcessHandle process) override {}
 
   bool ShouldUnsandboxedRunInJob() override { return false; }
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index 4057a343..7b8b1fc7 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -275,8 +275,7 @@
     deps += [
       "//components/variations:test_support",
       "//services/device/battery",
-      "//services/device/hid:test_support",
-      "//services/device/public/cpp/hid",
+      "//services/device/hid",
       "//services/device/vibration",
     ]
 
diff --git a/services/device/hid/BUILD.gn b/services/device/hid/BUILD.gn
index 10d9db1..fef8af1 100644
--- a/services/device/hid/BUILD.gn
+++ b/services/device/hid/BUILD.gn
@@ -76,28 +76,6 @@
   }
 }
 
-source_set("test_support") {
-  testonly = true
-
-  sources = [
-    "mock_hid_connection.cc",
-    "mock_hid_connection.h",
-    "mock_hid_service.cc",
-    "mock_hid_service.h",
-    "test_report_descriptors.cc",
-    "test_report_descriptors.h",
-    "test_util.cc",
-    "test_util.h",
-  ]
-
-  public_deps = [
-    ":hid",
-    "//base",
-    "//services/device/public/cpp/hid",
-    "//services/device/public/mojom",
-  ]
-}
-
 fuzzer_test("hid_report_descriptor_fuzzer") {
   sources = [ "hid_report_descriptor_fuzzer.cc" ]
   deps = [
diff --git a/services/device/hid/hid_device_info_unittest.cc b/services/device/hid/hid_device_info_unittest.cc
index 5a15352..5b15c4e 100644
--- a/services/device/hid/hid_device_info_unittest.cc
+++ b/services/device/hid/hid_device_info_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/containers/flat_map.h"
 #include "build/build_config.h"
 #include "services/device/hid/hid_report_type.h"
-#include "services/device/hid/test_report_descriptors.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/device/hid/hid_manager_unittest.cc b/services/device/hid/hid_manager_unittest.cc
index a1d25a6..1109c4b 100644
--- a/services/device/hid/hid_manager_unittest.cc
+++ b/services/device/hid/hid_manager_unittest.cc
@@ -15,8 +15,8 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/device_service_test_base.h"
 #include "services/device/hid/hid_manager_impl.h"
-#include "services/device/hid/mock_hid_connection.h"
-#include "services/device/hid/mock_hid_service.h"
+#include "services/device/public/cpp/test/mock_hid_connection.h"
+#include "services/device/public/cpp/test/mock_hid_service.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/device/public/cpp/BUILD.gn b/services/device/public/cpp/BUILD.gn
index f4032e4..d065f7c 100644
--- a/services/device/public/cpp/BUILD.gn
+++ b/services/device/public/cpp/BUILD.gn
@@ -57,13 +57,6 @@
     "test/test_wake_lock_provider.h",
   ]
 
-  if (is_android) {
-    sources += [
-      "test/scoped_nfc_overrider.cc",
-      "test/scoped_nfc_overrider.h",
-    ]
-  }
-
   public_deps = [
     "//base",
     "//services/device/public/cpp/generic_sensor",
@@ -83,4 +76,28 @@
     "//testing/gtest",
     "//url",
   ]
+
+  if (is_android) {
+    sources += [
+      "test/scoped_nfc_overrider.cc",
+      "test/scoped_nfc_overrider.h",
+    ]
+  } else {
+    sources += [
+      "test/fake_hid_manager.cc",
+      "test/fake_hid_manager.h",
+      "test/hid_test_util.cc",
+      "test/hid_test_util.h",
+      "test/mock_hid_connection.cc",
+      "test/mock_hid_connection.h",
+      "test/mock_hid_service.cc",
+      "test/mock_hid_service.h",
+      "test/test_report_descriptors.cc",
+      "test/test_report_descriptors.h",
+    ]
+
+    public_deps += [ "//services/device/public/cpp/hid" ]
+
+    deps += [ "//services/device/hid" ]
+  }
 }
diff --git a/services/device/public/cpp/hid/BUILD.gn b/services/device/public/cpp/hid/BUILD.gn
index fe1a873f..1a6ac19 100644
--- a/services/device/public/cpp/hid/BUILD.gn
+++ b/services/device/public/cpp/hid/BUILD.gn
@@ -44,18 +44,3 @@
     ]
   }
 }
-
-static_library("test_support") {
-  testonly = true
-
-  sources = [
-    "fake_hid_manager.cc",
-    "fake_hid_manager.h",
-  ]
-
-  public_deps = [
-    "//base",
-    "//services/device/public/cpp/hid",
-    "//services/device/public/mojom",
-  ]
-}
diff --git a/services/device/public/cpp/hid/hid_blocklist_unittest.cc b/services/device/public/cpp/hid/hid_blocklist_unittest.cc
index af4edb01..c51c65f8 100644
--- a/services/device/public/cpp/hid/hid_blocklist_unittest.cc
+++ b/services/device/public/cpp/hid/hid_blocklist_unittest.cc
@@ -8,9 +8,9 @@
 #include "base/guid.h"
 #include "base/strings/string_piece.h"
 #include "base/test/scoped_feature_list.h"
-#include "services/device/hid/test_report_descriptors.h"
-#include "services/device/hid/test_util.h"
 #include "services/device/public/cpp/hid/hid_switches.h"
+#include "services/device/public/cpp/test/hid_test_util.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/device/public/cpp/hid/hid_device_filter_unittest.cc b/services/device/public/cpp/hid/hid_device_filter_unittest.cc
index 1e34d8c..6e9583a6 100644
--- a/services/device/public/cpp/hid/hid_device_filter_unittest.cc
+++ b/services/device/public/cpp/hid/hid_device_filter_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "build/build_config.h"
 #include "services/device/hid/hid_device_info.h"
-#include "services/device/hid/test_report_descriptors.h"
 #include "services/device/public/cpp/hid/hid_device_filter.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc
index b996bae..edf3bb2 100644
--- a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc
+++ b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc
@@ -10,9 +10,9 @@
 #include <unordered_map>
 #include <utility>
 
-#include "services/device/hid/test_report_descriptors.h"
 #include "services/device/public/cpp/hid/hid_report_descriptor.h"
 #include "services/device/public/cpp/hid/hid_usage_and_page.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/device/public/cpp/hid/fake_hid_manager.cc b/services/device/public/cpp/test/fake_hid_manager.cc
similarity index 99%
rename from services/device/public/cpp/hid/fake_hid_manager.cc
rename to services/device/public/cpp/test/fake_hid_manager.cc
index c6bc3e4..3ae1f5f 100644
--- a/services/device/public/cpp/hid/fake_hid_manager.cc
+++ b/services/device/public/cpp/test/fake_hid_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/device/public/cpp/hid/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_hid_manager.h"
 
 #include <memory>
 #include <utility>
diff --git a/services/device/public/cpp/hid/fake_hid_manager.h b/services/device/public/cpp/test/fake_hid_manager.h
similarity index 94%
rename from services/device/public/cpp/hid/fake_hid_manager.h
rename to services/device/public/cpp/test/fake_hid_manager.h
index ae00f45..0ce10b77 100644
--- a/services/device/public/cpp/hid/fake_hid_manager.h
+++ b/services/device/public/cpp/test/fake_hid_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_PUBLIC_CPP_HID_FAKE_HID_MANAGER_H_
-#define SERVICES_DEVICE_PUBLIC_CPP_HID_FAKE_HID_MANAGER_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_TEST_FAKE_HID_MANAGER_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_TEST_FAKE_HID_MANAGER_H_
 
 #include <map>
 #include <string>
@@ -101,4 +101,4 @@
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_PUBLIC_CPP_HID_FAKE_HID_MANAGER_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_TEST_FAKE_HID_MANAGER_H_
diff --git a/services/device/hid/test_util.cc b/services/device/public/cpp/test/hid_test_util.cc
similarity index 96%
rename from services/device/hid/test_util.cc
rename to services/device/public/cpp/test/hid_test_util.cc
index e5526c5..ffd5cc3 100644
--- a/services/device/hid/test_util.cc
+++ b/services/device/public/cpp/test/hid_test_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/device/hid/test_util.h"
+#include "services/device/public/cpp/test/hid_test_util.h"
 
 #include "base/guid.h"
 #include "services/device/public/cpp/hid/hid_blocklist.h"
diff --git a/services/device/hid/test_util.h b/services/device/public/cpp/test/hid_test_util.h
similarity index 79%
rename from services/device/hid/test_util.h
rename to services/device/public/cpp/test/hid_test_util.h
index e6bece97..494ffab0f 100644
--- a/services/device/hid/test_util.h
+++ b/services/device/public/cpp/test/hid_test_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_HID_TEST_UTIL_H_
-#define SERVICES_DEVICE_HID_TEST_UTIL_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_TEST_HID_TEST_UTIL_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_TEST_HID_TEST_UTIL_H_
 
 #include <stdint.h>
 
@@ -22,4 +22,4 @@
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_HID_TEST_UTIL_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_TEST_HID_TEST_UTIL_H_
diff --git a/services/device/hid/mock_hid_connection.cc b/services/device/public/cpp/test/mock_hid_connection.cc
similarity index 95%
rename from services/device/hid/mock_hid_connection.cc
rename to services/device/public/cpp/test/mock_hid_connection.cc
index 7480a582..07dc860 100644
--- a/services/device/hid/mock_hid_connection.cc
+++ b/services/device/public/cpp/test/mock_hid_connection.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/device/hid/mock_hid_connection.h"
+#include "services/device/public/cpp/test/mock_hid_connection.h"
 
 #include "base/memory/ref_counted_memory.h"
 #include "services/device/hid/hid_device_info.h"
diff --git a/services/device/hid/mock_hid_connection.h b/services/device/public/cpp/test/mock_hid_connection.h
similarity index 85%
rename from services/device/hid/mock_hid_connection.h
rename to services/device/public/cpp/test/mock_hid_connection.h
index 4338707..a83dfed 100644
--- a/services/device/hid/mock_hid_connection.h
+++ b/services/device/public/cpp/test/mock_hid_connection.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_HID_MOCK_HID_CONNECTION_H_
-#define SERVICES_DEVICE_HID_MOCK_HID_CONNECTION_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_CONNECTION_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_CONNECTION_H_
 
 #include "services/device/hid/hid_connection.h"
 
@@ -34,4 +34,4 @@
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_HID_MOCK_HID_CONNECTION_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_CONNECTION_H_
diff --git a/services/device/hid/mock_hid_service.cc b/services/device/public/cpp/test/mock_hid_service.cc
similarity index 92%
rename from services/device/hid/mock_hid_service.cc
rename to services/device/public/cpp/test/mock_hid_service.cc
index 67a78525..bd141d0c 100644
--- a/services/device/hid/mock_hid_service.cc
+++ b/services/device/public/cpp/test/mock_hid_service.cc
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/device/public/cpp/test/mock_hid_service.h"
+
 #include <map>
 
 #include "base/memory/ref_counted_memory.h"
-#include "services/device/hid/mock_hid_connection.h"
-#include "services/device/hid/mock_hid_service.h"
+#include "services/device/public/cpp/test/mock_hid_connection.h"
 
 namespace device {
 
diff --git a/services/device/hid/mock_hid_service.h b/services/device/public/cpp/test/mock_hid_service.h
similarity index 85%
rename from services/device/hid/mock_hid_service.h
rename to services/device/public/cpp/test/mock_hid_service.h
index e500b30..0f6f6882 100644
--- a/services/device/hid/mock_hid_service.h
+++ b/services/device/public/cpp/test/mock_hid_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_HID_MOCK_HID_SERVICE_H_
-#define SERVICES_DEVICE_HID_MOCK_HID_SERVICE_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_SERVICE_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_SERVICE_H_
 
 #include "services/device/hid/hid_service.h"
 
@@ -35,4 +35,4 @@
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_HID_MOCK_HID_SERVICE_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_TEST_MOCK_HID_SERVICE_H_
diff --git a/services/device/hid/test_report_descriptors.cc b/services/device/public/cpp/test/test_report_descriptors.cc
similarity index 99%
rename from services/device/hid/test_report_descriptors.cc
rename to services/device/public/cpp/test/test_report_descriptors.cc
index fa6f237..d847022 100644
--- a/services/device/hid/test_report_descriptors.cc
+++ b/services/device/public/cpp/test/test_report_descriptors.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/device/hid/test_report_descriptors.h"
+#include "services/device/public/cpp/test/test_report_descriptors.h"
 
 namespace device {
 
diff --git a/services/device/hid/test_report_descriptors.h b/services/device/public/cpp/test/test_report_descriptors.h
similarity index 92%
rename from services/device/hid/test_report_descriptors.h
rename to services/device/public/cpp/test/test_report_descriptors.h
index 6af66049..525b713 100644
--- a/services/device/hid/test_report_descriptors.h
+++ b/services/device/public/cpp/test/test_report_descriptors.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_HID_TEST_REPORT_DESCRIPTORS_H_
-#define SERVICES_DEVICE_HID_TEST_REPORT_DESCRIPTORS_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_TEST_TEST_REPORT_DESCRIPTORS_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_TEST_TEST_REPORT_DESCRIPTORS_H_
 
 #include <stdint.h>
 
@@ -68,4 +68,4 @@
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_HID_TEST_REPORT_DESCRIPTORS_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_TEST_TEST_REPORT_DESCRIPTORS_H_
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1b3b1ee1..ccd5873d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1039,6 +1039,25 @@
             ]
         }
     ],
+    "AutofillFillMerchantPromoCodeFields": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillFillMerchantPromoCodeFields"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillIgnoreAutocompleteForImport": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 1205487..db996d2 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1427,6 +1427,10 @@
 
 const base::Feature kReduceUserAgentPlatformOsCpu{
     "ReduceUserAgentPlatformOsCpu", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<bool> kAllExceptLegacyWindowsPlatform = {
+    &kReduceUserAgentPlatformOsCpu, "all_except_legacy_windows_platform", true};
+const base::FeatureParam<bool> kLegacyWindowsPlatform = {
+    &kReduceUserAgentPlatformOsCpu, "legacy_windows_platform", true};
 
 const base::Feature kReportFCPOnlyOnSuccessfulCommit{
     "ReportFCPOnlyOnSuccessfulCommit", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 767c423..7563eef 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -684,6 +684,10 @@
 
 // If enabled, the platform and oscpu of the User-Agent string will be reduced.
 BLINK_COMMON_EXPORT extern const base::Feature kReduceUserAgentPlatformOsCpu;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
+    kAllExceptLegacyWindowsPlatform;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
+    kLegacyWindowsPlatform;
 
 // If enabled, we only report FCP if there’s a successful commit to the
 // compositor. Otherwise, FCP may be reported if first BeginMainFrame results in
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 856abe9..d0bd271a 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fence.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fence.cc
@@ -83,8 +83,16 @@
       return;
     }
 
-    fenced_frame = DynamicTo<LocalFrame>(
-        DomWindow()->GetFrame()->Top(FrameTreeBoundary::kFenced));
+    Frame* possibly_remote_fenced_frame =
+        DomWindow()->GetFrame()->Top(FrameTreeBoundary::kFenced);
+    if (!frame->GetSecurityContext()->GetSecurityOrigin()->CanAccess(
+            possibly_remote_fenced_frame->GetSecurityContext()
+                ->GetSecurityOrigin())) {
+      AddConsoleMessage(
+          "fence.reportEvent is only available in same-origin subframes.");
+      return;
+    }
+    fenced_frame = DynamicTo<LocalFrame>(possibly_remote_fenced_frame);
   }
 
   DCHECK(fenced_frame);
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 5c7bbb7a..003480a4 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -323,11 +323,12 @@
 // element, and c) this form control supports popup triggering. If multiple
 // toggle attributes are present:
 //  1. Only one idref will ever be used, if multiple attributes are present.
-//  2. If 'togglepopup' is present, its IDREF will be used.
-//  3. If 'showpopup' is present and 'togglepopup' isn't, its IDREF will be
-//  used.
-//  4. If both 'showpopup' and 'hidepopup' are present, the behavior is to
-//  toggle.
+//  2. If 'popuptoggletarget' is present, its IDREF will be used.
+//  3. If 'popupshowtarget' is present and 'popuptoggletarget' isn't present
+//     or its value doesn't match that of popupshowtarget, only popupshowtarget
+//     will be used.
+//  4. If both 'popupshowtarget' and 'popuphidetarget' are present and their
+//     values match, the behavior is to toggle.
 HTMLFormControlElement::PopupTargetElement
 HTMLFormControlElement::popupTargetElement() const {
   const PopupTargetElement no_element{.element = nullptr,
diff --git a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
index d215729..868b538 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
@@ -579,10 +579,13 @@
       const wtf_size_t fragment_table_row =
           table_row - *section_start_row_index;
 
-      // Check if we've exhausted the rows in this section. Store the final row
-      // which we painted.
+      // Check if we've exhausted the rows in this section.
       if (fragment_table_row >= section_row_offsets.size()) {
-        previous_painted_row_index = table_row - 1;
+        // Store the final row which we painted (if it wasn't fragmented).
+        if (is_end_row_fragmented)
+          previous_painted_row_index = absl::nullopt;
+        else
+          previous_painted_row_index = table_row - 1;
         break;
       }
 
diff --git a/third_party/blink/renderer/core/svg/svg_geometry_element.cc b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
index eeeafec..08c731e0 100644
--- a/third_party/blink/renderer/core/svg/svg_geometry_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
@@ -230,7 +230,7 @@
   // However, since 0 * Infinity is not zero (but rather NaN) per
   // IEEE, we need to make sure to clamp the result below - avoiding
   // the actual Infinity (and using max()) instead.
-  return ClampTo<float>(computed_path_length / author_path_length);
+  return ClampTo<float>(computed_path_length / std::fabs(author_path_length));
 }
 
 void SVGGeometryElement::GeometryPresentationAttributeChanged(
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 6c85314..4e546db5 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
@@ -152,11 +152,9 @@
   if (pending_layer.GetCompositingType() == PendingLayer::kScrollHitTestLayer)
     return pending_layer.ScrollTranslationForScrollHitTestLayer();
 
-  const auto& transform = pending_layer.GetPropertyTreeState().Transform();
-  // TODO(pdr): This could be a performance issue because it crawls up the
-  // transform tree for each pending layer. If this is on profiles, we should
-  // cache a lookup of transform node to scroll translation transform node.
-  return transform.NearestScrollTranslationNode();
+  return pending_layer.GetPropertyTreeState()
+      .Transform()
+      .NearestScrollTranslationNode();
 }
 
 namespace {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 5ad1734..6a5328b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -445,9 +445,6 @@
     cc::StickyPositionNodeData& sticky_data =
         transform_tree_.EnsureStickyPositionData(id);
     sticky_data.constraints = *sticky_constraint;
-    // TODO(pdr): This could be a performance issue because it crawls up the
-    // transform tree for each pending layer. If this is on profiles, we should
-    // cache a lookup of transform node to scroll translation transform node.
     const auto& scroll_ancestor = transform_node.NearestScrollTranslationNode();
     sticky_data.scroll_ancestor = EnsureCompositorScrollNode(scroll_ancestor);
     const auto& scroll_ancestor_compositor_node =
@@ -659,9 +656,6 @@
       root_layer_.property_tree_sequence_number());
   mask_layer->SetTransformTreeIndex(
       EnsureCompositorTransformNode(*current_.transform));
-  // TODO(pdr): This could be a performance issue because it crawls up the
-  // transform tree for each pending layer. If this is on profiles, we should
-  // cache a lookup of transform node to scroll translation transform node.
   int scroll_id = EnsureCompositorScrollNode(
       current_.transform->NearestScrollTranslationNode());
   mask_layer->SetScrollTreeIndex(scroll_id);
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
index 338f230..e1b79d9 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
@@ -34,6 +34,9 @@
     root_of_2d_translation_ = &node;
     plane_root_transform_ = nullptr;
     screen_transform_ = nullptr;
+
+    DCHECK(node.ScrollNode());
+    nearest_scroll_translation_ = &node;
     return;
   }
 
@@ -44,6 +47,9 @@
   has_sticky_ =
       node.RequiresCompositingForStickyPosition() || parent.has_sticky_;
 
+  nearest_scroll_translation_ =
+      node.ScrollNode() ? &node : parent.nearest_scroll_translation_;
+
   // screen_transform_ will be updated only when needed.
   screen_transform_ = nullptr;
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h
index 3f429a5..54d2350 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h
@@ -118,6 +118,11 @@
   bool has_fixed() const { return has_fixed_; }
   bool has_sticky() const { return has_sticky_; }
 
+  const TransformPaintPropertyNode& nearest_scroll_translation() const {
+    DCHECK(nearest_scroll_translation_);
+    return *nearest_scroll_translation_;
+  }
+
  private:
   friend class GeometryMapperTransformCacheTest;
 
@@ -214,6 +219,8 @@
   // Whether or not there is a sticky translation to the root.
   bool has_sticky_ = false;
 
+  const TransformPaintPropertyNode* nearest_scroll_translation_ = nullptr;
+
   unsigned cache_generation_ = s_global_generation - 1;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
index 6d8f4692..a2d0a4d 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
 
+#include "base/memory/values_equivalent.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -31,7 +32,7 @@
       compositor_element_id != other.compositor_element_id ||
       scroll != other.scroll ||
       scroll_translation_for_fixed != other.scroll_translation_for_fixed ||
-      !StickyConstraintEquals(other) ||
+      !base::ValuesEquivalent(sticky_constraint, other.sticky_constraint) ||
       anchor_scroll_container != other.anchor_scroll_container ||
       visible_frame_element_id != other.visible_frame_element_id) {
     return PaintPropertyChangeType::kChangedOnlyValues;
@@ -119,18 +120,6 @@
   return relative_to_node.Changed(change, TransformPaintPropertyNode::Root());
 }
 
-const TransformPaintPropertyNode&
-TransformPaintPropertyNode::NearestScrollTranslationNode() const {
-  const auto* transform = this;
-  while (!transform->ScrollNode()) {
-    transform = transform->UnaliasedParent();
-    // The transform should never be null because the root transform has an
-    // associated scroll node (see: TransformPaintPropertyNode::Root()).
-    DCHECK(transform);
-  }
-  return *transform;
-}
-
 std::unique_ptr<JSONObject> TransformPaintPropertyNode::ToJSON() const {
   auto json = ToJSONBase();
   if (IsIdentityOr2DTranslation()) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 16270ae..24079a8 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -192,13 +192,6 @@
     PaintPropertyChangeType ComputeChange(
         const State& other,
         const AnimationState& animation_state) const;
-
-    bool StickyConstraintEquals(const State& other) const {
-      if (!sticky_constraint && !other.sticky_constraint)
-        return true;
-      return sticky_constraint && other.sticky_constraint &&
-             *sticky_constraint == *other.sticky_constraint;
-    }
   };
 
   // This node is really a sentinel, and does not represent a real transform
@@ -285,8 +278,10 @@
 
   // If this is a scroll offset translation (i.e., has an associated scroll
   // node), returns this. Otherwise, returns the transform node that this node
-  // scrolls with respect to. This can require a full ancestor traversal.
-  const TransformPaintPropertyNode& NearestScrollTranslationNode() const;
+  // scrolls with respect to.
+  const TransformPaintPropertyNode& NearestScrollTranslationNode() const {
+    return GetTransformCache().nearest_scroll_translation();
+  }
 
   // If true, content with this transform node (or its descendant) appears in
   // the plane of its parent. This is implemented by flattening the total
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 2648e36..851b4a2 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -11,6 +11,7 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028.html [ Crash ]
 crbug.com/626703 external/wpt/css/cssom/caretPositionFromPoint-with-transformation.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-046.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-048.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index d8eb69c..2516606 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3410,6 +3410,7 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.15 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html [ Failure ]
 crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-images/gradient/gradient-eval-004.html [ Failure ]
 crbug.com/626703 [ Win11 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-data-sanitization.sub.https.html?source-type=navigation [ Failure Timeout ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 53f8ddc..77c2d29c 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 7d1c8152baa7c04ab1d72f88046b6dd2edcb11ed
+Version: 492ebc31d077bcfdd20dfb22f784d20f14a0e529
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 8678113..cdfb879 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
@@ -243644,6 +243644,110 @@
       {}
      ]
     ],
+    "slot-fallback-content-001.html": [
+     "e6e8747c93a1e8d3e6c18d5d3aa440f5b818c0e3",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-001-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-002.html": [
+     "16f2dea0811b2583d147e5896c2a16a1a26caec5",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-002-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-003.html": [
+     "ab7b2af05aa1b7aee94e473aca98139ccaec1fbd",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-003-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-004.html": [
+     "b7ca7144e4f76349b27d156f8a76a97b7f650ddc",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-004-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-005.html": [
+     "37e383ac8cd7fd5779d0234c9047832d95ff9d3c",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-005-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-006.html": [
+     "4708ee7b44ce36b32e03b27f003acacc4106d86a",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-006-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-007.html": [
+     "16471113928d878a02541a49494eca77e0fc23cc",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-007-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "slot-fallback-content-008.html": [
+     "3f93620bba02f652f5e930e315e38f3a71d3a610",
+     [
+      null,
+      [
+       [
+        "/shadow-dom/slot-fallback-content-008-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
     "untriaged": {
      "shadow-trees": {
       "nested-shadow-trees": {
@@ -331197,6 +331301,38 @@
      "d86fd234ca321a762fff5017b4adc68301124bcd",
      []
     ],
+    "slot-fallback-content-001-ref.html": [
+     "5161f0df95f96b1c48a6185b5131589d15fc2b25",
+     []
+    ],
+    "slot-fallback-content-002-ref.html": [
+     "2940bcebaf005da9154c802e4d30f914b40d03ac",
+     []
+    ],
+    "slot-fallback-content-003-ref.html": [
+     "ad735ec81eda25b97c950ee2acaa0d76c8157daf",
+     []
+    ],
+    "slot-fallback-content-004-ref.html": [
+     "85d8f6ef2b7c3a80ae83a49e8d4300ca7235f358",
+     []
+    ],
+    "slot-fallback-content-005-ref.html": [
+     "23bfc2ca53e9d446206798b9d81dc8cbc824b286",
+     []
+    ],
+    "slot-fallback-content-006-ref.html": [
+     "85d53bf53bc687ea282b4713828215a297a65e6d",
+     []
+    ],
+    "slot-fallback-content-007-ref.html": [
+     "992b495273593b48ae1e43b82b248e951c8089c0",
+     []
+    ],
+    "slot-fallback-content-008-ref.html": [
+     "13a3d6b6c99230b4a4d619f04f42008dcca392ad",
+     []
+    ],
     "untriaged": {
      "README": [
       "5b7572bda4e2c2b2477a151345de3441265a8ca8",
@@ -389776,6 +389912,13 @@
         {}
        ]
       ],
+      "contain-intrinsic-size-028.html": [
+       "b65716142058a0b8aa2cd1139cb06cab2fcaa01a",
+       [
+        null,
+        {}
+       ]
+      ],
       "parsing": {
        "contain-intrinsic-size-computed.html": [
         "c03b282699777728e36dba774ecd455d98745532",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028.html
new file mode 100644
index 0000000..b657161
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<title>CSS contain-intrinsic-size: single axis size containment</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#containment-inline-size">
+<style>
+.test {
+  contain: inline-size;
+  display: inline-block;
+  background: green;
+}
+.test::before {
+  content: '';
+  display: block;
+  width: 40px;
+  height: 20px;
+}
+.cis-none {
+  contain-intrinsic-size: none none;
+}
+.cis-height {
+  contain-intrinsic-size: none 50px;
+}
+.cis-width {
+  contain-intrinsic-size: 100px none;
+}
+.cis-both {
+  contain-intrinsic-size: 100px 50px;
+}
+.vertical {
+  writing-mode: vertical-lr;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.test')">
+  <div id="log"></div>
+
+  <div class="test cis-none"
+       data-expected-client-width="0" data-expected-client-height="20"></div>
+  <div class="test cis-height"
+       data-expected-client-width="0" data-expected-client-height="20"></div>
+  <div class="test cis-width"
+       data-expected-client-width="100" data-expected-client-height="20"></div>
+  <div class="test cis-both"
+       data-expected-client-width="100" data-expected-client-height="20"></div>
+
+  <div class="test cis-none vertical"
+       data-expected-client-width="40" data-expected-client-height="0"></div>
+  <div class="test cis-height vertical"
+       data-expected-client-width="40" data-expected-client-height="50"></div>
+  <div class="test cis-width vertical"
+       data-expected-client-width="40" data-expected-client-height="0"></div>
+  <div class="test cis-both vertical"
+       data-expected-client-width="40" data-expected-client-height="50"></div>
+
+  <hr>
+
+  <img class="test cis-none" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="60">
+  <img class="test cis-height" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="60">
+  <img class="test cis-width" src="/css/support/60x60-green.png"
+       data-expected-client-width="100" data-expected-client-height="60">
+  <img class="test cis-both" src="/css/support/60x60-green.png"
+       data-expected-client-width="100" data-expected-client-height="60">
+
+  <img class="test cis-none vertical" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="60">
+  <img class="test cis-height vertical" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="50">
+  <img class="test cis-width vertical" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="60">
+  <img class="test cis-both vertical" src="/css/support/60x60-green.png"
+       data-expected-client-width="60" data-expected-client-height="50">
+
+  <hr>
+
+  <svg class="test cis-none"
+       data-expected-client-width="300" data-expected-client-height="150"></svg>
+  <svg class="test cis-height"
+       data-expected-client-width="300" data-expected-client-height="150"></svg>
+  <svg class="test cis-width"
+       data-expected-client-width="100" data-expected-client-height="150"></svg>
+  <svg class="test cis-both"
+       data-expected-client-width="100" data-expected-client-height="150"></svg>
+
+  <svg class="test cis-none vertical"
+       data-expected-client-width="300" data-expected-client-height="150"></svg>
+  <svg class="test cis-height vertical"
+       data-expected-client-width="300" data-expected-client-height="50"></svg>
+  <svg class="test cis-width vertical"
+       data-expected-client-width="300" data-expected-client-height="150"></svg>
+  <svg class="test cis-both vertical"
+       data-expected-client-width="300" data-expected-client-height="50"></svg>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/svg/crashtests/chrome-bug-1345806.html b/third_party/blink/web_tests/external/wpt/svg/crashtests/chrome-bug-1345806.html
new file mode 100644
index 0000000..afe3029
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/crashtests/chrome-bug-1345806.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1345806">
+<link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org">
+<svg>
+  <path pathLength='-0.0' d='M0 0 L 100 0 L 100 100 L 0 100Z' stroke-dasharray='58,119,146' stroke="green"/>
+</svg>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028-expected.txt
new file mode 100644
index 0000000..3c641a6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-028-expected.txt
@@ -0,0 +1,43 @@
+This is a testharness.js-based test.
+PASS .test 1
+PASS .test 2
+PASS .test 3
+PASS .test 4
+PASS .test 5
+PASS .test 6
+PASS .test 7
+PASS .test 8
+FAIL .test 9 assert_equals: 
+<img class="test cis-none" src="/css/support/60x60-green.png" data-expected-client-width="60" data-expected-client-height="60">
+clientWidth expected 60 but got 0
+FAIL .test 10 assert_equals: 
+<img class="test cis-height" src="/css/support/60x60-green.png" data-expected-client-width="60" data-expected-client-height="60">
+clientWidth expected 60 but got 0
+PASS .test 11
+PASS .test 12
+FAIL .test 13 assert_equals: 
+<img class="test cis-none vertical" src="/css/support/60x60-green.png" data-expected-client-width="60" data-expected-client-height="60">
+clientHeight expected 60 but got 0
+PASS .test 14
+FAIL .test 15 assert_equals: 
+<img class="test cis-width vertical" src="/css/support/60x60-green.png" data-expected-client-width="60" data-expected-client-height="60">
+clientHeight expected 60 but got 0
+PASS .test 16
+FAIL .test 17 assert_equals: 
+<svg class="test cis-none" data-expected-client-width="300" data-expected-client-height="150"></svg>
+clientWidth expected 300 but got 0
+FAIL .test 18 assert_equals: 
+<svg class="test cis-height" data-expected-client-width="300" data-expected-client-height="150"></svg>
+clientWidth expected 300 but got 0
+PASS .test 19
+PASS .test 20
+FAIL .test 21 assert_equals: 
+<svg class="test cis-none vertical" data-expected-client-width="300" data-expected-client-height="150"></svg>
+clientHeight expected 150 but got 0
+PASS .test 22
+FAIL .test 23 assert_equals: 
+<svg class="test cis-width vertical" data-expected-client-width="300" data-expected-client-height="150"></svg>
+clientHeight expected 150 but got 0
+PASS .test 24
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-body-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-body-expected.txt
new file mode 100644
index 0000000..867b5e2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-body-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL non-passive mousewheel event listener on body assert_equals: expected true but got false
+Harness: the test ran to completion.
+
diff --git a/third_party/closure_compiler/externs/developer_private.js b/third_party/closure_compiler/externs/developer_private.js
index 0737a81..054c02fa 100644
--- a/third_party/closure_compiler/externs/developer_private.js
+++ b/third_party/closure_compiler/externs/developer_private.js
@@ -452,7 +452,8 @@
 
 /**
  * @typedef {{
- *   siteList: !chrome.developerPrivate.UserSiteSet,
+ *   siteList: (!chrome.developerPrivate.UserSiteSet|undefined),
+ *   numExtensions: number,
  *   site: string
  * }}
  */
@@ -461,6 +462,7 @@
 /**
  * @typedef {{
  *   etldPlusOne: string,
+ *   numExtensions: number,
  *   sites: !Array<!chrome.developerPrivate.SiteInfo>
  * }}
  */
@@ -845,7 +847,7 @@
 
 /**
  * Returns all hosts specified by user site settings, grouped by each host's
- * eTLD+1. TODO(crbug.com/1331137): Get extension specified sites as well.
+ * eTLD+1.
  * @param {function(!Array<!chrome.developerPrivate.SiteGroup>): void=} callback
  */
 chrome.developerPrivate.getUserAndExtensionSitesByEtld = function(callback) {};
diff --git a/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs b/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
index 8debfd1..6c1c20c5 100644
--- a/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
+++ b/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
@@ -485,8 +485,13 @@
             !self.is_defaulted_function()
     }
 
+    /// Is the referent a bit field declaration?
+    pub fn is_bit_field(&self) -> bool {
+        unsafe { clang_Cursor_isBitField(self.x) != 0 }
+    }
+
     /// Get the width of this cursor's referent bit field, or `None` if the
-    /// referent is not a bit field.
+    /// referent is not a bit field or if the width could not be evaluated.
     pub fn bit_width(&self) -> Option<u32> {
         unsafe {
             let w = clang_getFieldDeclBitWidth(self.x);
@@ -1804,9 +1809,15 @@
                 format!(" {}number-of-template-args = {}", prefix, num),
             );
         }
-        if let Some(width) = c.bit_width() {
-            print_indent(depth, format!(" {}bit-width = {}", prefix, width));
-        }
+
+        // It is not safe to check the bit width without ensuring it doesn't
+        // depend on some template parameter. See
+        // https://github.com/rust-lang/rust-bindgen/issues/2239
+
+        // if let Some(width) = c.bit_width() {
+        //     print_indent(depth, format!(" {}bit-width = {}", prefix, width));
+        // }
+
         if let Some(ty) = c.enum_type() {
             print_indent(
                 depth,
diff --git a/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs b/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
index e5a1a37..58f786f2 100644
--- a/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
+++ b/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
@@ -1062,6 +1062,10 @@
     /// size_t)
     has_non_type_template_params: bool,
 
+    /// Whether this type has a bit field member whose width depends on a
+    /// template parameter. We generate an opaque type in this case.
+    has_template_param_dependent_bit_field_width: bool,
+
     /// Whether we saw `__attribute__((packed))` on or within this type.
     packed_attr: bool,
 
@@ -1096,6 +1100,7 @@
             has_destructor: false,
             has_nonempty_base: false,
             has_non_type_template_params: false,
+            has_template_param_dependent_bit_field_width: false,
             packed_attr: false,
             found_unknown_attr: false,
             is_forward_declaration: false,
@@ -1341,7 +1346,26 @@
                         }
                     }
 
-                    let bit_width = cur.bit_width();
+                    let bit_width = if cur.is_bit_field() {
+                        // Check if the bit width expression depends on any
+                        // template parameters.
+                        if Self::is_field_bit_width_template_dependent(&cur) {
+                            // If so, make type opaque since we can't generate
+                            // bindings for it. See
+                            // https://github.com/rust-lang/rust-bindgen/issues/2239
+                            ci.has_template_param_dependent_bit_field_width =
+                                true;
+                            return CXChildVisit_Break;
+                        }
+
+                        Some(
+                            cur.bit_width()
+                                .expect("could not evaluate bit width?"),
+                        )
+                    } else {
+                        None
+                    };
+
                     let field_type = Item::from_ty_or_ref(
                         cur.cur_type(),
                         cur,
@@ -1718,6 +1742,71 @@
 
         true
     }
+
+    // Check whether a bit field's bit width expression contains any references
+    // to template parameters. If so, we don't evaluate its width.
+    fn is_field_bit_width_template_dependent(cursor: &clang::Cursor) -> bool {
+        use clang_sys::*;
+
+        if !cursor.is_bit_field() {
+            return false;
+        }
+
+        fn visitor(
+            found_template_parameter: &mut bool,
+            cur: clang::Cursor,
+        ) -> CXChildVisitResult {
+            // The next expression or literal is the bit width. Search it for
+            // references to template parameters.
+            if let Some(referenced) = cur.referenced() {
+                match referenced.kind() {
+                    // If we found a template argument, it is dependent.
+                    CXCursor_TemplateTemplateParameter |
+                    CXCursor_TemplateTypeParameter |
+                    CXCursor_NonTypeTemplateParameter => {
+                        *found_template_parameter = true;
+                        return CXChildVisit_Break;
+                    }
+                    // If we found a type alias or typedef, follow it and check
+                    // there.
+                    CXCursor_TypedefDecl |
+                    CXCursor_TypeAliasDecl |
+                    CXCursor_TypeAliasTemplateDecl => {
+                        referenced.visit(|cur| {
+                            visitor(found_template_parameter, cur)
+                        });
+                        if *found_template_parameter {
+                            return CXChildVisit_Break;
+                        }
+                    }
+                    _ => (),
+                }
+            }
+
+            CXChildVisit_Recurse
+        }
+
+        let mut found_template_parameter = false;
+
+        cursor.visit(|cur| {
+            // The first child may or may not be a TypeRef, depending on whether
+            // the field's type is builtin or not. Skip this.
+            if cur.kind() == CXCursor_TypeRef {
+                return CXChildVisit_Continue;
+            }
+
+            // The next expression or literal is the bit width. Search for
+            // references to template parameters.
+            cur.visit(|next_cur| {
+                visitor(&mut found_template_parameter, next_cur)
+            });
+
+            // There should be no more children.
+            CXChildVisit_Break
+        });
+
+        found_template_parameter
+    }
 }
 
 impl DotAttributes for CompInfo {
@@ -1777,7 +1866,9 @@
     type Extra = Option<Layout>;
 
     fn is_opaque(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool {
-        if self.has_non_type_template_params {
+        if self.has_non_type_template_params ||
+            self.has_template_param_dependent_bit_field_width
+        {
             return true;
         }
 
diff --git a/third_party/rust/autocxx_bindgen/v0_59/patches/0001-Workaround-for-template-dependent-bitfield-issue.patch b/third_party/rust/autocxx_bindgen/v0_59/patches/0001-Workaround-for-template-dependent-bitfield-issue.patch
new file mode 100644
index 0000000..697fa30
--- /dev/null
+++ b/third_party/rust/autocxx_bindgen/v0_59/patches/0001-Workaround-for-template-dependent-bitfield-issue.patch
@@ -0,0 +1,190 @@
+From dba24640c968f5524ead3ee58d89b1f35505de5b Mon Sep 17 00:00:00 2001
+From: Collin Baker <collinbaker@chromium.org>
+Date: Fri, 22 Jul 2022 16:06:39 -0400
+Subject: [PATCH] Workaround for template-dependent bitfield issue
+
+See https://github.com/rust-lang/rust-bindgen/pull/2243 and
+https://github.com/adetaylor/rust-bindgen/pull/8 for upstream PRs
+
+This patch applies the fix locally until the upstream changes are
+accepted.
+---
+ .../autocxx_bindgen/v0_59/crate/src/clang.rs  | 19 +++-
+ .../v0_59/crate/src/ir/comp.rs                | 95 ++++++++++++++++++-
+ 2 files changed, 108 insertions(+), 6 deletions(-)
+
+diff --git a/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs b/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
+index 8debfd12e58c1..6c1c20c545ee4 100644
+--- a/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
++++ b/third_party/rust/autocxx_bindgen/v0_59/crate/src/clang.rs
+@@ -485,8 +485,13 @@ impl Cursor {
+             !self.is_defaulted_function()
+     }
+ 
++    /// Is the referent a bit field declaration?
++    pub fn is_bit_field(&self) -> bool {
++        unsafe { clang_Cursor_isBitField(self.x) != 0 }
++    }
++
+     /// Get the width of this cursor's referent bit field, or `None` if the
+-    /// referent is not a bit field.
++    /// referent is not a bit field or if the width could not be evaluated.
+     pub fn bit_width(&self) -> Option<u32> {
+         unsafe {
+             let w = clang_getFieldDeclBitWidth(self.x);
+@@ -1804,9 +1809,15 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
+                 format!(" {}number-of-template-args = {}", prefix, num),
+             );
+         }
+-        if let Some(width) = c.bit_width() {
+-            print_indent(depth, format!(" {}bit-width = {}", prefix, width));
+-        }
++
++        // It is not safe to check the bit width without ensuring it doesn't
++        // depend on some template parameter. See
++        // https://github.com/rust-lang/rust-bindgen/issues/2239
++
++        // if let Some(width) = c.bit_width() {
++        //     print_indent(depth, format!(" {}bit-width = {}", prefix, width));
++        // }
++
+         if let Some(ty) = c.enum_type() {
+             print_indent(
+                 depth,
+diff --git a/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs b/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
+index e5a1a3744a97a..58f786f22a6ce 100644
+--- a/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
++++ b/third_party/rust/autocxx_bindgen/v0_59/crate/src/ir/comp.rs
+@@ -1062,6 +1062,10 @@ pub struct CompInfo {
+     /// size_t)
+     has_non_type_template_params: bool,
+ 
++    /// Whether this type has a bit field member whose width depends on a
++    /// template parameter. We generate an opaque type in this case.
++    has_template_param_dependent_bit_field_width: bool,
++
+     /// Whether we saw `__attribute__((packed))` on or within this type.
+     packed_attr: bool,
+ 
+@@ -1096,6 +1100,7 @@ impl CompInfo {
+             has_destructor: false,
+             has_nonempty_base: false,
+             has_non_type_template_params: false,
++            has_template_param_dependent_bit_field_width: false,
+             packed_attr: false,
+             found_unknown_attr: false,
+             is_forward_declaration: false,
+@@ -1341,7 +1346,26 @@ impl CompInfo {
+                         }
+                     }
+ 
+-                    let bit_width = cur.bit_width();
++                    let bit_width = if cur.is_bit_field() {
++                        // Check if the bit width expression depends on any
++                        // template parameters.
++                        if Self::is_field_bit_width_template_dependent(&cur) {
++                            // If so, make type opaque since we can't generate
++                            // bindings for it. See
++                            // https://github.com/rust-lang/rust-bindgen/issues/2239
++                            ci.has_template_param_dependent_bit_field_width =
++                                true;
++                            return CXChildVisit_Break;
++                        }
++
++                        Some(
++                            cur.bit_width()
++                                .expect("could not evaluate bit width?"),
++                        )
++                    } else {
++                        None
++                    };
++
+                     let field_type = Item::from_ty_or_ref(
+                         cur.cur_type(),
+                         cur,
+@@ -1718,6 +1742,71 @@ impl CompInfo {
+ 
+         true
+     }
++
++    // Check whether a bit field's bit width expression contains any references
++    // to template parameters. If so, we don't evaluate its width.
++    fn is_field_bit_width_template_dependent(cursor: &clang::Cursor) -> bool {
++        use clang_sys::*;
++
++        if !cursor.is_bit_field() {
++            return false;
++        }
++
++        fn visitor(
++            found_template_parameter: &mut bool,
++            cur: clang::Cursor,
++        ) -> CXChildVisitResult {
++            // The next expression or literal is the bit width. Search it for
++            // references to template parameters.
++            if let Some(referenced) = cur.referenced() {
++                match referenced.kind() {
++                    // If we found a template argument, it is dependent.
++                    CXCursor_TemplateTemplateParameter |
++                    CXCursor_TemplateTypeParameter |
++                    CXCursor_NonTypeTemplateParameter => {
++                        *found_template_parameter = true;
++                        return CXChildVisit_Break;
++                    }
++                    // If we found a type alias or typedef, follow it and check
++                    // there.
++                    CXCursor_TypedefDecl |
++                    CXCursor_TypeAliasDecl |
++                    CXCursor_TypeAliasTemplateDecl => {
++                        referenced.visit(|cur| {
++                            visitor(found_template_parameter, cur)
++                        });
++                        if *found_template_parameter {
++                            return CXChildVisit_Break;
++                        }
++                    }
++                    _ => (),
++                }
++            }
++
++            CXChildVisit_Recurse
++        }
++
++        let mut found_template_parameter = false;
++
++        cursor.visit(|cur| {
++            // The first child may or may not be a TypeRef, depending on whether
++            // the field's type is builtin or not. Skip this.
++            if cur.kind() == CXCursor_TypeRef {
++                return CXChildVisit_Continue;
++            }
++
++            // The next expression or literal is the bit width. Search for
++            // references to template parameters.
++            cur.visit(|next_cur| {
++                visitor(&mut found_template_parameter, next_cur)
++            });
++
++            // There should be no more children.
++            CXChildVisit_Break
++        });
++
++        found_template_parameter
++    }
+ }
+ 
+ impl DotAttributes for CompInfo {
+@@ -1777,7 +1866,9 @@ impl IsOpaque for CompInfo {
+     type Extra = Option<Layout>;
+ 
+     fn is_opaque(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool {
+-        if self.has_non_type_template_params {
++        if self.has_non_type_template_params ||
++            self.has_template_param_dependent_bit_field_width
++        {
+             return true;
+         }
+ 
+-- 
+2.37.1.359.gd136c6c3e2-goog
+
diff --git a/tools/crates/gnrt/deps.rs b/tools/crates/gnrt/deps.rs
index 391e370..723a2053 100644
--- a/tools/crates/gnrt/deps.rs
+++ b/tools/crates/gnrt/deps.rs
@@ -230,10 +230,19 @@
         // needed for build file generation later.
         for node_dep in iter_node_deps(node) {
             let dep_pkg = dep_graph.packages.get(node_dep.pkg).unwrap();
+
+            // Get the platform filter and remove terms with unsupported
+            // configurations.
+            let mut platform = node_dep.target;
+            if let Some(p) = platform {
+                assert!(platforms::matches_supported_target(&p));
+                platform = platforms::filter_unsupported_platform_terms(p);
+            }
+
             let dep_of_dep = DepOfDep {
                 normalized_name: NormalizedName::from_crate_name(&dep_pkg.name),
                 epoch: epoch_from_version(&dep_pkg.version),
-                platform: node_dep.target,
+                platform,
             };
 
             match node_dep.kind {
@@ -350,7 +359,7 @@
                 match &dep_kind_info.target {
                     None => (),
                     Some(platform) => {
-                        if !platforms::is_supported(platform) {
+                        if !platforms::matches_supported_target(platform) {
                             return None;
                         }
                     }
diff --git a/tools/crates/gnrt/platforms.rs b/tools/crates/gnrt/platforms.rs
index ef6c42d..ab580d5 100644
--- a/tools/crates/gnrt/platforms.rs
+++ b/tools/crates/gnrt/platforms.rs
@@ -48,8 +48,8 @@
 }
 
 /// Whether `platform`, either an explicit rustc target triple or a `cfg(...)`
-/// expression, is supported in Chromium.
-pub fn is_supported(platform: &Platform) -> bool {
+/// expression, matches any build target supported by Chromium.
+pub fn matches_supported_target(platform: &Platform) -> bool {
     match platform {
         Platform::Name(name) => SUPPORTED_NAMED_PLATFORMS.iter().any(|p| *p == name),
         Platform::Cfg(cfg_expr) => {
@@ -58,6 +58,35 @@
     }
 }
 
+/// Remove terms containing unsupported platforms from `platform`, assuming
+/// `matches_supported_target(&platform)` is true.
+///
+/// `platform` may contain a cfg(...) expression referencing platforms we don't
+/// support: for example, `cfg(any(unix, target_os = "wasi"))`. However, such an
+/// expression may still be true on configurations we do support.
+///
+/// `filter_unsupported_platform_terms` returns a new platform filter without
+/// unsupported terms that is logically equivalent for the set of platforms we
+/// do support, or `None` if the new filter would be true for all supported
+/// platforms. This is useful when generating conditional expressions in build
+/// files from such a cfg(...) expression.
+///
+/// Assumes `matches_supported_target(&platform)` is true. If not, the function
+/// may return an invalid result or panic.
+pub fn filter_unsupported_platform_terms(platform: Platform) -> Option<Platform> {
+    use ExprValidity::*;
+    match platform {
+        // If it's a target name, do nothing since `is_supported` is true.
+        x @ Platform::Name(_) => Some(x),
+        // Rewrite `cfg_expr` to be valid.
+        Platform::Cfg(mut cfg_expr) => match cfg_expr_filter_visitor(&mut cfg_expr) {
+            Valid => Some(Platform::Cfg(cfg_expr)),
+            AlwaysTrue => None,
+            AlwaysFalse => unreachable!("cfg would be false on all supported platforms"),
+        },
+    }
+}
+
 pub fn supported_os_cfgs_for_testing() -> &'static [Cfg] {
     supported_os_cfgs()
 }
@@ -66,6 +95,109 @@
     SUPPORTED_NAMED_PLATFORMS
 }
 
+// The validity of a cfg expr for our set of supported platforms.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum ExprValidity {
+    // Contains only terms for supported platforms.
+    Valid,
+    // Contains terms for unsupported platforms, and would evaluate to true on
+    // all supported platforms.
+    AlwaysTrue,
+    // Contains terms for unsupported platforms, and would evaluate to false on
+    // all supported platforms.
+    AlwaysFalse,
+}
+
+// Rewrites `cfg_expr` to exclude unsupported terms. `ExprValidity::Valid` if
+// the rewritten expr is valid: it contains no unsupported terms. Otherwise
+// returns `AlwaysTrue` or `AlwaysFalse`.
+fn cfg_expr_filter_visitor(cfg_expr: &mut cargo_platform::CfgExpr) -> ExprValidity {
+    use ExprValidity::*;
+    // Any logical operation on a set of valid expressions also yields a valid
+    // expression. If any of the set is invalid, we must apply special handling
+    // to remove the invalid term or decide the expression is always true or
+    // false.
+    match cfg_expr {
+        // A not(...) expr inverts the truth value of an invalid expr.
+        cargo_platform::CfgExpr::Not(sub_expr) => match cfg_expr_filter_visitor(sub_expr) {
+            Valid => Valid,
+            AlwaysTrue => AlwaysFalse,
+            AlwaysFalse => AlwaysTrue,
+        },
+        // An all(...) expr is always false if any term is always false. If any
+        // term is always true, it can be removed.
+        cargo_platform::CfgExpr::All(sub_exprs) => {
+            let mut validity = Valid;
+            sub_exprs.retain_mut(|e| match cfg_expr_filter_visitor(e) {
+                // Keep valid terms.
+                Valid => true,
+                // Remove always-true terms.
+                AlwaysTrue => false,
+                // If a term is always false, it doesn't matter; we will discard
+                // this expr.
+                AlwaysFalse => {
+                    validity = AlwaysFalse;
+                    true
+                }
+            });
+            if validity == AlwaysFalse {
+                AlwaysFalse
+            } else if sub_exprs.is_empty() {
+                // We only reach this if all the terms we removed were always
+                // true, in which case the expression is always true.
+                AlwaysTrue
+            } else if sub_exprs.len() == 1 {
+                // If only one term remains, we can simplify by replacing
+                // all(<term>) with <term>.
+                let new_expr = sub_exprs.drain(..).next().unwrap();
+                *cfg_expr = new_expr;
+                Valid
+            } else {
+                Valid
+            }
+        }
+        // An any(...) expr is always true if any term is always true. If any
+        // term is always false, it can be removed.
+        cargo_platform::CfgExpr::Any(sub_exprs) => {
+            let mut validity = Valid;
+            sub_exprs.retain_mut(|e| match cfg_expr_filter_visitor(e) {
+                // Keep valid terms.
+                Valid => true,
+                // If a term is always true, it doesn't matter; we will discard
+                // this expr.
+                AlwaysTrue => {
+                    validity = AlwaysTrue;
+                    true
+                }
+                // Remove always-false terms.
+                AlwaysFalse => false,
+            });
+            if validity == AlwaysTrue {
+                AlwaysTrue
+            } else if sub_exprs.is_empty() {
+                // We only reach this if all the terms we removed were always
+                // false, in which case the expression is always false.
+                AlwaysFalse
+            } else if sub_exprs.len() == 1 {
+                // If only one term remains, we can simplify by replacing
+                // any(<term>) with <term>.
+                let new_expr = sub_exprs.drain(..).next().unwrap();
+                *cfg_expr = new_expr;
+                Valid
+            } else {
+                Valid
+            }
+        }
+        cargo_platform::CfgExpr::Value(cfg) => {
+            if supported_os_cfgs().iter().any(|c| c == cfg) {
+                Valid
+            } else {
+                AlwaysFalse
+            }
+        }
+    }
+}
+
 fn supported_os_cfgs() -> &'static [Cfg] {
     static CFG_SET: OnceCell<Vec<Cfg>> = OnceCell::new();
     CFG_SET.get_or_init(|| {
diff --git a/tools/crates/gnrt/platforms_unittest.rs b/tools/crates/gnrt/platforms_unittest.rs
index 8eb60dc..a94a829 100644
--- a/tools/crates/gnrt/platforms_unittest.rs
+++ b/tools/crates/gnrt/platforms_unittest.rs
@@ -6,29 +6,61 @@
 
 use gnrt_lib::platforms::*;
 
+use cargo_platform::{CfgExpr, Platform};
+use std::str::FromStr;
+
 #[gtest(PlatformTest, PlatformIsSupported)]
 fn test() {
-    use cargo_platform::{CfgExpr, Platform};
-    use std::str::FromStr;
-
     for named_platform in supported_named_platforms_for_testing() {
-        expect_true!(is_supported(&Platform::Name(named_platform.to_string())));
+        expect_true!(matches_supported_target(&Platform::Name(named_platform.to_string())));
     }
 
-    expect_false!(is_supported(&Platform::Name("x86_64-unknown-redox".to_string())));
-    expect_false!(is_supported(&Platform::Name("wasm32-wasi".to_string())));
+    expect_false!(matches_supported_target(&Platform::Name("x86_64-unknown-redox".to_string())));
+    expect_false!(matches_supported_target(&Platform::Name("wasm32-wasi".to_string())));
 
     for os in supported_os_cfgs_for_testing() {
-        expect_true!(is_supported(&Platform::Cfg(CfgExpr::Value(os.clone()))));
+        expect_true!(matches_supported_target(&Platform::Cfg(CfgExpr::Value(os.clone()))));
     }
 
-    expect_false!(is_supported(&Platform::Cfg(
+    expect_false!(matches_supported_target(&Platform::Cfg(
         CfgExpr::from_str("target_os = \"redox\"").unwrap()
     )));
-    expect_false!(is_supported(&Platform::Cfg(
+    expect_false!(matches_supported_target(&Platform::Cfg(
         CfgExpr::from_str("target_os = \"haiku\"").unwrap()
     )));
-    expect_false!(is_supported(&Platform::Cfg(
+    expect_false!(matches_supported_target(&Platform::Cfg(
         CfgExpr::from_str("target_arch = \"sparc\"").unwrap()
     )));
+
+    expect_true!(matches_supported_target(&Platform::Cfg(
+        CfgExpr::from_str("any(unix, target_os = \"wasi\")").unwrap()
+    )));
+
+    expect_false!(matches_supported_target(&Platform::Cfg(
+        CfgExpr::from_str("all(unix, target_os = \"wasi\")").unwrap()
+    )));
+}
+
+#[gtest(PlatformTest, FilterUnsupported)]
+fn test() {
+    expect_eq!(
+        filter_unsupported_platform_terms(Platform::Cfg(
+            CfgExpr::from_str("any(unix, target_os = \"wasi\")").unwrap()
+        )),
+        Some(Platform::Cfg(CfgExpr::from_str("unix").unwrap()))
+    );
+
+    expect_eq!(
+        filter_unsupported_platform_terms(Platform::Cfg(
+            CfgExpr::from_str("all(not(unix), not(target_os = \"wasi\"))").unwrap()
+        )),
+        Some(Platform::Cfg(CfgExpr::from_str("not(unix)").unwrap()))
+    );
+
+    expect_eq!(
+        filter_unsupported_platform_terms(Platform::Cfg(
+            CfgExpr::from_str("not(target_os = \"wasi\")").unwrap()
+        )),
+        None
+    );
 }
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 67789c8b..86afcf8 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -6211,6 +6211,15 @@
   </description>
 </action>
 
+<action name="ContentSuggestions.Feed.CardAction.OpenInNewTabInGroup">
+  <owner>jianli@chromium.org</owner>
+  <owner>harringtond@chromium.org</owner>
+  <owner>feed@chromium.org</owner>
+  <description>
+    The user selected the 'open in new tab in group' option for a Feed card.
+  </description>
+</action>
+
 <action name="ContentSuggestions.Feed.CardAction.ReadLater">
   <owner>adamta@google.org</owner>
   <owner>sczs@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e309f9ed..0e57f206 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -58345,6 +58345,7 @@
   <int value="-303467388" label="ShimlessRMAEnableStandalone:enabled"/>
   <int value="-302948746" label="DeferBeginMainFrameDuringLoading:disabled"/>
   <int value="-302407285" label="WipeDataOnChildAccountSignin:disabled"/>
+  <int value="-302078301" label="RequestDesktopSiteDefaults:enabled"/>
   <int value="-301689561" label="PermissionQuietChip:disabled"/>
   <int value="-301228557" label="PageInfoDiscoverability:disabled"/>
   <int value="-300018686" label="disable-cloud-import"/>
@@ -58962,6 +58963,7 @@
   <int value="98218116" label="ContextualSearchLongpressResolve:disabled"/>
   <int value="99177659" label="OmniboxUICuesForSearchHistoryMatches:disabled"/>
   <int value="99660832" label="DockedMagnifierResizing:enabled"/>
+  <int value="101253756" label="RequestDesktopSiteDefaults:disabled"/>
   <int value="103347629" label="WinrtSensorsImplementation:enabled"/>
   <int value="103560544" label="ViewportHeightClientHintHeader:disabled"/>
   <int value="103932290" label="show-autofill-type-predictions"/>
@@ -59893,6 +59895,7 @@
   <int value="698273842" label="SyncSendTabToSelf:enabled"/>
   <int value="698809951" label="WebRtcHWVP8Encoding:enabled"/>
   <int value="699149897" label="ContentSuggestionsDebugLog:disabled"/>
+  <int value="699632009" label="NewTabPageTilesTitleWrapAround:enabled"/>
   <int value="700346797" label="WheelEventRegions:enabled"/>
   <int value="700926106" label="OmniboxPedalsBatch3NonEnglish:enabled"/>
   <int value="701766325" label="PerNavigationMojoInterface:enabled"/>
@@ -60186,6 +60189,7 @@
   <int value="879891709"
       label="ExperimentalAccessibilityDictationWithPumpkin:enabled"/>
   <int value="879992337" label="disable-pull-to-refresh-effect"/>
+  <int value="880201245" label="NewTabPageTilesTitleWrapAround:disabled"/>
   <int value="880510010" label="enable-permissions-bubbles"/>
   <int value="882893584" label="UseOfHashAffiliationFetcher:disabled"/>
   <int value="883190338" label="PrintWithReducedRasterization:disabled"/>
@@ -85974,6 +85978,11 @@
   <int value="7" label="Not enough results"/>
 </enum>
 
+<enum name="SearchResumptionModule.ModuleShowStatus">
+  <int value="0" label="Module is shown and expanded"/>
+  <int value="1" label="Module is shown and collapsed"/>
+</enum>
+
 <enum name="SearchTemplateURLEvent">
   <summary>
     Events within the TemplateURL system we log, i.e. edge cases.
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index d7ca1af..451b6e5 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -95,7 +95,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.DeviceDisconnect"
-    enum="BluetoothDeviceType" expires_after="2023-01-24">
+    enum="BluetoothDeviceType" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>Emitted each time a Bluetooth device is disconnected.</summary>
@@ -1028,7 +1028,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.Pairing.Duration.Failure{BluetoothTransportTypes}"
-    units="ms" expires_after="2023-01-23">
+    units="ms" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1044,7 +1044,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.Pairing.Duration.Success{BluetoothTransportTypes}"
-    units="ms" expires_after="2023-01-23">
+    units="ms" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1095,7 +1095,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.Pairing.TransportType"
-    enum="BluetoothTransportType" expires_after="2023-01-23">
+    enum="BluetoothTransportType" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1106,7 +1106,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.PoweredState" enum="BooleanEnabled"
-    expires_after="2022-11-27">
+    expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1129,14 +1129,14 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.SetNickname.Result"
-    enum="SetNicknameResult" expires_after="2022-12-10">
+    enum="SetNicknameResult" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>Emitted each time a nickname change attempt completes.</summary>
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.UiSurfaceDisplayed"
-    enum="BluetoothUiSurface" expires_after="2022-12-04">
+    enum="BluetoothUiSurface" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>Metric emmitted each time a UI surface is shown.</summary>
@@ -1144,7 +1144,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.UserInitiatedDisconnect.Result{BluetoothTransportTypes}"
-    enum="DisconnectResult" expires_after="2023-01-24">
+    enum="DisconnectResult" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1158,7 +1158,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Duration.Failure{BluetoothTransportTypes}"
-    units="ms" expires_after="2023-06-19">
+    units="ms" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1186,7 +1186,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Result.FailureReason{UserInitiatedReconnectionUISurfaces}"
-    enum="BluetoothConnectionFailureReason" expires_after="2023-01-23">
+    enum="BluetoothConnectionFailureReason" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -1205,7 +1205,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Result{UserInitiatedReconnectionUISurfaces}"
-    enum="BooleanSuccess" expires_after="2022-09-14">
+    enum="BooleanSuccess" expires_after="2023-06-21">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 821a2d9..843e898c 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -1216,25 +1216,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Experimental.Total2.PrivateMemoryFootprint" units="MB"
-    expires_after="2023-01-10">
-  <owner>erikchen@chromium.org</owner>
-  <owner>ssid@chromium.org</owner>
-  <summary>
-    A rough estimate of the private memory footprint of all processes.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This memory footprint metric cannot be compared across platforms because
-    each platform relies on platform-level APIs for accounting. As such, though
-    this attempts to measure private memory footprint as best as possible, it
-    does not measure the same thing on each platform. We have not found a good
-    way to compare any system level memory metric across platforms due to the
-    different nature of memory management on each platform.
-  </summary>
-</histogram>
-
 <histogram
     name="Memory.Experimental.UserLevelMemoryPressureSignal.RendererPrivateMemoryFootprintAfter"
     units="MB" expires_after="2019-09-30">
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 231f798..5d78fb1 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -1685,6 +1685,17 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.SearchResumptionModule.Show"
+    enum="SearchResumptionModule.ModuleShowStatus" expires_after="2022-10-31">
+  <owner>hanxi@chromium.org</owner>
+  <owner>spdonghao@chromium.org</owner>
+  <summary>
+    Logs whether the search resumption module is expanded or collapsed when the
+    module is shown on the NTP. Recorded when the NewTabPage is created and the
+    search resumption module is shown.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.ShoppingTasks.ProductClick" units="index"
     expires_after="2022-11-20">
   <owner>danpeng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 0567da6..bc486c6 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -5764,6 +5764,17 @@
   </summary>
 </histogram>
 
+<histogram name="Feedback.ChromeOSApp.Openduration" units="ms"
+    expires_after="2023-07-15">
+  <owner>longbowei@google.com</owner>
+  <owner>xiangdongkong@google.com</owner>
+  <owner>cros-feedback-app@google.com</owner>
+  <summary>
+    Records the duration that the Feedback App is open. Emitted when the app is
+    closed.
+  </summary>
+</histogram>
+
 <histogram name="Feedback.Duration.FetchSystemInformation" units="ms"
     expires_after="2023-03-25">
   <owner>xiangdongkong@google.com</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 2e6e7d22..c0907e1 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3090,6 +3090,36 @@
   </metric>
 </event>
 
+<event name="Blink.DeveloperMetricsRare">
+  <owner>schenney@chromium.org</owner>
+  <owner>chrome-webdev-metrics@google.com</owner>
+  <summary>
+    Collects data for a subset of UseCounter features of interest to the Web
+    Platform Developer Metrics team due to their recent launch or potential for
+    spec changes. Features reported in this set should be rare, appearing in
+    fewer than 5% of page loads.
+  </summary>
+  <metric name="Feature" enum="FeatureObserver">
+    <summary>
+      Opt-in UseCounter feature, listed in
+      components/page_load_metrics/browser/observers/use_counter/webdev_metrics_ukm_features.cc
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+  <metric name="IsMainFrameFeature" enum="Boolean">
+    <summary>
+      Emits True(1) or False(0) to indicate whether the Feature recorded is in
+      the main frame or not.
+    </summary>
+  </metric>
+</event>
+
 <event name="Blink.FedCm">
   <owner>yigu@chromium.org</owner>
   <owner>webid-core@google.com</owner>
diff --git a/tools/perf/core/minidump_unittest.py b/tools/perf/core/minidump_unittest.py
index 905ffb39..e1b5898 100644
--- a/tools/perf/core/minidump_unittest.py
+++ b/tools/perf/core/minidump_unittest.py
@@ -65,6 +65,7 @@
   @decorators.Disabled(
       'chromeos-local',
       'win7',  # https://crbug.com/1084931
+      'android-nougat',  # Flaky: https://crbug.com/1342706
   )
   def testSymbolizeMinidump(self):
     # Wait for the browser to restart fully before crashing
@@ -110,6 +111,7 @@
   @decorators.Disabled(
       'chromeos-local',
       'win7',  # https://crbug.com/1084931
+      'android-nougat',  # Flaky: https://crbug.com/1342706
   )
   def testMultipleCrashMinidumps(self):
     # Wait for the browser to restart fully before crashing
@@ -202,6 +204,7 @@
   @decorators.Disabled(
       'chromeos-local',
       'win7',  # https://crbug.com/1084931
+      'android-nougat',  # Flaky: https://crbug.com/1342706
   )
   def testMinidumpFromRendererHang(self):
     """Tests that renderer hangs result in minidumps.
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index dda11a2..dcc8fa9 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "4e48210501240e4874296fa74880b889e1a45118",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/209791f79d097387b721b2121ae5b54c4d4facb3/trace_processor_shell.exe"
+            "hash": "d7e53918c0f1864c0da750f3eaf6c91d5fc0953c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/86c920c3179e3abff64a8ed736b11ca3ac9998a5/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "089843aacd95210b425e410669c98cb1e00f93f2",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/209791f79d097387b721b2121ae5b54c4d4facb3/trace_processor_shell"
+            "hash": "05d75c83cb2794e125f66c042ae6fb21f185ef51",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/86c920c3179e3abff64a8ed736b11ca3ac9998a5/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "a8521c27577bae49cbc934a246eae4673a303f6d",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/209791f79d097387b721b2121ae5b54c4d4facb3/trace_processor_shell"
+            "hash": "5faecc89babc29a2f6475a37cb422d19854364b8",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/86c920c3179e3abff64a8ed736b11ca3ac9998a5/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/android/java/src/org/chromium/ui/display/DisplayAndroid.java b/ui/android/java/src/org/chromium/ui/display/DisplayAndroid.java
index fbed317..385a785 100644
--- a/ui/android/java/src/org/chromium/ui/display/DisplayAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/display/DisplayAndroid.java
@@ -70,6 +70,8 @@
     private final int mDisplayId;
     private Point mSize;
     private float mDipScale;
+    private float mXdpi;
+    private float mYdpi;
     private int mBitsPerPixel;
     private int mBitsPerComponent;
     private int mRotation;
@@ -162,6 +164,20 @@
     }
 
     /**
+     * @return The exact physical pixels per inch of the screen in the X dimension.
+     */
+    public float getXdpi() {
+        return mXdpi;
+    }
+
+    /**
+     * @return The exact physical pixels per inch of the screen in the Y dimension.
+     */
+    public float getYdpi() {
+        return mYdpi;
+    }
+
+    /**
      * You probably do not want to use this function.
      *
      * In VR, there's a mismatch between the dip scale reported by getDipScale and the dip scale
@@ -251,7 +267,8 @@
     }
 
     public void updateIsDisplayServerWideColorGamut(Boolean isDisplayServerWideColorGamut) {
-        update(null, null, null, null, null, null, isDisplayServerWideColorGamut, null, null, null);
+        update(null, null, null, null, null, null, null, null, isDisplayServerWideColorGamut, null,
+                null, null);
     }
 
     /**
@@ -262,10 +279,25 @@
             Integer bitsPerComponent, Integer rotation, Boolean isDisplayWideColorGamut,
             Boolean isDisplayServerWideColorGamut, Float refreshRate, Display.Mode currentMode,
             List<Display.Mode> supportedModes) {
+        update(size, dipScale, null, null, bitsPerPixel, bitsPerComponent, rotation,
+                isDisplayWideColorGamut, isDisplayServerWideColorGamut, refreshRate, currentMode,
+                supportedModes);
+    }
+
+    /**
+     * Update the display to the provided parameters. Null values leave the parameter unchanged.
+     */
+    @SuppressLint("NewApi")
+    protected void update(Point size, Float dipScale, Float xdpi, Float ydpi, Integer bitsPerPixel,
+            Integer bitsPerComponent, Integer rotation, Boolean isDisplayWideColorGamut,
+            Boolean isDisplayServerWideColorGamut, Float refreshRate, Display.Mode currentMode,
+            List<Display.Mode> supportedModes) {
         boolean sizeChanged = size != null && !mSize.equals(size);
         // Intentional comparison of floats: we assume that if scales differ, they differ
         // significantly.
         boolean dipScaleChanged = dipScale != null && mDipScale != dipScale;
+        boolean xdpiChanged = xdpi != null && mXdpi != xdpi;
+        boolean ydpiChanged = ydpi != null && mYdpi != ydpi;
         boolean bitsPerPixelChanged = bitsPerPixel != null && mBitsPerPixel != bitsPerPixel;
         boolean bitsPerComponentChanged =
                 bitsPerComponent != null && mBitsPerComponent != bitsPerComponent;
@@ -288,6 +320,8 @@
 
         if (sizeChanged) mSize = size;
         if (dipScaleChanged) mDipScale = dipScale;
+        if (xdpiChanged) mXdpi = xdpi;
+        if (ydpiChanged) mYdpi = ydpi;
         if (bitsPerPixelChanged) mBitsPerPixel = bitsPerPixel;
         if (bitsPerComponentChanged) mBitsPerComponent = bitsPerComponent;
         if (rotationChanged) mRotation = rotation;
diff --git a/ui/android/java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java b/ui/android/java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java
index 86d2fae3..a996ad7f 100644
--- a/ui/android/java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java
@@ -166,7 +166,8 @@
         Rect rect = ApiHelperForR.getMaximumWindowMetricsBounds(windowManager);
         size.set(rect.width(), rect.height());
         DisplayMetrics displayMetrics = mWindowContext.getResources().getDisplayMetrics();
-        updateCommon(size, displayMetrics.density, ApiHelperForR.getDisplay(mWindowContext));
+        updateCommon(size, displayMetrics.density, displayMetrics.xdpi, displayMetrics.ydpi,
+                ApiHelperForR.getDisplay(mWindowContext));
     }
 
     /* package */ void onDisplayRemoved() {
@@ -193,10 +194,11 @@
             display.getSize(size);
             display.getMetrics(displayMetrics);
         }
-        updateCommon(size, displayMetrics.density, display);
+        updateCommon(
+                size, displayMetrics.density, displayMetrics.xdpi, displayMetrics.ydpi, display);
     }
 
-    private void updateCommon(Point size, float density, Display display) {
+    private void updateCommon(Point size, float density, float xdpi, float ydpi, Display display) {
         if (hasForcedDIPScale()) density = sForcedDIPScale.floatValue();
         boolean isWideColorGamut = false;
         // Although this API was added in Android O, it was buggy.
@@ -220,8 +222,8 @@
             assert supportedModes.size() > 0;
         }
 
-        super.update(size, density, bitsPerPixel(pixelFormatId), bitsPerComponent(pixelFormatId),
-                display.getRotation(), isWideColorGamut, null, display.getRefreshRate(),
-                currentMode, supportedModes);
+        super.update(size, density, xdpi, ydpi, bitsPerPixel(pixelFormatId),
+                bitsPerComponent(pixelFormatId), display.getRotation(), isWideColorGamut, null,
+                display.getRefreshRate(), currentMode, supportedModes);
     }
 }
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 989f4dc6..351ec3a 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -136,6 +136,7 @@
   E_CPONLY(kColorShadowValueKeyShadowElevationSixteen) \
   E_CPONLY(kColorShadowValueKeyShadowElevationThree) \
   E_CPONLY(kColorSidePanelComboboxBorder) \
+  E_CPONLY(kColorSidePanelComboboxBackground) \
   E_CPONLY(kColorSliderThumb) \
   E_CPONLY(kColorSliderThumbMinimal) \
   E_CPONLY(kColorSliderTrack) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index 6e97a16..23a1891 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -5,6 +5,7 @@
 #include "ui/color/ui_color_mixer.h"
 
 #include "build/build_config.h"
+#include "ui/color/color_id.h"
 #include "ui/color/color_mixer.h"
 #include "ui/color/color_provider.h"
 #include "ui/color/color_provider_manager.h"
@@ -161,6 +162,7 @@
   mixer[kColorShadowValueKeyShadowElevationSixteen] =
       SetAlpha(kColorShadowBase, 0x1a);
   mixer[kColorSidePanelComboboxBorder] = {SK_ColorTRANSPARENT};
+  mixer[kColorSidePanelComboboxBackground] = {SK_ColorTRANSPARENT};
   mixer[kColorSliderThumb] = {kColorAccent};
   mixer[kColorSliderThumbMinimal] = {kColorSecondaryForeground};
   mixer[kColorSliderTrack] = {kColorSubtleAccent};
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index d728dba..507b5aa 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -274,10 +274,9 @@
   return false;
 }
 
-// Note: this is not SteelSeries's actual VID; the Stratus Duo just reports it
-// incorrectly over Bluetooth.
-const uint16_t kSteelSeriesStratusDuoBluetoothVendorId = 0x0111;
+const uint16_t kSteelSeriesBluetoothVendorId = 0x0111;
 const uint16_t kSteelSeriesStratusDuoBluetoothProductId = 0x1431;
+const uint16_t kSteelSeriesStratusPlusBluetoothProductId = 0x1434;
 
 bool GetEventBits(int fd,
                   const base::FilePath& path,
@@ -766,8 +765,9 @@
   // The SteelSeries Stratus Duo claims to be a mouse over Bluetooth, preventing
   // it from being set up as a gamepad correctly, so check for its vendor and
   // product ID. (b/189491809)
-  if (input_id_.vendor == kSteelSeriesStratusDuoBluetoothVendorId &&
-      input_id_.product == kSteelSeriesStratusDuoBluetoothProductId) {
+  if (input_id_.vendor == kSteelSeriesBluetoothVendorId &&
+      (input_id_.product == kSteelSeriesStratusDuoBluetoothProductId ||
+      input_id_.product == kSteelSeriesStratusPlusBluetoothProductId)) {
     return false;
   }
 
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index d913ccb4..72eb8fd 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -147,6 +147,7 @@
   SetFocusBehavior(FocusBehavior::ALWAYS);
 #endif
 
+  SetBackgroundColorId(ui::kColorTextfieldBackground);
   UpdateBorder();
 
   arrow_button_->SetVisible(true);
@@ -264,6 +265,11 @@
   UpdateBorder();
 }
 
+void Combobox::SetBackgroundColorId(ui::ColorId color_id) {
+  SetBackground(CreateThemedRoundedRectBackground(
+      color_id, FocusableBorder::kCornerRadiusDp));
+}
+
 void Combobox::SetEventHighlighting(bool should_highlight) {
   should_highlight_ = should_highlight;
   AsViewClass<TransparentButton>(arrow_button_)
@@ -285,10 +291,6 @@
 
 void Combobox::OnThemeChanged() {
   View::OnThemeChanged();
-  SetBackground(
-      CreateBackgroundFromPainter(Painter::CreateSolidRoundRectPainter(
-          GetColorProvider()->GetColor(ui::kColorTextfieldBackground),
-          FocusableBorder::kCornerRadiusDp)));
   OnContentSizeMaybeChanged();
 }
 
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
index 0515b2aa..828cb65 100644
--- a/ui/views/controls/combobox/combobox.h
+++ b/ui/views/controls/combobox/combobox.h
@@ -15,6 +15,7 @@
 #include "ui/base/models/combobox_model.h"
 #include "ui/base/models/combobox_model_observer.h"
 #include "ui/base/models/menu_model.h"
+#include "ui/color/color_id.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/prefix_delegate.h"
 #include "ui/views/metadata/view_factory.h"
@@ -111,6 +112,7 @@
   bool GetInvalid() const { return invalid_; }
 
   void SetBorderColorId(ui::ColorId color_id);
+  void SetBackgroundColorId(ui::ColorId color_id);
 
   // Sets whether there should be ink drop highlighting on hover/press.
   void SetEventHighlighting(bool should_highlight);
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index 7c9330c..53937a4e 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -11,6 +11,7 @@
 import("//ui/webui/resources/include_polymer.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("//ui/webui/resources/tools/js_modulizer.gni")
+import("//ui/webui/webui_features.gni")
 import("./cr_elements.gni")
 
 preprocess_folder = "$root_gen_dir/ui/webui/resources/preprocessed/cr_elements"
@@ -43,12 +44,16 @@
 
 group("preprocess") {
   public_deps = [
-    ":preprocess_generated_ts",
-    ":preprocess_src_ts",
+    ":html_wrapper_files_native",
+    ":preprocess_ts",
   ]
 
   if (include_polymer) {
-    public_deps += [ ":preprocess_generated" ]
+    public_deps += [
+      ":css_wrapper_files",
+      ":html_wrapper_files",
+      ":preprocess_generated",
+    ]
   }
 
   if (is_chromeos_ash) {
@@ -79,12 +84,14 @@
 
 # TS files are passed to a separate target so that they are not listed in the
 # |out_manifest|.
-preprocess_if_expr("preprocess_src_ts") {
+preprocess_if_expr("preprocess_ts") {
   in_folder = "."
   out_folder = preprocess_folder
-  in_files = web_component_files_native_ts + non_web_component_files_ts
+  in_files = web_component_files_native_ts + native_html_files +
+             non_web_component_files_ts
   if (include_polymer) {
-    in_files += web_component_files_polymer_ts
+    in_files += web_component_files_polymer_ts + polymer_html_files +
+                icons_html_files + css_files
   }
 }
 
@@ -203,22 +210,6 @@
   }
 }
 
-# TS files are passed to a separate target so that they are not listed in the
-# |out_manifest|.
-preprocess_if_expr("preprocess_generated_ts") {
-  deps = [ ":html_wrapper_files_native" ]
-  in_folder = target_gen_dir
-  out_folder = preprocess_folder
-  in_files = native_html_wrapper_files
-  if (include_polymer) {
-    deps += [
-      ":css_wrapper_files",
-      ":html_wrapper_files",
-    ]
-    in_files += polymer_html_wrapper_files + css_wrapper_files
-  }
-}
-
 if (include_polymer) {
   group("closure_compile") {
     deps = [
@@ -409,16 +400,28 @@
 }
 
 html_to_wrapper("html_wrapper_files_native") {
+  deps = [ ":preprocess_ts" ]
+  in_folder = preprocess_folder
+  out_folder = preprocess_folder
   in_files = native_html_files
+  minify = optimize_webui
   template = "native"
 }
 
 if (include_polymer) {
   html_to_wrapper("html_wrapper_files") {
+    deps = [ ":preprocess_ts" ]
+    in_folder = preprocess_folder
+    out_folder = preprocess_folder
     in_files = polymer_html_files + icons_html_files
+    minify = optimize_webui
   }
 
   css_to_wrapper("css_wrapper_files") {
+    deps = [ ":preprocess_ts" ]
+    in_folder = preprocess_folder
+    out_folder = preprocess_folder
     in_files = css_files
+    minify = optimize_webui
   }
 }
diff --git a/ui/webui/resources/cr_elements/cr_elements.gni b/ui/webui/resources/cr_elements/cr_elements.gni
index 87034e4..51e7fcb 100644
--- a/ui/webui/resources/cr_elements/cr_elements.gni
+++ b/ui/webui/resources/cr_elements/cr_elements.gni
@@ -47,11 +47,8 @@
   ]
 
   if (is_chromeos) {
-    web_component_files_polymer_ts += [
-      # cr-searchable-drop-down is only used in smb and cups dialogs, both of
-      # which are chromeos only.
-      "cr_searchable_drop_down/cr_searchable_drop_down.ts",
-    ]
+    web_component_files_polymer_ts +=
+        [ "cr_searchable_drop_down/cr_searchable_drop_down.ts" ]
   }
 
   icons_html_files = [ "mwb_shared_icons.html" ]
@@ -103,7 +100,8 @@
   }
 }
 
-# List of all files above with the "cr_elements/" prefix to be used in a parent BUILD.gn file.
+# List of all files above with the "cr_elements/" prefix to be used in a parent
+# BUILD.gn file.
 cr_elements_files = []
 foreach(f,
         web_component_files_native_ts + native_html_wrapper_files +
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index 5ae639a..d303483 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -274,6 +274,8 @@
             long toMillis, @NonNull IObjectWrapper completionCallback) {
         StrictModeWorkaround.apply();
         checkNotDestroyed();
+        // `toMillis` should be greater than `fromMillis`
+        assert fromMillis < toMillis;
         // Handle ContentCapture data clearing.
         PlatformContentCaptureController controller =
                 PlatformContentCaptureController.getInstance();