diff --git a/DEPS b/DEPS
index 6e0815f..37d1ce2 100644
--- a/DEPS
+++ b/DEPS
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5fb37db69f1163a45603412a2584a1d1357e09ef',
+  'skia_revision': '6ff07e0b02968f8c27492f0a741aaa0d14b484b1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -261,11 +261,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c1e328857a9f100d538ca0d77a237838fc1971b0',
+  'angle_revision': 'b72718d2372093d4a9931535d6847ae1dd631075',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '39fdd0d506bfc673068c27e9ad4ee975fbbd38ed',
+  'swiftshader_revision': '80df3d84b2e40024917c02db011756566481e1f1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -320,7 +320,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': '74f773b8a535b0a4dd401229ae4caef2c972c417',
+  'catapult_revision': '4756e385c6030e3da50cc5ea3724bdecea1fe7e6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # 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 feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'd1c8d9fdd149b61767dc68fb0751f7e0ab1821d6',
+  'dawn_revision': 'd58b7ef97bde18e1ec2e6e1396698fca13b9f54e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -704,7 +704,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'b1ba8148df8bf7cef2f31ec8882381688780f334',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'da7da8c0de746135a79eb85370a4413c4aeff0ea',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1002,7 +1002,7 @@
     Var('chromium_git') + '/angle/angle.git' + '@' +  Var('angle_revision'),
 
   'src/third_party/dav1d/libdav1d':
-    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'b010080fea75435327fc90824bb4d8035722b3cb',
+    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '3262e3d0c89c83389e5bec32d7d81564077e2687',
 
   'src/third_party/dawn':
     Var('dawn_git') + '/dawn.git' + '@' +  Var('dawn_revision'),
@@ -1131,7 +1131,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4bda2abfcf6e5debf6e0463f987a8432a56e06e1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e210b542901532a862e9c9d025f5f72121c97316',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1592,7 +1592,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'b3xywTDMiV57Y73i6_dK84wODQR2dwjLDwU021ku_MwC'
+              'version': 'duzfTEopbyRA5woJYenIk_Ix1_wGeq40HGwZWMddDS4C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1732,10 +1732,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'cf04aebdf9b53bb2853f22a81465688daf879ec6',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'afed9f68f68ec30374db2631654d48bf855e630e',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '08f9efb510710548a7bc7d6ccb6c8596a9770002',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f57d74f205e4ada2c9459353d810645ae1b8b15e',
+    Var('webrtc_git') + '/src.git' + '@' + 'b0044c1c539bfde6ee603e3a76e21121596f367b',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1783,7 +1783,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'nHUjLIViYsLxRjv-zDdmzqT8p1R3VoyHq5gdGkKeMYwC',
+          'version': 'jw5QK1qcTGSBr-yjH0d-9F_MNeq6e5_5aWLq_oGWy0QC',
         },
       ],
       'dep_type': 'cipd',
@@ -1794,7 +1794,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': '-mc865SGfJAqreLZM6fkn8tgCJ7u5QLk5zm7r-ZRJ9gC',
+          'version': 'o4BSMT1hKtY4T4VBfANeSm-NuhxoxPYUp3lF0EpoUvMC',
         },
       ],
       'dep_type': 'cipd',
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@28af56c886f33848eab3b5bf9756f9dc5e454e64',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e83bd8d98b7b6e52175b61a8f0aa5f461a298906',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index b87b2f3..41d3563 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1362,6 +1362,7 @@
       'filepath': 'ash/components/proximity_auth/'\
                   '|ash/multi_device_setup/'\
                   '|ash/resources/multidevice_resources.grdp'\
+                  '|ash/services/device_sync/'\
                   '|ash/services/multidevice_setup/'\
                   '|ash/services/secure_channel/'\
                   '|chrome/browser/ash/cryptauth/'\
diff --git a/android_webview/browser/aw_devtools_manager_delegate.cc b/android_webview/browser/aw_devtools_manager_delegate.cc
index c4aa5e0..433efaebe 100644
--- a/android_webview/browser/aw_devtools_manager_delegate.cc
+++ b/android_webview/browser/aw_devtools_manager_delegate.cc
@@ -29,16 +29,16 @@
   if (!bvr)
     return "";
   base::DictionaryValue description;
-  description.SetBoolean("attached", bvr->attached_to_window());
-  description.SetBoolean("never_attached", !bvr->was_attached());
-  description.SetBoolean("visible", bvr->IsVisible());
+  description.SetBoolKey("attached", bvr->attached_to_window());
+  description.SetBoolKey("never_attached", !bvr->was_attached());
+  description.SetBoolKey("visible", bvr->IsVisible());
   gfx::Rect screen_rect = bvr->GetScreenRect();
-  description.SetInteger("screenX", screen_rect.x());
-  description.SetInteger("screenY", screen_rect.y());
-  description.SetBoolean("empty", screen_rect.size().IsEmpty());
+  description.SetIntKey("screenX", screen_rect.x());
+  description.SetIntKey("screenY", screen_rect.y());
+  description.SetBoolKey("empty", screen_rect.size().IsEmpty());
   if (!screen_rect.size().IsEmpty()) {
-    description.SetInteger("width", screen_rect.width());
-    description.SetInteger("height", screen_rect.height());
+    description.SetIntKey("width", screen_rect.width());
+    description.SetIntKey("height", screen_rect.height());
   }
   std::string json;
   base::JSONWriter::Write(description, &json);
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 32be11e..a3fb8ed 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -268,7 +268,7 @@
                     "Whether WebView will send variations headers on URLs where applicable."),
             Flag.baseFeature(ContentFeatures.INCLUDE_IPC_OVERHEAD_IN_NAVIGATION_START,
                     "Whether navigation metrics include ipc overhead."),
-            Flag.baseFeature(ContentFeatures.AVOID_UNNECESSARY_BEFORE_UNLOAD_CHECK,
+            Flag.baseFeature(ContentFeatures.AVOID_UNNECESSARY_BEFORE_UNLOAD_CHECK_POST_TASK,
                     "Avoids an unnecessary renderer ipc during navigation for before-unload "
                             + "handlers."),
             Flag.baseFeature(AwFeatures.WEBVIEW_X_REQUESTED_WITH_HEADER,
diff --git a/android_webview/tools/generate_flag_labels.py b/android_webview/tools/generate_flag_labels.py
index f59f3b9..84072d39 100755
--- a/android_webview/tools/generate_flag_labels.py
+++ b/android_webview/tools/generate_flag_labels.py
@@ -53,6 +53,7 @@
     'CompositeBgColorAnimation': 'CompositeBGColorAnimation',
     'RtcDisallowPlanBOutsideDeprecationTrial': 'RTCDisallowPlanBOutsideDeprecationTrial',  # pylint: disable=line-too-long
     'enable-http2-grease-settings': 'http2-grease-settings',
+    'AvoidUnnecessaryBeforeUnloadCheckPostTask': 'AvoidUnnecessaryBeforeUnloadCheck',  # pylint: disable=line-too-long
 }
 
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index fdb4e7d1..464db92 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1323,8 +1323,6 @@
     "system/palette/tools/metalayer_mode.h",
     "system/pcie_peripheral/pcie_peripheral_notification_controller.cc",
     "system/pcie_peripheral/pcie_peripheral_notification_controller.h",
-    "system/phonehub/animated_loading_card.cc",
-    "system/phonehub/animated_loading_card.h",
     "system/phonehub/bluetooth_disabled_view.cc",
     "system/phonehub/bluetooth_disabled_view.h",
     "system/phonehub/camera_roll_menu_model.cc",
diff --git a/ash/app_list/OWNERS b/ash/app_list/OWNERS
index 2bd36c8..814bba4a 100644
--- a/ash/app_list/OWNERS
+++ b/ash/app_list/OWNERS
@@ -1,4 +1,3 @@
-calamity@chromium.org
 jennyz@chromium.org
 khmel@chromium.org
 newcomer@chromium.org
diff --git a/ash/app_list/test/app_list_test_helper.cc b/ash/app_list/test/app_list_test_helper.cc
index e0c042de..cff830f 100644
--- a/ash/app_list/test/app_list_test_helper.cc
+++ b/ash/app_list/test/app_list_test_helper.cc
@@ -4,7 +4,6 @@
 
 #include "ash/app_list/test/app_list_test_helper.h"
 
-#include <tuple>
 #include <utility>
 
 #include "ash/app_list/app_list_bubble_presenter.h"
@@ -31,8 +30,6 @@
 #include "base/guid.h"
 #include "base/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/test/test_utils.h"
 
 namespace ash {
 
@@ -98,18 +95,6 @@
   WaitUntilIdle();
 }
 
-void AppListTestHelper::WaitForLayerAnimation(ui::Layer* layer) {
-  auto* compositor = layer->GetCompositor();
-  while (layer->GetAnimator()->is_animating()) {
-    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
-  }
-
-  // Ensure there is one more frame presented after animation finishes
-  // to allow animation throughput data is passed from cc to ui.
-  std::ignore =
-      ui::WaitForNextFrameToBePresented(compositor, base::Milliseconds(200));
-}
-
 void AppListTestHelper::StartSlideAnimationOnBubbleAppsPage(
     views::View* view,
     int vertical_offset,
diff --git a/ash/app_list/test/app_list_test_helper.h b/ash/app_list/test/app_list_test_helper.h
index 0340c6d09..f816936d 100644
--- a/ash/app_list/test/app_list_test_helper.h
+++ b/ash/app_list/test/app_list_test_helper.h
@@ -17,10 +17,6 @@
 class TimeDelta;
 }
 
-namespace ui {
-class Layer;
-}
-
 namespace views {
 class View;
 }
@@ -82,10 +78,6 @@
   // until animation finishes.
   void ToggleAndRunLoop(uint64_t display_id, AppListShowSource show_source);
 
-  // Waits for a layer animation to complete and for animation throughput data
-  // to be passed from cc to ui.
-  void WaitForLayerAnimation(ui::Layer* layer);
-
   // Slides a bubble apps page's component using a layer animation.
   void StartSlideAnimationOnBubbleAppsPage(views::View* view,
                                            int vertical_offset,
diff --git a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
index ccc8c787..7a356be 100644
--- a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/app_list/views/scrollable_apps_grid_view.h"
 #include "ash/constants/ash_features.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/layer_animation_stopped_waiter.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -103,7 +104,7 @@
   // Before the animation completes, delete the search. This should abort
   // animations, animate back to the apps page, and leave the apps page visible.
   PressAndReleaseKey(ui::VKEY_BACK);
-  helper->WaitForLayerAnimation(apps_page->GetPageAnimationLayerForTest());
+  LayerAnimationStoppedWaiter().Wait(apps_page->GetPageAnimationLayerForTest());
   EXPECT_TRUE(apps_page->GetVisible());
   EXPECT_EQ(1.0f, apps_page->scroll_view()->contents()->layer()->opacity());
 }
@@ -126,7 +127,13 @@
 
   // Type a key to trigger the animation to transition to the search page.
   PressAndReleaseKey(ui::VKEY_A);
-  helper->WaitForLayerAnimation(apps_page->GetPageAnimationLayerForTest());
+  ui::Layer* layer = apps_page->GetPageAnimationLayerForTest();
+  LayerAnimationStoppedWaiter().Wait(layer);
+
+  // Ensure there is one more frame presented after animation finishes to allow
+  // animation throughput data to be passed from cc to ui.
+  layer->GetCompositor()->ScheduleFullRedraw();
+  EXPECT_TRUE(ui::WaitForNextFrameToBePresented(layer->GetCompositor()));
 
   // Apps page is not visible.
   EXPECT_FALSE(apps_page->GetVisible());
@@ -159,7 +166,13 @@
 
   // Press escape to trigger animation back to the apps page.
   PressAndReleaseKey(ui::VKEY_ESCAPE);
-  helper->WaitForLayerAnimation(apps_page->GetPageAnimationLayerForTest());
+  ui::Layer* layer = apps_page->GetPageAnimationLayerForTest();
+  LayerAnimationStoppedWaiter().Wait(layer);
+
+  // Ensure there is one more frame presented after animation finishes to allow
+  // animation throughput data to be passed from cc to ui.
+  layer->GetCompositor()->ScheduleFullRedraw();
+  EXPECT_TRUE(ui::WaitForNextFrameToBePresented(layer->GetCompositor()));
 
   // Apps page is visible.
   EXPECT_TRUE(apps_page->GetVisible());
diff --git a/ash/app_list/views/app_list_bubble_search_page_unittest.cc b/ash/app_list/views/app_list_bubble_search_page_unittest.cc
index 4f13ca2..9540436 100644
--- a/ash/app_list/views/app_list_bubble_search_page_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_search_page_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/app_list/views/app_list_bubble_apps_page.h"
 #include "ash/constants/ash_features.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/layer_animation_stopped_waiter.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_element.h"
@@ -83,7 +84,7 @@
       ui::LayerAnimationElement::AnimatableProperty::TRANSFORM));
 
   // Search page visibility updates at the end of the animation.
-  helper->WaitForLayerAnimation(layer);
+  LayerAnimationStoppedWaiter().Wait(layer);
   EXPECT_FALSE(search_page->GetVisible());
 }
 
diff --git a/ash/app_list/views/app_list_bubble_view_unittest.cc b/ash/app_list/views/app_list_bubble_view_unittest.cc
index 16da98a..9525202 100644
--- a/ash/app_list/views/app_list_bubble_view_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_view_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 #include <string>
-#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -38,6 +37,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/layer_animation_stopped_waiter.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -183,10 +183,6 @@
     return view ? view->GetClassName() : "none";
   }
 
-  void WaitForLayerAnimation(ui::Layer* layer) {
-    GetAppListTestHelper()->WaitForLayerAnimation(layer);
-  }
-
   base::test::ScopedFeatureList scoped_features_;
   std::unique_ptr<test::AppListTestModel> app_list_test_model_;
   std::unique_ptr<SearchModel> search_model_;
@@ -377,7 +373,7 @@
   EXPECT_TRUE(apps_grid_view->layer());
 
   // Finish the animation.
-  WaitForLayerAnimation(apps_grid_view->layer());
+  LayerAnimationStoppedWaiter().Wait(apps_grid_view->layer());
 
   // Temporary layers are cleaned up.
   EXPECT_FALSE(continue_section->layer());
@@ -405,7 +401,7 @@
 
   // Finish the animation.
   auto* apps_grid_view = GetAppsGridView();
-  WaitForLayerAnimation(apps_grid_view->layer());
+  LayerAnimationStoppedWaiter().Wait(apps_grid_view->layer());
 
   // Gradient mask layer is restored.
   EXPECT_TRUE(scroll_view->layer()->layer_mask_layer());
@@ -425,7 +421,7 @@
 
   // Finish the animation.
   auto* apps_grid_view = GetAppsGridView();
-  WaitForLayerAnimation(apps_grid_view->layer());
+  LayerAnimationStoppedWaiter().Wait(apps_grid_view->layer());
 
   // Shadow is restored.
   EXPECT_TRUE(app_list_bubble_view->view_shadow_for_test());
@@ -443,16 +439,20 @@
   ShowAppList();
 
   // Wait for the animation to finish.
-  WaitForLayerAnimation(GetAppsGridView()->layer());
+  ui::Layer* layer = GetAppsGridView()->layer();
+  LayerAnimationStoppedWaiter().Wait(layer);
+
+  // Ensure there is one more frame presented after animation finishes to allow
+  // animation throughput data to be passed from cc to ui.
+  layer->GetCompositor()->ScheduleFullRedraw();
+  EXPECT_TRUE(ui::WaitForNextFrameToBePresented(layer->GetCompositor()));
 
   // Smoothness was recorded.
   histograms.ExpectTotalCount(
       "Apps.ClamshellLauncher.AnimationSmoothness.OpenAppsPage", 1);
 }
 
-// TODO(crbug.com/1300774): Disabled due to flakiness.
-TEST_F(AppListBubbleViewTest,
-       DISABLED_HideAnimationsRecordsSmoothnessHistogram) {
+TEST_F(AppListBubbleViewTest, HideAnimationsRecordsSmoothnessHistogram) {
   base::HistogramTester histograms;
 
   // Show the app list without animation.
@@ -464,19 +464,16 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   AppListBubbleView* view = GetBubblePresenter()->bubble_view_for_test();
-  ui::Compositor* compositor = view->layer()->GetCompositor();
+  ui::Layer* layer = view->layer();
 
-  // Run the hide animation and wait for it to finish. This doesn't use
-  // WaitForLayerAnimation() because the view and its layer are deleted at the
-  // end of the animation.
-  base::RunLoop run_loop;
-  view->StartHideAnimation(/*is_side_shelf=*/false, run_loop.QuitClosure());
-  run_loop.Run();
+  // Run the hide animation and wait for it to finish.
+  view->StartHideAnimation(/*is_side_shelf=*/false, base::DoNothing());
+  LayerAnimationStoppedWaiter().Wait(layer);
 
   // Ensure there is one more frame presented after animation finishes to allow
   // animation throughput data to be passed from cc to ui.
-  std::ignore =
-      ui::WaitForNextFrameToBePresented(compositor, base::Milliseconds(200));
+  layer->GetCompositor()->ScheduleFullRedraw();
+  EXPECT_TRUE(ui::WaitForNextFrameToBePresented(layer->GetCompositor()));
 
   // Smoothness was recorded.
   histograms.ExpectTotalCount(
@@ -493,7 +490,7 @@
   // Show the app list and wait for the show animation to finish.
   AddAppItems(5);
   ShowAppList();
-  WaitForLayerAnimation(GetAppsGridView()->layer());
+  LayerAnimationStoppedWaiter().Wait(GetAppsGridView()->layer());
 
   // Dismiss the app list, but don't wait for the animation to finish.
   GetAppListTestHelper()->Dismiss();
diff --git a/ash/app_list/views/continue_task_view.cc b/ash/app_list/views/continue_task_view.cc
index 6d34b932..cb15956 100644
--- a/ash/app_list/views/continue_task_view.cc
+++ b/ash/app_list/views/continue_task_view.cc
@@ -90,6 +90,7 @@
   SetFocusPainter(nullptr);
 
   views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
+  views::InkDrop::Get(this)->GetInkDrop()->SetShowHighlightOnHover(false);
   SetHasInkDropActionOnClick(true);
   SetShowInkDropWhenHotTracked(false);
 
diff --git a/ash/app_list/views/productivity_launcher_search_view_unittest.cc b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
index 17831b8..dcf5a3d 100644
--- a/ash/app_list/views/productivity_launcher_search_view_unittest.cc
+++ b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
@@ -20,6 +20,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/layer_animation_stopped_waiter.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -37,18 +38,6 @@
 const int kResultContainersCount = static_cast<int>(
     ash::SearchResultListView::SearchResultListType::kMaxValue);
 
-// Waits for a layer animation to complete.
-void WaitForLayerAnimation(ui::Layer* layer) {
-  auto* compositor = layer->GetCompositor();
-  while (layer->GetAnimator()->is_animating()) {
-    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
-  }
-
-  // Ensure there is one more frame presented after animation finishes
-  // to allow animation throughput data is passed from cc to ui.
-  std::ignore =
-      ui::WaitForNextFrameToBePresented(compositor, base::Milliseconds(200));
-}
 }  // namespace
 
 namespace ash {
@@ -202,8 +191,10 @@
   EXPECT_LT(result_containers[2]->GetResultViewAt(0)->layer()->opacity(), 1.0f);
   EXPECT_TRUE(result_containers[3]->GetVisible());
   EXPECT_LT(result_containers[3]->GetResultViewAt(0)->layer()->opacity(), 1.0f);
-  WaitForLayerAnimation(result_containers[2]->GetResultViewAt(0)->layer());
-  WaitForLayerAnimation(result_containers[3]->GetResultViewAt(0)->layer());
+  LayerAnimationStoppedWaiter().Wait(
+      result_containers[2]->GetResultViewAt(0)->layer());
+  LayerAnimationStoppedWaiter().Wait(
+      result_containers[3]->GetResultViewAt(0)->layer());
   EXPECT_EQ(result_containers[3]->GetResultViewAt(0)->layer()->opacity(), 1.0f);
   EXPECT_EQ(result_containers[3]->GetResultViewAt(0)->layer()->opacity(), 1.0f);
 }
diff --git a/ash/app_list/views/search_result_inline_icon_view.cc b/ash/app_list/views/search_result_inline_icon_view.cc
index 7f26bbc..e98135d 100644
--- a/ash/app_list/views/search_result_inline_icon_view.cc
+++ b/ash/app_list/views/search_result_inline_icon_view.cc
@@ -28,23 +28,11 @@
 constexpr int kBorderThickness = 1;
 constexpr float kButtonCornerRadius = 6.0f;
 constexpr int kLeftRightMargin = 6;
-constexpr gfx::Insets kBubbleBorder(0, kLeftRightMargin, 0, kLeftRightMargin);
 constexpr int kIconSize = 14;
 constexpr int kLabelMinEdgeLength = 20;
 
 }  // namespace
 
-class SearchResultInlineIconView::SizedLabel : public views::Label {
- public:
-  SizedLabel() = default;
-  ~SizedLabel() override = default;
-
-  // views::Label:
-  gfx::Size GetMinimumSize() const override {
-    return gfx::Size(kLabelMinEdgeLength, kLabelMinEdgeLength);
-  }
-};
-
 SearchResultInlineIconView::SearchResultInlineIconView() {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 }
@@ -66,24 +54,29 @@
   icon_image_->SetImageSize(gfx::Size(kIconSize, kIconSize));
   icon_image_->SetVisible(true);
 
-  icon_image_->SetBorder(views::CreateEmptyBorder(kBubbleBorder));
+  icon_image_->SetBorder(
+      views::CreateEmptyBorder(0, kLeftRightMargin, 0, kLeftRightMargin));
   SetVisible(true);
 }
 
 void SearchResultInlineIconView::SetText(const std::u16string& text) {
   DCHECK(!icon_image_);
   if (!label_) {
-    label_ = AddChildView(std::make_unique<SizedLabel>());
+    label_ = AddChildView(std::make_unique<views::Label>());
     label_->SetBackgroundColor(SK_ColorTRANSPARENT);
     label_->SetVisible(true);
     label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
-    label_->SetTextContext(CONTEXT_SEARCH_RESULT_VIEW);
-    label_->SetTextStyle(STYLE_PRODUCTIVITY_LAUNCHER);
+    label_->SetTextContext(CONTEXT_SEARCH_RESULT_VIEW_INLINE_ANSWER_DETAILS);
+    label_->SetTextStyle(STYLE_EMPHASIZED);
   }
 
   label_->SetText(text);
   label_->SetVisible(true);
-  label_->SetBorder(views::CreateEmptyBorder(kBubbleBorder));
+
+  int label_left_right_margin =
+      std::max(kLeftRightMargin, (kLabelMinEdgeLength - label_->width()) / 2);
+  label_->SetBorder(views::CreateEmptyBorder(0, label_left_right_margin, 0,
+                                             label_left_right_margin));
 
   label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kTextColorURL));
diff --git a/ash/app_list/views/search_result_inline_icon_view.h b/ash/app_list/views/search_result_inline_icon_view.h
index 1f929ab6..fb3c832 100644
--- a/ash/app_list/views/search_result_inline_icon_view.h
+++ b/ash/app_list/views/search_result_inline_icon_view.h
@@ -13,6 +13,7 @@
 
 namespace views {
 class ImageView;
+class Label;
 }  // namespace views
 
 namespace ash {
@@ -43,7 +44,7 @@
 
   views::ImageView* icon_image_ = nullptr;  // Owned by views hierarchy.
 
-  SizedLabel* label_ = nullptr;  // Owned by views hierarchy.
+  views::Label* label_ = nullptr;  // Owned by views hierarchy.
 };
 
 }  // namespace ash
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index 5acd751..7bce22cc 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -27,6 +27,7 @@
 #include "ash/public/cpp/view_shadow.h"
 #include "ash/search_box/search_box_constants.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/system_shadow.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -37,7 +38,6 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/compositor_extra/shadow.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
@@ -75,8 +75,9 @@
 // Minimum spacing between shelf and bottom of search box.
 constexpr int kSearchResultPageMinimumBottomMargin = 24;
 
-// The shadow elevation value for the shadow of the expanded search box.
-constexpr int kSearchBoxSearchResultShadowElevation = 12;
+// The shadow type for the shadow of the expanded search box.
+constexpr SystemShadow::Type kSearchBoxSearchResultShadowType =
+    SystemShadow::Type::kElevation12;
 
 // The amount of time by which notifications to accessibility framework about
 // result page changes are delayed.
@@ -192,8 +193,10 @@
   contents_view_->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
 
-  view_shadow_ =
-      std::make_unique<ViewShadow>(this, kSearchBoxSearchResultShadowElevation);
+  view_shadow_ = std::make_unique<ViewShadow>(
+      this,
+      SystemShadow::GetElevationFromType(kSearchBoxSearchResultShadowType));
+  view_shadow_->shadow()->SetShadowStyle(gfx::ShadowStyle::kChromeOSSystemUI);
   view_shadow_->SetRoundedCornerRadius(
       kSearchBoxBorderCornerRadiusSearchResult);
 
@@ -611,8 +614,10 @@
 }
 
 void SearchResultPageView::OnAnimationBetweenBoundsEnded() {
-  view_shadow_ =
-      std::make_unique<ViewShadow>(this, kSearchBoxSearchResultShadowElevation);
+  view_shadow_ = std::make_unique<ViewShadow>(
+      this,
+      SystemShadow::GetElevationFromType(kSearchBoxSearchResultShadowType));
+  view_shadow_->shadow()->SetShadowStyle(gfx::ShadowStyle::kChromeOSSystemUI);
   view_shadow_->SetRoundedCornerRadius(
       GetCornerRadiusForSearchResultsState(current_search_results_state_));
 
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index b46a3de..74f2c0329e 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -609,13 +609,24 @@
 
     bool has_match_tag = (tag.styles & SearchResult::Tag::MATCH);
     if (has_match_tag) {
-      label->SetTextStyleRange(AshTextStyle::STYLE_EMPHASIZED, tag.range);
+      switch (view_type_) {
+        case SearchResultViewType::kClassic:
+          label->SetTextStyleRange(AshTextStyle::STYLE_EMPHASIZED, tag.range);
+          break;
+        case SearchResultViewType::kDefault:
+          ABSL_FALLTHROUGH_INTENDED;
+        case SearchResultViewType::kAnswerCard:
+          label->SetTextStyleRange(AshTextStyle::STYLE_HIGHLIGHT, tag.range);
+          break;
+      }
     }
   }
 
   switch (color_tag) {
     case SearchResult::Tag::NONE:
+      ABSL_FALLTHROUGH_INTENDED;
     case SearchResult::Tag::DIM:
+      ABSL_FALLTHROUGH_INTENDED;
     case SearchResult::Tag::MATCH:
       label->SetEnabledColor(
           is_title_label
@@ -646,7 +657,7 @@
 
 void SearchResultView::StyleTitleContainer() {
   for (auto& span : title_label_tags_) {
-    StyleLabel(span.GetLabel(), false /*is_title_label*/, span.GetTags());
+    StyleLabel(span.GetLabel(), true /*is_title_label*/, span.GetTags());
   }
 }
 
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index 14adae93..add6a9e 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -53,6 +53,7 @@
     "//ash/public/cpp/external_arc:external_arc",
     "//chromeos/components/sensors:buildflags",
     "//chromeos/dbus/dlcservice:dlcservice",
+    "//chromeos/dbus/dlcservice:dlcservice_proto",
     "//chromeos/dbus/session_manager",
     "//chromeos/dbus/upstart",
     "//chromeos/memory:memory",
diff --git a/ash/components/arc/session/arc_dlc_installer.cc b/ash/components/arc/session/arc_dlc_installer.cc
index be81a20..d92564b 100644
--- a/ash/components/arc/session/arc_dlc_installer.cc
+++ b/ash/components/arc/session/arc_dlc_installer.cc
@@ -6,6 +6,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 
 namespace arc {
 
@@ -61,8 +62,10 @@
 
   state_ = InstallerState::kInstalling;
   VLOG(2) << "Installing ARC DLC: " << kHoudiniRvcDlc;
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kHoudiniRvcDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      kHoudiniRvcDlc,
+      install_request,
       base::BindOnce(&ArcDlcInstaller::OnDlcInstalled,
                      weak_ptr_factory_.GetWeakPtr(), kHoudiniRvcDlc),
       base::DoNothing());
diff --git a/ash/components/phonehub/camera_roll_manager.h b/ash/components/phonehub/camera_roll_manager.h
index 1dc4839..5c6c8087 100644
--- a/ash/components/phonehub/camera_roll_manager.h
+++ b/ash/components/phonehub/camera_roll_manager.h
@@ -45,11 +45,6 @@
     // permissions have been rejected on the Android device. In this state the
     // UI is hidden but the settings toggle is shown in a grayed out state.
     NO_STORAGE_PERMISSION,
-    // Feature is supported by the phone but the settings hasn't been enabled on
-    // system settings and not have been dismissed by user
-    CAN_OPT_IN,
-    // Feature is supported and enabled but no item has been loaded yet
-    LOADING_VIEW,
     // We have items that can be displayed
     ITEMS_VISIBLE,
   };
@@ -72,14 +67,6 @@
   // device specified by the |item_metadata| to the Downloads folder.
   virtual void DownloadItem(
       const proto::CameraRollItemMetadata& item_metadata) = 0;
-  // Attempt to enable camera roll feature; return whether the operation is
-  // succeeded. It can only be changed via this function if the current
-  // state is mojom::FeatureState::kFurtherSetupRequired or
-  // mojom::FeatureState::kDisabledByUser.
-  virtual void EnableCameraRollFeatureInSystemSetting() = 0;
-
-  // Record user have dismissed the onboarding dialog.
-  virtual void OnCameraRollOnboardingUiDismissed() = 0;
 
  protected:
   CameraRollManager();
diff --git a/ash/components/phonehub/camera_roll_manager_impl.cc b/ash/components/phonehub/camera_roll_manager_impl.cc
index d8c5200..f94a43d 100644
--- a/ash/components/phonehub/camera_roll_manager_impl.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl.cc
@@ -12,7 +12,6 @@
 #include "ash/components/phonehub/camera_roll_thumbnail_decoder_impl.h"
 #include "ash/components/phonehub/message_receiver.h"
 #include "ash/components/phonehub/message_sender.h"
-#include "ash/components/phonehub/pref_names.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "ash/components/phonehub/util/histogram_util.h"
 #include "ash/services/secure_channel/public/cpp/client/connection_manager.h"
@@ -22,8 +21,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
@@ -39,21 +36,13 @@
 
 }  // namespace
 
-// static
-void CameraRollManagerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kHasDismissedCameraRollOnboardingUi,
-                                false);
-}
-
 CameraRollManagerImpl::CameraRollManagerImpl(
-    PrefService* pref_service,
     MessageReceiver* message_receiver,
     MessageSender* message_sender,
     multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
     secure_channel::ConnectionManager* connection_manager,
     std::unique_ptr<CameraRollDownloadManager> camera_roll_download_manager)
-    : pref_service_(pref_service),
-      message_receiver_(message_receiver),
+    : message_receiver_(message_receiver),
       message_sender_(message_sender),
       multidevice_setup_client_(multidevice_setup_client),
       connection_manager_(connection_manager),
@@ -166,7 +155,6 @@
   if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
     ClearCurrentItems();
     CancelPendingThumbnailRequests();
-    resetViewRefreshingFlagIfNeeded();
     return;
   }
 
@@ -180,7 +168,6 @@
   if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
     ClearCurrentItems();
     CancelPendingThumbnailRequests();
-    resetViewRefreshingFlagIfNeeded();
     return;
   }
 
@@ -222,7 +209,6 @@
 void CameraRollManagerImpl::OnItemThumbnailsDecoded(
     CameraRollThumbnailDecoder::BatchDecodeResult result,
     const std::vector<CameraRollItem>& items) {
-  resetViewRefreshingFlagIfNeeded();
   if (result == CameraRollThumbnailDecoder::BatchDecodeResult::kCompleted) {
     if (fetch_items_request_start_timestamp_) {
       base::UmaHistogramMediumTimes(
@@ -238,18 +224,6 @@
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
-void CameraRollManagerImpl::EnableCameraRollFeatureInSystemSetting() {
-  multidevice_setup_client_->SetFeatureEnabledState(
-      chromeos::multidevice_setup::mojom::Feature::kPhoneHubCameraRoll,
-      /*enabled=*/true, /*auth_token=*/absl::nullopt, base::DoNothing());
-  is_refreshing_after_user_opt_in_ = true;
-  // Re-compute and update ui immediately instead of waiting for the callback to
-  // finish would hide the view on user's tap action, giving a indicator for the
-  // user that the action is performed. When camera items are received, camera
-  // roll view would be visible again.
-  ComputeAndUpdateUiState();
-}
-
 bool CameraRollManagerImpl::IsCameraRollSettingEnabled() {
   chromeos::multidevice_setup::mojom::FeatureState camera_roll_feature_state =
       multidevice_setup_client_->GetFeatureState(
@@ -258,10 +232,6 @@
          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;
 }
 
-void CameraRollManagerImpl::resetViewRefreshingFlagIfNeeded() {
-  is_refreshing_after_user_opt_in_ = false;
-}
-
 void CameraRollManagerImpl::OnFeatureStatesChanged(
     const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
         feature_states_map) {
@@ -294,12 +264,6 @@
   }
 }
 
-void CameraRollManagerImpl::OnCameraRollOnboardingUiDismissed() {
-  pref_service_->SetBoolean(prefs::kHasDismissedCameraRollOnboardingUi, true);
-  // Force the observing views to refresh
-  ComputeAndUpdateUiState();
-}
-
 void CameraRollManagerImpl::ComputeAndUpdateUiState() {
   if (!is_android_storage_granted_) {
     ui_state_ = CameraRollUiState::NO_STORAGE_PERMISSION;
@@ -312,15 +276,11 @@
           chromeos::multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
   switch (feature_state) {
     case chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser:
-      ui_state_ =
-          pref_service_->GetBoolean(prefs::kHasDismissedCameraRollOnboardingUi)
-              ? CameraRollUiState::SHOULD_HIDE
-              : CameraRollUiState::CAN_OPT_IN;
+      ui_state_ = CameraRollUiState::SHOULD_HIDE;
+      ;
       break;
     case chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser:
-      if (is_refreshing_after_user_opt_in_) {
-        ui_state_ = CameraRollUiState::LOADING_VIEW;
-      } else if (current_items().empty()) {
+      if (current_items().empty()) {
         ui_state_ = CameraRollUiState::SHOULD_HIDE;
       } else {
         ui_state_ = CameraRollUiState::ITEMS_VISIBLE;
diff --git a/ash/components/phonehub/camera_roll_manager_impl.h b/ash/components/phonehub/camera_roll_manager_impl.h
index ab9cccc..266968c 100644
--- a/ash/components/phonehub/camera_roll_manager_impl.h
+++ b/ash/components/phonehub/camera_roll_manager_impl.h
@@ -22,9 +22,6 @@
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-class PrefRegistrySimple;
-class PrefService;
-
 namespace ash {
 namespace phonehub {
 
@@ -39,17 +36,13 @@
       public multidevice_setup::MultiDeviceSetupClient::Observer,
       public secure_channel::ConnectionManager::Observer {
  public:
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
   CameraRollManagerImpl(
-      PrefService* pref_service,
       MessageReceiver* message_receiver,
       MessageSender* message_sender,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
       secure_channel::ConnectionManager* connection_manager,
       std::unique_ptr<CameraRollDownloadManager> camera_roll_download_manager);
   ~CameraRollManagerImpl() override;
-  void EnableCameraRollFeatureInSystemSetting() override;
 
  private:
   friend class CameraRollManagerImplTest;
@@ -95,18 +88,14 @@
       chromeos::secure_channel::mojom::FileTransferUpdatePtr update);
 
   bool IsCameraRollSettingEnabled();
-  void resetViewRefreshingFlagIfNeeded();
   void UpdateCameraRollAccessStateAndNotifyIfNeeded(
       const proto::CameraRollAccessState& access_state);
-  void OnCameraRollOnboardingUiDismissed() override;
   void ComputeAndUpdateUiState() override;
 
   bool is_android_feature_enabled_ = false;
   bool is_android_storage_granted_ = false;
-  bool is_refreshing_after_user_opt_in_ = false;
   absl::optional<base::TimeTicks> fetch_items_request_start_timestamp_;
 
-  PrefService* pref_service_;
   MessageReceiver* message_receiver_;
   MessageSender* message_sender_;
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
diff --git a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
index 2db9456..8cbdb17 100644
--- a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
@@ -10,7 +10,6 @@
 #include "ash/components/phonehub/fake_camera_roll_download_manager.h"
 #include "ash/components/phonehub/fake_message_receiver.h"
 #include "ash/components/phonehub/fake_message_sender.h"
-#include "ash/components/phonehub/pref_names.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -21,9 +20,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/image/image.h"
@@ -71,14 +67,6 @@
       last_download_error_ = absl::nullopt;
 };
 
-// Registers preferences for
-void RegisterHasDismissedOnBoardingUiPreferences(
-    TestingPrefServiceSimple* pref_service) {
-  DCHECK(pref_service);
-  pref_service->registry()->RegisterBooleanPref(
-      prefs::kHasDismissedCameraRollOnboardingUi, false);
-}
-
 void PopulateItemProto(proto::CameraRollItem* item_proto, std::string key) {
   proto::CameraRollItemMetadata* metadata = item_proto->mutable_metadata();
   metadata->set_key(key);
@@ -148,7 +136,6 @@
   ~CameraRollManagerImplTest() override = default;
 
   void SetUp() override {
-    RegisterHasDismissedOnBoardingUiPreferences(&pref_service_);
     fake_multidevice_setup_client_ =
         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
     fake_connection_manager_ =
@@ -161,7 +148,7 @@
 
     SetCameraRollFeatureState(FeatureState::kEnabledByUser);
     camera_roll_manager_ = std::make_unique<CameraRollManagerImpl>(
-        &pref_service_, &fake_message_receiver_, &fake_message_sender_,
+        &fake_message_receiver_, &fake_message_sender_,
         fake_multidevice_setup_client_.get(), fake_connection_manager_.get(),
         std::move(fake_camera_roll_download_manager));
     camera_roll_manager_->thumbnail_decoder_ =
@@ -320,7 +307,6 @@
   base::HistogramTester histogram_tester_;
 
  private:
-  TestingPrefServiceSimple pref_service_;
   FakeMessageSender fake_message_sender_;
   std::unique_ptr<secure_channel::FakeConnectionManager>
       fake_connection_manager_;
@@ -497,7 +483,7 @@
   SendPhoneStatusUpdate(/*has_camera_roll_updates=*/true);
 
   EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::CAN_OPT_IN,
+  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
             camera_roll_manager()->ui_state());
   EXPECT_EQ(4, GetOnCameraRollViewUiStateUpdatedCallCount());
   EXPECT_EQ(0, GetCurrentItemsCount());
@@ -542,7 +528,7 @@
   SendPhoneStatusSnapshot();
 
   EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::CAN_OPT_IN,
+  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
             camera_roll_manager()->ui_state());
   EXPECT_EQ(4, GetOnCameraRollViewUiStateUpdatedCallCount());
   EXPECT_EQ(0, GetCurrentItemsCount());
@@ -576,7 +562,7 @@
 
   SetCameraRollFeatureState(FeatureState::kDisabledByUser);
 
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::CAN_OPT_IN,
+  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
             camera_roll_manager()->ui_state());
   EXPECT_EQ(3, GetOnCameraRollViewUiStateUpdatedCallCount());
   EXPECT_EQ(0, GetCurrentItemsCount());
@@ -595,57 +581,6 @@
             camera_roll_manager()->ui_state());
 }
 
-TEST_F(CameraRollManagerImplTest, EnableFromOptInDialog) {
-  SetCameraRollFeatureState(FeatureState::kDisabledByUser);
-  proto::PhoneStatusSnapshot snapshot;
-  proto::CameraRollAccessState* access_state =
-      snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_storage_permission_granted(true);
-  fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::CAN_OPT_IN,
-            camera_roll_manager()->ui_state());
-
-  camera_roll_manager()->EnableCameraRollFeatureInSystemSetting();
-  // Verify that the CameraRollManager attempted to enable the feature via the
-  // MultideviceSetupClient. Then actually set the feature state to
-  // kEnabledByUser since the FakeMultideviceSetupClient doesn't do that.
-  fake_multidevice_setup_client_->InvokePendingSetFeatureEnabledStateCallback(
-      /*expected_feature=*/chromeos::multidevice_setup::mojom::Feature::
-          kPhoneHubCameraRoll,
-      /*expected_enabled=*/true,
-      /*expected_auth_token=*/absl::nullopt,
-      /*success=*/true);
-  SetCameraRollFeatureState(FeatureState::kEnabledByUser);
-  // The UI should change into the loading view after the setting is enabled and
-  // before the items are received.
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::LOADING_VIEW,
-            camera_roll_manager()->ui_state());
-
-  proto::FetchCameraRollItemsResponse response;
-  PopulateItemProto(response.add_items(), "key2");
-  PopulateItemProto(response.add_items(), "key1");
-  fake_message_receiver_.NotifyFetchCameraRollItemsResponseReceived(response);
-  CompleteThumbnailDecoding(BatchDecodeResult::kCompleted);
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::ITEMS_VISIBLE,
-            camera_roll_manager()->ui_state());
-}
-
-TEST_F(CameraRollManagerImplTest, DismissOptInDialog) {
-  SetCameraRollFeatureState(FeatureState::kDisabledByUser);
-  proto::PhoneStatusSnapshot snapshot;
-  proto::CameraRollAccessState* access_state =
-      snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_storage_permission_granted(true);
-  fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::CAN_OPT_IN,
-            camera_roll_manager()->ui_state());
-
-  camera_roll_manager()->OnCameraRollOnboardingUiDismissed();
-
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
-            camera_roll_manager()->ui_state());
-}
-
 TEST_F(CameraRollManagerImplTest, DownloadItem) {
   // Make an item available to CameraRollManager.
   proto::FetchCameraRollItemsResponse response;
diff --git a/ash/components/phonehub/fake_camera_roll_manager.cc b/ash/components/phonehub/fake_camera_roll_manager.cc
index dc1354d..e10972f 100644
--- a/ash/components/phonehub/fake_camera_roll_manager.cc
+++ b/ash/components/phonehub/fake_camera_roll_manager.cc
@@ -24,11 +24,6 @@
   }
 }
 
-void FakeCameraRollManager::OnCameraRollOnboardingUiDismissed() {
-  has_dismissed_onboarding_dialog_ = true;
-  ComputeAndUpdateUiState();
-}
-
 void FakeCameraRollManager::SetIsCameraRollAvailableToBeEnabled(
     bool can_enable) {
   is_avaiable_to_be_enabled_ = can_enable;
@@ -40,27 +35,11 @@
   ComputeAndUpdateUiState();
 }
 
-void FakeCameraRollManager::EnableCameraRollFeatureInSystemSetting() {
-  is_avaiable_to_be_enabled_ = false;
-  is_refreshing_after_user_opt_in_ = true;
-  ComputeAndUpdateUiState();
-}
-
-void FakeCameraRollManager::SetIsCameraRollOnboardingDismissed(bool dismissed) {
-  has_dismissed_onboarding_dialog_ = dismissed;
-  ComputeAndUpdateUiState();
-}
-
 void FakeCameraRollManager::SetIsAndroidStorageGranted(bool granted) {
   is_android_storage_granted_ = granted;
   ComputeAndUpdateUiState();
 }
 
-void FakeCameraRollManager::SetIsCameraRollLoadingViewShown(bool loading) {
-  is_refreshing_after_user_opt_in_ = loading;
-  ComputeAndUpdateUiState();
-}
-
 void FakeCameraRollManager::SetSimulatedDownloadError(bool has_error) {
   is_simulating_error_ = has_error;
 }
@@ -76,11 +55,7 @@
   } else if (!is_android_storage_granted_) {
     ui_state_ = CameraRollUiState::NO_STORAGE_PERMISSION;
   } else if (is_avaiable_to_be_enabled_) {
-    ui_state_ = (has_dismissed_onboarding_dialog_)
-                    ? CameraRollUiState::SHOULD_HIDE
-                    : CameraRollUiState::CAN_OPT_IN;
-  } else if (is_refreshing_after_user_opt_in_) {
-    ui_state_ = CameraRollUiState::LOADING_VIEW;
+    ui_state_ = CameraRollUiState::SHOULD_HIDE;
   } else if (current_items().empty()) {
     ui_state_ = CameraRollUiState::SHOULD_HIDE;
   } else {
diff --git a/ash/components/phonehub/fake_camera_roll_manager.h b/ash/components/phonehub/fake_camera_roll_manager.h
index ee1b1c5..5d579198 100644
--- a/ash/components/phonehub/fake_camera_roll_manager.h
+++ b/ash/components/phonehub/fake_camera_roll_manager.h
@@ -16,23 +16,13 @@
   FakeCameraRollManager();
   ~FakeCameraRollManager() override;
 
-  void OnCameraRollOnboardingUiDismissed() override;
   void SetIsCameraRollAvailableToBeEnabled(bool can_enable);
   void SetIsCameraRollAccessible(bool accessiable);
-  void EnableCameraRollFeatureInSystemSetting() override;
-  void SetIsCameraRollOnboardingDismissed(bool dismissed);
   void SetIsAndroidStorageGranted(bool granted);
-  void SetIsCameraRollLoadingViewShown(bool loading);
   void SetSimulatedDownloadError(bool has_error);
   void SetSimulatedErrorType(Observer::DownloadErrorType error_type);
 
   bool is_camera_roll_enabled() const { return !is_avaiable_to_be_enabled_; }
-  bool is_onboarding_dismissed() const {
-    return has_dismissed_onboarding_dialog_;
-  }
-  bool is_loading_view_shown() const {
-    return is_refreshing_after_user_opt_in_;
-  }
 
   using CameraRollManager::SetCurrentItems;
 
@@ -43,10 +33,8 @@
   // CameraRollManager:
   void DownloadItem(
       const proto::CameraRollItemMetadata& item_metadata) override;
-  bool has_dismissed_onboarding_dialog_ = false;
   bool is_avaiable_to_be_enabled_ = true;
   bool is_camera_roll_accessible_ = true;
-  bool is_refreshing_after_user_opt_in_ = false;
   bool is_android_storage_granted_ = true;
   bool is_simulating_error_ = false;
   Observer::DownloadErrorType simulated_error_type_ =
diff --git a/ash/components/phonehub/fake_phone_hub_manager.cc b/ash/components/phonehub/fake_phone_hub_manager.cc
index e841fea..d6dd382 100644
--- a/ash/components/phonehub/fake_phone_hub_manager.cc
+++ b/ash/components/phonehub/fake_phone_hub_manager.cc
@@ -59,9 +59,8 @@
 
 RecentAppsInteractionHandler*
 FakePhoneHubManager::GetRecentAppsInteractionHandler() {
-  return features::IsPhoneHubRecentAppsEnabled()
-             ? &fake_recent_apps_interaction_handler_
-             : nullptr;
+  return features::IsEcheSWAEnabled() ? &fake_recent_apps_interaction_handler_
+                                      : nullptr;
 }
 
 ScreenLockManager* FakePhoneHubManager::GetScreenLockManager() {
diff --git a/ash/components/phonehub/phone_hub_manager_impl.cc b/ash/components/phonehub/phone_hub_manager_impl.cc
index cb71310..745c5345 100644
--- a/ash/components/phonehub/phone_hub_manager_impl.cc
+++ b/ash/components/phonehub/phone_hub_manager_impl.cc
@@ -114,7 +114,7 @@
       notification_processor_(
           std::make_unique<NotificationProcessor>(notification_manager_.get())),
       recent_apps_interaction_handler_(
-          features::IsPhoneHubRecentAppsEnabled()
+          features::IsEcheSWAEnabled()
               ? std::make_unique<RecentAppsInteractionHandlerImpl>(
                     pref_service,
                     multidevice_setup_client,
@@ -152,7 +152,6 @@
               phone_model_.get())),
       camera_roll_manager_(features::IsPhoneHubCameraRollEnabled()
                                ? std::make_unique<CameraRollManagerImpl>(
-                                     pref_service,
                                      message_receiver_.get(),
                                      message_sender_.get(),
                                      multidevice_setup_client,
diff --git a/ash/components/phonehub/phone_status_processor.cc b/ash/components/phonehub/phone_status_processor.cc
index d1d4dd8b..49b3b6b 100644
--- a/ash/components/phonehub/phone_status_processor.cc
+++ b/ash/components/phonehub/phone_status_processor.cc
@@ -281,7 +281,7 @@
   find_my_device_controller_->SetPhoneRingingStatusInternal(
       ComputeFindMyDeviceStatus(phone_properties));
 
-  if (features::IsPhoneHubRecentAppsEnabled()) {
+  if (features::IsEcheSWAEnabled()) {
     recent_apps_interaction_handler_->set_user_states(
         GetUserStates(phone_properties.user_states()));
   }
diff --git a/ash/components/phonehub/phone_status_processor_unittest.cc b/ash/components/phonehub/phone_status_processor_unittest.cc
index 7b8472b..aa56c0c 100644
--- a/ash/components/phonehub/phone_status_processor_unittest.cc
+++ b/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -95,7 +95,7 @@
     fake_recent_apps_interaction_handler_ =
         std::make_unique<FakeRecentAppsInteractionHandler>();
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kPhoneHubRecentApps,
+        /*enabled_features=*/{features::kEcheSWA,
                               features::kPhoneHubCameraRoll},
         /*disabled_features=*/{});
   }
diff --git a/ash/components/phonehub/pref_names.cc b/ash/components/phonehub/pref_names.cc
index 9bf11c1..1ff0e5a 100644
--- a/ash/components/phonehub/pref_names.cc
+++ b/ash/components/phonehub/pref_names.cc
@@ -40,11 +40,6 @@
 const char kIsAwaitingVerifiedHost[] =
     "cros.phonehub.is_awaiting_verified_host";
 
-// Whether the Camera Roll onboarding dialog in the PhoneHub UI has
-// been dismissed.
-const char kHasDismissedCameraRollOnboardingUi[] =
-    "cros.phonehub.has_dismissed_camera_roll_onboarding_ui";
-
 // Whether the Notification access setup banner in the PhoneHub UI has
 // been dismissed.
 const char kHasDismissedSetupRequiredUi[] =
diff --git a/ash/components/phonehub/pref_names.h b/ash/components/phonehub/pref_names.h
index 85e9278..1e2a49de 100644
--- a/ash/components/phonehub/pref_names.h
+++ b/ash/components/phonehub/pref_names.h
@@ -14,7 +14,6 @@
 extern const char kNotificationAccessProhibitedReason[];
 extern const char kHideOnboardingUi[];
 extern const char kIsAwaitingVerifiedHost[];
-extern const char kHasDismissedCameraRollOnboardingUi[];
 extern const char kHasDismissedSetupRequiredUi[];
 extern const char kNeedsOneTimeNotificationAccessUpdate[];
 extern const char kScreenLockStatus[];
diff --git a/ash/components/proximity_auth/BUILD.gn b/ash/components/proximity_auth/BUILD.gn
index a750fee..3e4185c 100644
--- a/ash/components/proximity_auth/BUILD.gn
+++ b/ash/components/proximity_auth/BUILD.gn
@@ -81,13 +81,13 @@
   public_deps = [ ":proximity_auth" ]
 
   deps = [
+    "//ash/services/device_sync/proto",
     "//ash/services/secure_channel:test_support",
     "//ash/services/secure_channel/public/cpp/client",
     "//base",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/multidevice/logging",
-    "//chromeos/services/device_sync/proto",
     "//testing/gmock",
   ]
 }
diff --git a/ash/components/proximity_auth/DEPS b/ash/components/proximity_auth/DEPS
index 7eca46b..1ba5194 100644
--- a/ash/components/proximity_auth/DEPS
+++ b/ash/components/proximity_auth/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
-  "+chromeos/services/device_sync/proto",
+  "+ash/services/device_sync/proto",
   "+components/sync_preferences/testing_pref_service_syncable.h",
 ]
diff --git a/ash/components/proximity_auth/proximity_auth_client.h b/ash/components/proximity_auth/proximity_auth_client.h
index 4dcdedc..d02ccdf 100644
--- a/ash/components/proximity_auth/proximity_auth_client.h
+++ b/ash/components/proximity_auth/proximity_auth_client.h
@@ -10,8 +10,8 @@
 
 #include "ash/components/proximity_auth/proximity_auth_pref_manager.h"
 #include "ash/public/cpp/smartlock_state.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/callback_forward.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace proximity_auth {
 
diff --git a/ash/components/tether/BUILD.gn b/ash/components/tether/BUILD.gn
index 75e5101..1377ffb 100644
--- a/ash/components/tether/BUILD.gn
+++ b/ash/components/tether/BUILD.gn
@@ -138,8 +138,8 @@
   ]
 
   public_deps = [
+    "//ash/services/device_sync/proto",
     "//ash/services/multidevice_setup/public/mojom",
-    "//chromeos/services/device_sync/proto",
   ]
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index c1c06e4..214fa86 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -210,6 +210,11 @@
 const base::Feature kBorealisDiskManagement{"BorealisDiskManagement",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enable borealis on this device. This won't necessarily allow it, since you
+// might fail subsequent checks.
+const base::Feature kBorealisPermitted{"BorealisPermitted",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Force the client to be on its beta version. If not set, the client will be on
 // its stable version.
 const base::Feature kBorealisForceBetaClient{"BorealisForceBetaClient",
@@ -1067,11 +1072,6 @@
 const base::Feature kPhoneHubMonochromeNotificationIcons{
     "PhoneHubMonochromeNotificationIcons", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables the Recent Apps feature in Phone Hub, which allows users to relaunch
-// the streamed app.
-const base::Feature kPhoneHubRecentApps{"PhoneHubRecentApps",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kPinSetupForManagedUsers{"PinSetupForManagedUsers",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -1110,6 +1110,10 @@
 const base::Feature kProjectorAppDebug{"ProjectorAppDebug",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether the Projector exclude transcript feature is enabled.
+const base::Feature kProjectorExcludeTranscript{
+    "ProjectorExcludeTranscript", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether the quick dim prototype is enabled.
 const base::Feature kQuickDim{"QuickDim", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -1907,10 +1911,6 @@
   return base::FeatureList::IsEnabled(kPhoneHubCallNotification);
 }
 
-bool IsPhoneHubRecentAppsEnabled() {
-  return base::FeatureList::IsEnabled(kPhoneHubRecentApps);
-}
-
 bool IsPinAutosubmitBackfillFeatureEnabled() {
   return base::FeatureList::IsEnabled(kQuickUnlockPinAutosubmitBackfill);
 }
@@ -1952,6 +1952,10 @@
   return base::FeatureList::IsEnabled(kProjectorAppDebug);
 }
 
+bool IsProjectorExcludeTranscriptEnabled() {
+  return base::FeatureList::IsEnabled(kProjectorExcludeTranscript);
+}
+
 bool IsQuickDimEnabled() {
   return base::FeatureList::IsEnabled(kQuickDim);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index d1fe4c2..ce61a70 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -99,6 +99,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kBorealisLinuxMode;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kBorealisPermitted;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCalendarView;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCameraAppDocumentManualCrop;
@@ -419,7 +421,6 @@
 extern const base::Feature kPhoneHubCallNotification;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kPhoneHubMonochromeNotificationIcons;
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kPhoneHubRecentApps;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kPinSetupForManagedUsers;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kPipRoundedCorners;
@@ -433,6 +434,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kProjectorAnnotator;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kProjectorAppDebug;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kProjectorExcludeTranscript;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kQuickDim;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kQuickSettingsNetworkRevamp;
@@ -674,7 +677,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPersonalizationHubEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPhoneHubEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPhoneHubCallNotificationEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPhoneHubRecentAppsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPinAutosubmitBackfillFeatureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPinAutosubmitFeatureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPinSetupForManagedUsersEnabled();
@@ -685,6 +687,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorManagedUserEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorAnnotatorEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorAppDebugMode();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorExcludeTranscriptEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickDimEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsQuickSettingsNetworkRevampEnabled();
diff --git a/ash/public/cpp/ash_typography.cc b/ash/public/cpp/ash_typography.cc
index 7ffdcae..9283cb3 100644
--- a/ash/public/cpp/ash_typography.cc
+++ b/ash/public/cpp/ash_typography.cc
@@ -48,6 +48,9 @@
     case STYLE_EMPHASIZED:
       details.weight = gfx::Font::Weight::MEDIUM;
       break;
+    case STYLE_HIGHLIGHT:
+      details.weight = gfx::Font::Weight::BOLD;
+      break;
     case STYLE_SHARESHEET:
       DCHECK(context == CONTEXT_SHARESHEET_BUBBLE_TITLE ||
              context == CONTEXT_SHARESHEET_BUBBLE_BODY ||
diff --git a/ash/public/cpp/ash_typography.h b/ash/public/cpp/ash_typography.h
index 398ccc3..d40062d 100644
--- a/ash/public/cpp/ash_typography.h
+++ b/ash/public/cpp/ash_typography.h
@@ -64,10 +64,13 @@
 enum AshTextStyle {
   ASH_TEXT_STYLE_START = views::style::VIEWS_TEXT_STYLE_END,
 
-  // Used to draw attention to a section of body text such as a matched search
-  // string.
+  // Used to draw attention to a section of body text such as the date.
   STYLE_EMPHASIZED = ASH_TEXT_STYLE_START,
 
+  // Used to strongly draw attention to a section or body of text such as a
+  // matched search string.
+  STYLE_HIGHLIGHT,
+
   // Text styling specifically for the Chrome OS sharesheet.
   STYLE_SHARESHEET,
 
diff --git a/ash/public/cpp/desk_template.cc b/ash/public/cpp/desk_template.cc
index b6e19088..2ae4021 100644
--- a/ash/public/cpp/desk_template.cc
+++ b/ash/public/cpp/desk_template.cc
@@ -67,7 +67,7 @@
 
 std::string DeskTemplate::ToString() const {
   std::string result =
-      "Template name: " + base::UTF16ToASCII(template_name_) + "\n";
+      "Template name: " + base::UTF16ToUTF8(template_name_) + "\n";
   result += "Source: ";
   switch (source_) {
     case DeskTemplateSource::kUnknownSource:
diff --git a/ash/public/cpp/tab_cluster/OWNERS b/ash/public/cpp/tab_cluster/OWNERS
index e34d8d64..8ba08ba3 100644
--- a/ash/public/cpp/tab_cluster/OWNERS
+++ b/ash/public/cpp/tab_cluster/OWNERS
@@ -1,3 +1,2 @@
 zxdan@chromium.org
 tby@chromium.org
-myy@chromium.org
diff --git a/ash/services/device_sync/DEPS b/ash/services/device_sync/DEPS
new file mode 100644
index 0000000..174a4407
--- /dev/null
+++ b/ash/services/device_sync/DEPS
@@ -0,0 +1,19 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //chromeos/services/device_sync should be updated as well. We need to sync
+# both files until the migration of //chromeos/services/device_sync is done.
+include_rules = [
+  "+components/gcm_driver",
+  "+components/proximity_auth/logging",
+  "+components/signin/public",
+  "+components/version_info",
+  "+device/bluetooth",
+  "+google_apis/gaia",
+  "+mojo/public/cpp",
+  "+services/network/public",
+  "+services/network/test",
+  "+components/cryptauth",
+  "+third_party/securemessage",
+
+  # TODO(https://crbug.com/1164001): Remove this deps when migration is done.
+  "+chromeos/services/device_sync/public",
+]
diff --git a/ash/services/device_sync/DIR_METADATA b/ash/services/device_sync/DIR_METADATA
new file mode 100644
index 0000000..14c08b6
--- /dev/null
+++ b/ash/services/device_sync/DIR_METADATA
@@ -0,0 +1,7 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //chromeos/services/device_sync should be updated as well. We need to sync
+# both files until the migration of //chromeos/services/device_sync is done.
+buganizer {
+  component_id: 1108889
+}
+team_email: "chromeos-cross-device-eng@google.com "
diff --git a/ash/services/device_sync/OWNERS b/ash/services/device_sync/OWNERS
new file mode 100644
index 0000000..eb441b8
--- /dev/null
+++ b/ash/services/device_sync/OWNERS
@@ -0,0 +1,7 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //chromeos/services/device_sync should be updated as well. We need to sync
+# both files until the migration of //chromeos/services/device_sync is done.
+file://chromeos/components/multidevice/OWNERS
+
+per-file *_type_converter*.*=set noparent
+per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
diff --git a/chromeos/services/device_sync/proto/BUILD.gn b/ash/services/device_sync/proto/BUILD.gn
similarity index 91%
rename from chromeos/services/device_sync/proto/BUILD.gn
rename to ash/services/device_sync/proto/BUILD.gn
index 6b87e05..e5c89f5 100644
--- a/chromeos/services/device_sync/proto/BUILD.gn
+++ b/ash/services/device_sync/proto/BUILD.gn
@@ -2,8 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//third_party/protobuf/proto_library.gni")
 
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //ash")
+
 proto_library("proto") {
   sources = [
     "cryptauth_api.proto",
diff --git a/chromeos/services/device_sync/proto/cryptauth_api.proto b/ash/services/device_sync/proto/cryptauth_api.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_api.proto
rename to ash/services/device_sync/proto/cryptauth_api.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.proto b/ash/services/device_sync/proto/cryptauth_better_together_device_metadata.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.proto
rename to ash/services/device_sync/proto/cryptauth_better_together_device_metadata.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto b/ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto
rename to ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_client_app_metadata.proto b/ash/services/device_sync/proto/cryptauth_client_app_metadata.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_client_app_metadata.proto
rename to ash/services/device_sync/proto/cryptauth_client_app_metadata.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_common.proto b/ash/services/device_sync/proto/cryptauth_common.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_common.proto
rename to ash/services/device_sync/proto/cryptauth_common.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_devicesync.proto b/ash/services/device_sync/proto/cryptauth_devicesync.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_devicesync.proto
rename to ash/services/device_sync/proto/cryptauth_devicesync.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_directive.proto b/ash/services/device_sync/proto/cryptauth_directive.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_directive.proto
rename to ash/services/device_sync/proto/cryptauth_directive.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_enrollment.proto b/ash/services/device_sync/proto/cryptauth_enrollment.proto
similarity index 100%
rename from chromeos/services/device_sync/proto/cryptauth_enrollment.proto
rename to ash/services/device_sync/proto/cryptauth_enrollment.proto
diff --git a/chromeos/services/device_sync/proto/cryptauth_logging.cc b/ash/services/device_sync/proto/cryptauth_logging.cc
similarity index 99%
rename from chromeos/services/device_sync/proto/cryptauth_logging.cc
rename to ash/services/device_sync/proto/cryptauth_logging.cc
index ce95681..3de15e7 100644
--- a/chromeos/services/device_sync/proto/cryptauth_logging.cc
+++ b/ash/services/device_sync/proto/cryptauth_logging.cc
@@ -4,7 +4,7 @@
 
 #include <utility>
 
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 
 #include "base/base64url.h"
 #include "base/i18n/time_formatting.h"
diff --git a/chromeos/services/device_sync/proto/cryptauth_logging.h b/ash/services/device_sync/proto/cryptauth_logging.h
similarity index 87%
rename from chromeos/services/device_sync/proto/cryptauth_logging.h
rename to ash/services/device_sync/proto/cryptauth_logging.h
index 5de58ba3..384350f 100644
--- a/chromeos/services/device_sync/proto/cryptauth_logging.h
+++ b/ash/services/device_sync/proto/cryptauth_logging.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
+#ifndef ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
+#define ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
 
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/values.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 
 namespace cryptauthv2 {
 
@@ -90,4 +90,4 @@
 
 }  // namespace cryptauthv2
 
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
+#endif  // ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_LOGGING_H_
diff --git a/chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc b/ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc
similarity index 94%
rename from chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc
rename to ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc
index 3b54304..1555d4b 100644
--- a/chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc
+++ b/ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
+#include "ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 
 namespace cryptauthv2 {
 
diff --git a/chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h b/ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h
similarity index 92%
rename from chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h
rename to ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h
index 544f9b9..b08f12a 100644
--- a/chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h
+++ b/ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_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 CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
+#ifndef ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
+#define ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
 
 #include <string>
 #include <utility>
@@ -93,4 +93,4 @@
 
 }  // namespace cryptauthv2
 
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
+#endif  // ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_PROTO_TO_QUERY_PARAMETERS_UTIL_H_
diff --git a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc b/ash/services/device_sync/proto/cryptauth_v2_test_util.cc
similarity index 98%
rename from chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc
rename to ash/services/device_sync/proto/cryptauth_v2_test_util.cc
index 260aff17..3895746 100644
--- a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc
+++ b/ash/services/device_sync/proto/cryptauth_v2_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 "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h b/ash/services/device_sync/proto/cryptauth_v2_test_util.h
similarity index 78%
rename from chromeos/services/device_sync/proto/cryptauth_v2_test_util.h
rename to ash/services/device_sync/proto/cryptauth_v2_test_util.h
index fc918d9..608eead 100644
--- a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h
+++ b/ash/services/device_sync/proto/cryptauth_v2_test_util.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
+#ifndef ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
+#define ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
 
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cryptauthv2 {
@@ -75,4 +75,4 @@
 
 }  // namespace cryptauthv2
 
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
+#endif  // ASH_SERVICES_DEVICE_SYNC_PROTO_CRYPTAUTH_V2_TEST_UTIL_H_
diff --git a/chromeos/services/device_sync/proto/device_classifier_util.cc b/ash/services/device_sync/proto/device_classifier_util.cc
similarity index 91%
rename from chromeos/services/device_sync/proto/device_classifier_util.cc
rename to ash/services/device_sync/proto/device_classifier_util.cc
index c85f5e6..37539b1 100644
--- a/chromeos/services/device_sync/proto/device_classifier_util.cc
+++ b/ash/services/device_sync/proto/device_classifier_util.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/device_sync/proto/device_classifier_util.h"
+#include "ash/services/device_sync/proto/device_classifier_util.h"
 
 #include <vector>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/no_destructor.h"
 #include "base/system/sys_info.h"
 #include "base/version.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "components/version_info/version_info.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/proto/device_classifier_util.h b/ash/services/device_sync/proto/device_classifier_util.h
new file mode 100644
index 0000000..edfa759
--- /dev/null
+++ b/ash/services/device_sync/proto/device_classifier_util.h
@@ -0,0 +1,24 @@
+// Copyright 2018 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_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
+#define ASH_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
+
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace device_classifier_util {
+
+const cryptauth::DeviceClassifier& GetDeviceClassifier();
+
+}  // namespace device_classifier_util
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // ASH_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
diff --git a/chromeos/services/device_sync/proto/enum_util.cc b/ash/services/device_sync/proto/enum_util.cc
similarity index 98%
rename from chromeos/services/device_sync/proto/enum_util.cc
rename to ash/services/device_sync/proto/enum_util.cc
index 098a20e..b94ed4c 100644
--- a/chromeos/services/device_sync/proto/enum_util.cc
+++ b/ash/services/device_sync/proto/enum_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 "chromeos/services/device_sync/proto/enum_util.h"
+#include "ash/services/device_sync/proto/enum_util.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/proto/enum_util.h b/ash/services/device_sync/proto/enum_util.h
similarity index 91%
rename from chromeos/services/device_sync/proto/enum_util.h
rename to ash/services/device_sync/proto/enum_util.h
index 806fc03..364d8a2c 100644
--- a/chromeos/services/device_sync/proto/enum_util.h
+++ b/ash/services/device_sync/proto/enum_util.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
+#ifndef ASH_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
+#define ASH_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
 
 #include <ostream>
 
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
@@ -60,4 +60,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
+#endif  // ASH_SERVICES_DEVICE_SYNC_PROTO_ENUM_UTIL_H_
diff --git a/ash/services/ime/decoder/decoder_engine.cc b/ash/services/ime/decoder/decoder_engine.cc
index a26ecc2..6abac1a8 100644
--- a/ash/services/ime/decoder/decoder_engine.cc
+++ b/ash/services/ime/decoder/decoder_engine.cc
@@ -9,14 +9,11 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 
-namespace chromeos {
+namespace ash {
 namespace ime {
 
 namespace {
 
-// TODO(https://crbug.com/1164001): remove after migrating to ash.
-namespace mojom = ::ash::ime::mojom;
-
 // A client delegate passed to the shared library in order for the
 // shared library to send replies back to the engine.
 class ClientDelegate : public ImeClientDelegate {
@@ -105,4 +102,4 @@
 }
 
 }  // namespace ime
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/services/ime/decoder/decoder_engine.h b/ash/services/ime/decoder/decoder_engine.h
index ed4ec6d0e..7fee356 100644
--- a/ash/services/ime/decoder/decoder_engine.h
+++ b/ash/services/ime/decoder/decoder_engine.h
@@ -17,13 +17,13 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace chromeos {
+namespace ash {
 namespace ime {
 
 // A Mojo wrapper around a "decoder" that converts key events and pointer events
 // to text. The built-in Chrome OS XKB extension communicates with this to
 // implement its IMEs.
-class DecoderEngine : public ash::ime::mojom::InputChannel {
+class DecoderEngine : public mojom::InputChannel {
  public:
   explicit DecoderEngine(ImeCrosPlatform* platform,
                          absl::optional<ImeDecoder::EntryPoints> entry_points);
@@ -35,11 +35,10 @@
 
   // Binds the mojom::InputChannel interface to this object and returns true if
   // the given ime_spec is supported by the engine.
-  bool BindRequest(
-      const std::string& ime_spec,
-      mojo::PendingReceiver<ash::ime::mojom::InputChannel> receiver,
-      mojo::PendingRemote<ash::ime::mojom::InputChannel> remote,
-      const std::vector<uint8_t>& extra);
+  bool BindRequest(const std::string& ime_spec,
+                   mojo::PendingReceiver<mojom::InputChannel> receiver,
+                   mojo::PendingRemote<mojom::InputChannel> remote,
+                   const std::vector<uint8_t>& extra);
 
   // mojom::InputChannel:
   void ProcessMessage(const std::vector<uint8_t>& message,
@@ -48,10 +47,15 @@
  private:
   ImeCrosPlatform* platform_ = nullptr;
   absl::optional<ImeDecoder::EntryPoints> decoder_entry_points_;
-  mojo::ReceiverSet<ash::ime::mojom::InputChannel> decoder_channel_receivers_;
+  mojo::ReceiverSet<mojom::InputChannel> decoder_channel_receivers_;
 };
 
 }  // namespace ime
-}  // namespace chromeos
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos::ime {
+using ::ash::ime::DecoderEngine;
+}
 
 #endif  // ASH_SERVICES_IME_DECODER_DECODER_ENGINE_H_
diff --git a/ash/services/ime/decoder/system_engine.cc b/ash/services/ime/decoder/system_engine.cc
index 14f4984..6d2eaf1 100644
--- a/ash/services/ime/decoder/system_engine.cc
+++ b/ash/services/ime/decoder/system_engine.cc
@@ -10,12 +10,9 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 
-namespace chromeos {
+namespace ash {
 namespace ime {
 
-// TODO(https://crbug.com/1164001): remove after migrating to ash.
-namespace mojom = ::ash::ime::mojom;
-
 SystemEngine::SystemEngine(ImeCrosPlatform* platform,
                            absl::optional<ImeDecoder::EntryPoints> entry_points)
     : platform_(platform) {
@@ -61,4 +58,4 @@
 }
 
 }  // namespace ime
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/services/ime/decoder/system_engine.h b/ash/services/ime/decoder/system_engine.h
index 8cb7f33..af7a543b 100644
--- a/ash/services/ime/decoder/system_engine.h
+++ b/ash/services/ime/decoder/system_engine.h
@@ -19,7 +19,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace chromeos {
+namespace ash {
 namespace ime {
 
 // An enhanced implementation of the basic InputEngine that uses a built-in
@@ -36,12 +36,12 @@
   // Binds the mojom::InputMethod interface to this object and returns true if
   // the given ime_spec is supported by the engine.
   bool BindRequest(const std::string& ime_spec,
-                   mojo::PendingReceiver<ash::ime::mojom::InputMethod> receiver,
-                   mojo::PendingRemote<ash::ime::mojom::InputMethodHost> host);
+                   mojo::PendingReceiver<mojom::InputMethod> receiver,
+                   mojo::PendingRemote<mojom::InputMethodHost> host);
 
   // Binds the mojom::ConnectionFactory interface in the shared library.
   bool BindConnectionFactory(
-      mojo::PendingReceiver<ash::ime::mojom::ConnectionFactory> receiver);
+      mojo::PendingReceiver<mojom::ConnectionFactory> receiver);
 
   // InputEngine:
   bool IsConnected() override;
@@ -52,6 +52,11 @@
 };
 
 }  // namespace ime
-}  // namespace chromeos
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove when the migration is finished.
+namespace chromeos::ime {
+using ::ash::ime::SystemEngine;
+}
 
 #endif  // ASH_SERVICES_IME_DECODER_SYSTEM_ENGINE_H_
diff --git a/ash/services/ime/decoder/system_engine_unittest.cc b/ash/services/ime/decoder/system_engine_unittest.cc
index a4050b9d..92c1c7a 100644
--- a/ash/services/ime/decoder/system_engine_unittest.cc
+++ b/ash/services/ime/decoder/system_engine_unittest.cc
@@ -11,7 +11,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 namespace ime {
 
 namespace {
@@ -20,9 +20,6 @@
 
 class TestDecoderState;
 
-// TODO(https://crbug.com/1164001): remove after migrating to ash.
-namespace mojom = ::ash::ime::mojom;
-
 // The fake decoder state has to be available globally because
 // ImeDecoder::EntryPoints is a list of stateless C functions, so the only way
 // to have a stateful fake is to have a global reference to it.
@@ -222,4 +219,4 @@
 }
 
 }  // namespace ime
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/services/ime/ime_decoder.h b/ash/services/ime/ime_decoder.h
index d4afd6e..ee0b44e 100644
--- a/ash/services/ime/ime_decoder.h
+++ b/ash/services/ime/ime_decoder.h
@@ -105,4 +105,9 @@
 }  // namespace ime
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove when it moved to ash.
+namespace ash::ime {
+using ::chromeos::ime::ImeDecoder;
+}
+
 #endif  // ASH_SERVICES_IME_IME_DECODER_H_
diff --git a/ash/services/ime/input_engine.h b/ash/services/ime/input_engine.h
index 05320ee..77accf7 100644
--- a/ash/services/ime/input_engine.h
+++ b/ash/services/ime/input_engine.h
@@ -19,4 +19,9 @@
 }  // namespace ime
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove when it moved to ash.
+namespace ash::ime {
+using ::chromeos::ime::InputEngine;
+}
+
 #endif  // ASH_SERVICES_IME_INPUT_ENGINE_H_
diff --git a/ash/services/multidevice_setup/BUILD.gn b/ash/services/multidevice_setup/BUILD.gn
index 4baa617c..a65bc5b 100644
--- a/ash/services/multidevice_setup/BUILD.gn
+++ b/ash/services/multidevice_setup/BUILD.gn
@@ -61,6 +61,7 @@
 
   deps = [
     "//ash/constants",
+    "//ash/services/device_sync/proto:util",
     "//ash/services/multidevice_setup/proto",
     "//ash/services/multidevice_setup/public/cpp:android_sms_app_helper_delegate",
     "//ash/services/multidevice_setup/public/cpp:android_sms_pairing_state_tracker",
@@ -72,7 +73,6 @@
     "//base",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
-    "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
     "//components/pref_registry",
diff --git a/ash/services/multidevice_setup/DEPS b/ash/services/multidevice_setup/DEPS
index a8b144a..33e25b0 100644
--- a/ash/services/multidevice_setup/DEPS
+++ b/ash/services/multidevice_setup/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+ash/constants",
-  '+components/keyed_service/core',
+  "+ash/services/device_sync",
+  "+components/keyed_service/core",
   "+components/session_manager/core",
   "+components/sync_preferences/testing_pref_service_syncable.h",
   "+mojo/public/cpp/bindings",
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
index 80e38a52..d8ebaecb 100644
--- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
+++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
@@ -7,13 +7,13 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/containers/flat_set.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time_override.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/host_verifier_impl.cc b/ash/services/multidevice_setup/host_verifier_impl.cc
index c0bb37a..e5d4618 100644
--- a/ash/services/multidevice_setup/host_verifier_impl.cc
+++ b/ash/services/multidevice_setup/host_verifier_impl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
@@ -14,7 +15,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
index a3d7fb9..ccf667c 100644
--- a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "ash/services/multidevice_setup/fake_host_backend_delegate.h"
 #include "ash/services/multidevice_setup/fake_host_verifier.h"
 #include "base/test/scoped_feature_list.h"
@@ -17,7 +18,6 @@
 #include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/BUILD.gn b/ash/services/secure_channel/BUILD.gn
index adbefea0..5054be7 100644
--- a/ash/services/secure_channel/BUILD.gn
+++ b/ash/services/secure_channel/BUILD.gn
@@ -173,13 +173,13 @@
 
   deps = [
     "//ash/constants",
+    "//ash/services/device_sync/proto",
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//base",
     "//chromeos",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
-    "//chromeos/services/device_sync/proto",
     "//crypto",
     "//device/bluetooth",
     "//device/bluetooth/public/cpp",
diff --git a/ash/services/secure_channel/DEPS b/ash/services/secure_channel/DEPS
index ac4bf15a..b3ad22e 100644
--- a/ash/services/secure_channel/DEPS
+++ b/ash/services/secure_channel/DEPS
@@ -1,8 +1,11 @@
 include_rules = [
   "+ash/constants",
+  "+ash/services/device_sync",
   "+ash/services/multidevice_setup/public/cpp",
   "+crypto",
-  "+chromeos/services/device_sync",
   "+third_party/securemessage",
   "+third_party/ukey2",
+
+  # TODO(https://crbug.com/1164001): Remove this deps when migration is done.
+  "+chromeos/services/device_sync",
 ]
diff --git a/ash/services/secure_channel/background_eid_generator.cc b/ash/services/secure_channel/background_eid_generator.cc
index c7c5d70..3ea9bba 100644
--- a/ash/services/secure_channel/background_eid_generator.cc
+++ b/ash/services/secure_channel/background_eid_generator.cc
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/strings/string_number_conversions.h"
@@ -16,7 +17,6 @@
 #include "chromeos/components/multidevice/beacon_seed.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/background_eid_generator_unittest.cc b/ash/services/secure_channel/background_eid_generator_unittest.cc
index 6ffb3cc..34975cbc 100644
--- a/ash/services/secure_channel/background_eid_generator_unittest.cc
+++ b/ash/services/secure_channel/background_eid_generator_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/strings/string_util.h"
 #include "base/test/simple_test_clock.h"
@@ -14,7 +15,6 @@
 #include "chromeos/components/multidevice/beacon_seed.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
index a806023..45f912aa 100644
--- a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
+++ b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/mock_foreground_eid_generator.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/ble_scanner_impl.cc b/ash/services/secure_channel/ble_scanner_impl.cc
index 1dde8acc..eb1cf801 100644
--- a/ash/services/secure_channel/ble_scanner_impl.cc
+++ b/ash/services/secure_channel/ble_scanner_impl.cc
@@ -7,6 +7,7 @@
 #include <iostream>
 #include <sstream>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/ble_constants.h"
 #include "ash/services/secure_channel/ble_synchronizer_base.h"
 #include "base/bind.h"
@@ -16,7 +17,6 @@
 #include "base/strings/string_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_session.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
diff --git a/ash/services/secure_channel/device_to_device_initiator_helper.cc b/ash/services/secure_channel/device_to_device_initiator_helper.cc
index 7a521301..95d9285 100644
--- a/ash/services/secure_channel/device_to_device_initiator_helper.cc
+++ b/ash/services/secure_channel/device_to_device_initiator_helper.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/secure_channel/device_to_device_initiator_helper.h"
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/secure_message_delegate.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/device_to_device_responder_operations.cc b/ash/services/secure_channel/device_to_device_responder_operations.cc
index e7631675..918007e7 100644
--- a/ash/services/secure_channel/device_to_device_responder_operations.cc
+++ b/ash/services/secure_channel/device_to_device_responder_operations.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/secure_channel/device_to_device_responder_operations.h"
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/secure_message_delegate.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 #include "third_party/ukey2/proto/device_to_device_messages.pb.h"
 
diff --git a/ash/services/secure_channel/device_to_device_secure_context.cc b/ash/services/secure_channel/device_to_device_secure_context.cc
index 8ab7146..30fb649 100644
--- a/ash/services/secure_channel/device_to_device_secure_context.cc
+++ b/ash/services/secure_channel/device_to_device_secure_context.cc
@@ -6,11 +6,11 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/secure_message_delegate.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
index 913a78a1..6aab6b2 100644
--- a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
+++ b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
@@ -7,12 +7,12 @@
 #include <list>
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
diff --git a/ash/services/secure_channel/foreground_eid_generator.cc b/ash/services/secure_channel/foreground_eid_generator.cc
index 09b2f979..ed01435 100644
--- a/ash/services/secure_channel/foreground_eid_generator.cc
+++ b/ash/services/secure_channel/foreground_eid_generator.cc
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/memory/ptr_util.h"
@@ -16,7 +17,6 @@
 #include "base/time/time.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/foreground_eid_generator_unittest.cc b/ash/services/secure_channel/foreground_eid_generator_unittest.cc
index a008631..c7f50f4 100644
--- a/ash/services/secure_channel/foreground_eid_generator_unittest.cc
+++ b/ash/services/secure_channel/foreground_eid_generator_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/strings/string_util.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/public/mojom/secure_channel.mojom b/ash/services/secure_channel/public/mojom/secure_channel.mojom
index 68e2965..1478873 100644
--- a/ash/services/secure_channel/public/mojom/secure_channel.mojom
+++ b/ash/services/secure_channel/public/mojom/secure_channel.mojom
@@ -123,7 +123,7 @@
   // bytes were publicly visible over BLE when the connection with the remote
   // device was being established. It is used by some clients for further
   // cryptographic communication.
-  // Defined by //chromeos/services/device_sync/proto/securemessage.proto.
+  // Defined by //ash/services/device_sync/proto/securemessage.proto.
   string channel_binding_data;
 };
 
diff --git a/ash/system/hps/hps_configuration.cc b/ash/system/hps/hps_configuration.cc
index eca3c85..07be9662 100644
--- a/ash/system/hps/hps_configuration.cc
+++ b/ash/system/hps/hps_configuration.cc
@@ -51,11 +51,13 @@
 hps::FeatureConfig GetDefaultHpsNotifyFeatureConfig() {
   hps::FeatureConfig config;
 
-  auto& filter_config = *config.mutable_average_filter_config();
-  filter_config.set_average_window_size(3);
-  filter_config.set_positive_score_threshold(-30);
-  filter_config.set_negative_score_threshold(-30);
-  filter_config.set_default_uncertain_score(-128);
+  // Just apply a threshold to the last-seen inference.
+  auto& filter_config = *config.mutable_consecutive_results_filter_config();
+  filter_config.set_positive_count_threshold(1);
+  filter_config.set_negative_count_threshold(1);
+  filter_config.set_uncertain_count_threshold(1);
+  filter_config.set_positive_score_threshold(-50);
+  filter_config.set_negative_score_threshold(-50);
 
   return config;
 }
diff --git a/ash/system/hps/hps_configuration_unittest.cc b/ash/system/hps/hps_configuration_unittest.cc
index 157a797..ba565603 100644
--- a/ash/system/hps/hps_configuration_unittest.cc
+++ b/ash/system/hps/hps_configuration_unittest.cc
@@ -30,7 +30,7 @@
   EXPECT_EQ(GetEnableHpsSenseConfig()->filter_config_case(),
             hps::FeatureConfig::kConsecutiveResultsFilterConfig);
   EXPECT_EQ(GetEnableHpsNotifyConfig()->filter_config_case(),
-            hps::FeatureConfig::kAverageFilterConfig);
+            hps::FeatureConfig::kConsecutiveResultsFilterConfig);
 }
 
 TEST(HpsFeatureConfigTest, ReturnNullIfTypeIsNotRecognizableHpsSense) {
diff --git a/ash/system/model/system_tray_model.cc b/ash/system/model/system_tray_model.cc
index a4465ee3..0110397 100644
--- a/ash/system/model/system_tray_model.cc
+++ b/ash/system/model/system_tray_model.cc
@@ -38,12 +38,10 @@
       virtual_keyboard_(std::make_unique<VirtualKeyboardModel>()),
       network_state_model_(std::make_unique<TrayNetworkStateModel>()),
       active_network_icon_(
-          std::make_unique<ActiveNetworkIcon>(network_state_model_.get())) {
-  std::set<base::Time> prunable_months;
-  calendar_utils::GetSurroundingMonthsUTC(
-      base::Time::Now(), CalendarModel::kNumSurroundingMonthsCached,
-      prunable_months);
-  calendar_model_ = std::make_unique<CalendarModel>(prunable_months);
+          std::make_unique<ActiveNetworkIcon>(network_state_model_.get())),
+      calendar_model_(std::make_unique<CalendarModel>()) {
+  calendar_model_->FetchEventsSurrounding(
+      CalendarModel::kNumSurroundingMonthsCached, base::Time::Now());
 }
 
 SystemTrayModel::~SystemTrayModel() = default;
diff --git a/ash/system/phonehub/animated_loading_card.cc b/ash/system/phonehub/animated_loading_card.cc
deleted file mode 100644
index 1bf2658..0000000
--- a/ash/system/phonehub/animated_loading_card.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "ash/system/phonehub/animated_loading_card.h"
-
-#include "ui/compositor/layer.h"
-#include "ui/compositor/paint_recorder.h"
-#include "ui/gfx/color_utils.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/views/layout/layout_provider.h"
-
-namespace ash {
-AnimatedLoadingCard::AnimatedLoadingCard() {
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-  layer()->SetFillsBoundsCompletely(false);
-}
-
-AnimatedLoadingCard::~AnimatedLoadingCard() = default;
-
-void AnimatedLoadingCard::OnPaint(gfx::Canvas* canvas) {
-  View::OnPaint(canvas);
-  const SkColor color = SkColorSetRGB(241, 243, 244);
-
-  cc::PaintFlags flags;
-  flags.setShader(cc::PaintShader::MakeColor(color));
-  gfx::Rect local_bounds = gfx::Rect(layer()->size());
-  const float dsf = canvas->UndoDeviceScaleFactor();
-  gfx::RectF local_bounds_f = gfx::RectF(local_bounds);
-  local_bounds_f.Scale(dsf);
-  canvas->DrawRect(gfx::ToEnclosingRect(local_bounds_f), flags);
-}
-}  // namespace ash
diff --git a/ash/system/phonehub/animated_loading_card.h b/ash/system/phonehub/animated_loading_card.h
deleted file mode 100644
index 5d699c5..0000000
--- a/ash/system/phonehub/animated_loading_card.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/ash_export.h"
-#include "ui/views/view.h"
-
-#ifndef ASH_SYSTEM_PHONEHUB_ANIMATED_LOADING_CARD_H_
-#define ASH_SYSTEM_PHONEHUB_ANIMATED_LOADING_CARD_H_
-
-namespace ash {
-// A camera roll item card loading animation based on specs defined here:
-// https://carbon.googleplex.com/cros-ux/pages/show-off/motion#d9d2fa79-1a5e-4e51-834c-273072edbd45
-class ASH_EXPORT AnimatedLoadingCard : public views::View {
- public:
-  AnimatedLoadingCard();
-  AnimatedLoadingCard(const AnimatedLoadingCard&) = delete;
-  AnimatedLoadingCard& operator=(const AnimatedLoadingCard&) = delete;
-  ~AnimatedLoadingCard() override;
-
- protected:
-  // views::View override
-  void OnPaint(gfx::Canvas* canvas) override;
-};
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_PHONEHUB_ANIMATED_LOADING_CARD_H_
diff --git a/ash/system/phonehub/camera_roll_view.cc b/ash/system/phonehub/camera_roll_view.cc
index 1f093db..ad64887 100644
--- a/ash/system/phonehub/camera_roll_view.cc
+++ b/ash/system/phonehub/camera_roll_view.cc
@@ -8,7 +8,6 @@
 
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/system/phonehub/animated_loading_card.h"
 #include "ash/system/phonehub/camera_roll_thumbnail.h"
 #include "ash/system/phonehub/phone_hub_metrics.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
@@ -33,13 +32,6 @@
 constexpr int kCameraRollItemVerticalPadding = 4;
 constexpr int kHeaderLabelLineHeight = 48;
 
-// Animation constants for loading card
-constexpr float kAnimationLoadingCardCorner = 12.0f;
-constexpr float kAnimationLoadingCardOpacity = 0.15f;
-constexpr int kAnimationLoadingCardDelayInMs = 83;
-constexpr int kAnimationLoadingCardTransitDurationInMs = 200;
-constexpr int kAnimationLoadingCardFreezeDurationInMs = 150;
-
 // Typography.
 constexpr int kHeaderTextFontSizeDip = 15;
 
@@ -121,43 +113,6 @@
   AddChildView(camera_roll_item);
 }
 
-void CameraRollView::CameraRollItemsView::AddLoadingAnimatedItem(
-    bool disable_repeated_animation_for_test) {
-  gfx::RoundedCornersF rounded_corners(
-      kAnimationLoadingCardCorner, kAnimationLoadingCardCorner,
-      kAnimationLoadingCardCorner, kAnimationLoadingCardCorner);
-  views::AnimationBuilder animation_builder;
-  // create 4 annimated loading cards
-  for (size_t index = 0; index < kCameraRollItemsInRow; index++) {
-    AnimatedLoadingCard* loading_card = new AnimatedLoadingCard();
-    camera_roll_items_.Add(loading_card, index);
-    animation_builder.Once()
-        .SetRoundedCorners(loading_card, rounded_corners)
-        .SetOpacity(loading_card,
-                    kAnimationLoadingCardOpacity * (index % 4 + 1));
-    if (!disable_repeated_animation_for_test) {
-      // In order to test a view with repeated animation, we need to do
-      // loading_card->layer()->GetAnimator()->set_disable_timer_for_test(true)
-      // before animation_builder tourches the view and manually step through
-      // the timer, otherwise test will time out. Cosidering view unitest is
-      // focusing on verify view state,disable repeated animation for tests.
-      animation_builder.Repeatedly()
-          .Offset(base::Milliseconds(kAnimationLoadingCardDelayInMs * index))
-          .SetDuration(
-              base::Milliseconds(kAnimationLoadingCardTransitDurationInMs))
-          .SetOpacity(loading_card, 1.0f, gfx::Tween::LINEAR)
-          .Then()
-          .Offset(base::Milliseconds(kAnimationLoadingCardFreezeDurationInMs))
-          .Then()
-          .SetDuration(
-              base::Milliseconds(kAnimationLoadingCardTransitDurationInMs))
-          .SetOpacity(loading_card, kAnimationLoadingCardOpacity,
-                      gfx::Tween::LINEAR);
-    }
-    AddChildView(loading_card);
-  }
-}
-
 void CameraRollView::CameraRollItemsView::Reset() {
   camera_roll_items_.Clear();
   RemoveAllChildViews();
@@ -218,15 +173,8 @@
   switch (current_ui_state) {
     case phonehub::CameraRollManager::CameraRollUiState::SHOULD_HIDE:
     case phonehub::CameraRollManager::CameraRollUiState::NO_STORAGE_PERMISSION:
-    case phonehub::CameraRollManager::CameraRollUiState::CAN_OPT_IN:
       SetVisible(false);
       break;
-    case phonehub::CameraRollManager::CameraRollUiState::LOADING_VIEW:
-      items_view_->SetVisible(true);
-      SetVisible(true);
-      items_view_->AddLoadingAnimatedItem(
-          should_disable_annimator_timer_for_test_);
-      break;
     case phonehub::CameraRollManager::CameraRollUiState::ITEMS_VISIBLE:
       items_view_->SetVisible(true);
       SetVisible(true);
diff --git a/ash/system/phonehub/camera_roll_view.h b/ash/system/phonehub/camera_roll_view.h
index f0e435a..699ee84 100644
--- a/ash/system/phonehub/camera_roll_view.h
+++ b/ash/system/phonehub/camera_roll_view.h
@@ -36,14 +36,9 @@
   // views::View:
   const char* GetClassName() const override;
 
- protected:
-  bool should_disable_annimator_timer_for_test_ = false;
-
  private:
   friend class CameraRollViewTest;
-  FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, DisplayOptInView);
   FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, OptInAlready);
-  FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, RightAfterOptIn);
   FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, ViewLayout);
   FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, ImageThumbnail);
   FRIEND_TEST_ALL_PREFIXES(CameraRollViewTest, VideoThumbnail);
@@ -56,7 +51,6 @@
     CameraRollItemsView operator=(CameraRollItemsView&) = delete;
 
     void AddCameraRollItem(views::View* camera_roll_item);
-    void AddLoadingAnimatedItem(bool disable_repeated_animation_for_test);
     void Reset();
 
     // views::View:
diff --git a/ash/system/phonehub/camera_roll_view_unittest.cc b/ash/system/phonehub/camera_roll_view_unittest.cc
index 5d2906a..c8c60a8 100644
--- a/ash/system/phonehub/camera_roll_view_unittest.cc
+++ b/ash/system/phonehub/camera_roll_view_unittest.cc
@@ -13,9 +13,6 @@
 #include "camera_roll_view.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/compositor/property_change_reason.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/compositor/test/layer_animator_test_controller.h"
-#include "ui/compositor/test/test_layer_animation_delegate.h"
 #include "ui/gfx/image/image.h"
 #include "ui/views/controls/button/menu_button.h"
 #include "ui/views/view.h"
@@ -26,9 +23,7 @@
  public:
   CameraRollViewForTest(phonehub::CameraRollManager* camera_roll_manager,
                         phonehub::UserActionRecorder* user_action_recorder)
-      : CameraRollView(camera_roll_manager, user_action_recorder) {
-    should_disable_annimator_timer_for_test_ = true;
-  }
+      : CameraRollView(camera_roll_manager, user_action_recorder) {}
   ~CameraRollViewForTest() override = default;
 };
 
@@ -53,13 +48,9 @@
     return fake_camera_roll_manager_.get();
   }
 
-  void PresetCameraRollOptInState(bool has_been_dismissed,
-                                  bool can_be_enabled) {
+  void PresetCameraRollOptInState(bool can_be_enabled) {
     fake_camera_roll_manager_ =
         std::make_unique<phonehub::FakeCameraRollManager>();
-    if (has_been_dismissed) {
-      fake_camera_roll_manager_->OnCameraRollOnboardingUiDismissed();
-    }
     fake_camera_roll_manager_->SetIsCameraRollAvailableToBeEnabled(
         can_be_enabled);
     fake_user_action_recorder_ =
@@ -127,8 +118,7 @@
 };
 
 TEST_F(CameraRollViewTest, OptInAlready) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/false,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
 
   fake_camera_roll_manager()->ClearCurrentItems();
   EXPECT_FALSE(camera_roll_view()->GetVisible());
@@ -138,24 +128,8 @@
   EXPECT_TRUE(camera_roll_view()->items_view_->GetVisible());
 }
 
-TEST_F(CameraRollViewTest, RightAfterOptIn) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/false,
-                             /*can_be_enabled=*/false);
-  fake_camera_roll_manager()->EnableCameraRollFeatureInSystemSetting();
-
-  EXPECT_TRUE(camera_roll_view()->GetVisible());
-  EXPECT_TRUE(camera_roll_view()->items_view_->GetVisible());
-  // There should be 4 camera roll item placeholder.
-  size_t expected_placeholder_seize = 4;
-  EXPECT_EQ(GetItemsView()->children().size(), expected_placeholder_seize);
-  size_t expected_item_size = 0;
-  EXPECT_EQ(fake_camera_roll_manager()->current_items().size(),
-            expected_item_size);
-}
-
 TEST_F(CameraRollViewTest, OptInAndDismissed) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/true);
+  PresetCameraRollOptInState(/*can_be_enabled=*/true);
 
   fake_camera_roll_manager()->ClearCurrentItems();
   EXPECT_FALSE(camera_roll_view()->GetVisible());
@@ -166,8 +140,7 @@
 }
 
 TEST_F(CameraRollViewTest, ViewVisibility) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   // The camera roll view is not visible if there are no items available and
   // visible when there are one or more items available.
   fake_camera_roll_manager()->ClearCurrentItems();
@@ -181,8 +154,7 @@
 }
 
 TEST_F(CameraRollViewTest, SingleItem) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   // Set 1 camera roll item.
   size_t expected_size = 1;
   fake_camera_roll_manager()->SetCurrentItems(CreateFakeItems(expected_size));
@@ -190,8 +162,7 @@
 }
 
 TEST_F(CameraRollViewTest, MultipleItems) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   // Set 4 camera roll items.
   size_t expected_size = 4;
   fake_camera_roll_manager()->SetCurrentItems(CreateFakeItems(expected_size));
@@ -199,8 +170,7 @@
 }
 
 TEST_F(CameraRollViewTest, ViewLayout) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   // Test the layout size and positions of the items. If the layout is being
   // intentionally changed this test will need to be updated.
   fake_camera_roll_manager()->SetCurrentItems(CreateFakeItems(4));
@@ -213,8 +183,7 @@
 }
 
 TEST_F(CameraRollViewTest, AccessibleNameAndTooltip) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   fake_camera_roll_manager()->SetCurrentItems(CreateFakeItems(4));
 
   EXPECT_EQ(u"Recent photo 1 of 4.", GetThumbnailView(0)->GetAccessibleName());
@@ -228,8 +197,7 @@
 }
 
 TEST_F(CameraRollViewTest, ImageThumbnail) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   fake_camera_roll_manager()->SetCurrentItems(
       CreateSingleItemWithType(/*is_video=*/false));
 
@@ -238,8 +206,7 @@
 }
 
 TEST_F(CameraRollViewTest, VideoThumbnail) {
-  PresetCameraRollOptInState(/*has_been_dismissed=*/true,
-                             /*can_be_enabled=*/false);
+  PresetCameraRollOptInState(/*can_be_enabled=*/false);
   fake_camera_roll_manager()->SetCurrentItems(
       CreateSingleItemWithType(/*is_video=*/true));
 
diff --git a/ash/system/phonehub/phone_connected_view.cc b/ash/system/phonehub/phone_connected_view.cc
index 0681233..25c87619 100644
--- a/ash/system/phonehub/phone_connected_view.cc
+++ b/ash/system/phonehub/phone_connected_view.cc
@@ -58,8 +58,7 @@
 
   auto* recent_apps_handler =
       phone_hub_manager->GetRecentAppsInteractionHandler();
-  if (features::IsEcheSWAEnabled() && features::IsPhoneHubRecentAppsEnabled() &&
-      recent_apps_handler) {
+  if (features::IsEcheSWAEnabled() && recent_apps_handler) {
     setup_layered_view(AddChildView(
         std::make_unique<PhoneHubRecentAppsView>(recent_apps_handler)));
   }
diff --git a/ash/system/time/calendar_model.cc b/ash/system/time/calendar_model.cc
index f8da231..ce35ef5 100644
--- a/ash/system/time/calendar_model.cc
+++ b/ash/system/time/calendar_model.cc
@@ -150,10 +150,7 @@
   base::WeakPtrFactory<CalendarEventFetch> weak_factory_{this};
 };
 
-// The calendar model itself.
-CalendarModel::CalendarModel(const std::set<base::Time>& base_months) {
-  FetchEvents(base_months);
-}
+CalendarModel::CalendarModel() = default;
 
 CalendarModel::~CalendarModel() = default;
 
@@ -235,6 +232,13 @@
     MaybeFetchMonth(month.UTCMidnight());
 }
 
+void CalendarModel::FetchEventsSurrounding(int num_months,
+                                           const base::Time day) {
+  std::set<base::Time> months =
+      calendar_utils::GetSurroundingMonthsUTC(day, num_months);
+  FetchEvents(months);
+}
+
 int CalendarModel::EventsNumberOfDayInternal(base::Time day,
                                              SingleDayEventList* events) const {
   const SingleDayEventList& list = FindEvents(day);
diff --git a/ash/system/time/calendar_model.h b/ash/system/time/calendar_model.h
index ca24f19..09deae7b 100644
--- a/ash/system/time/calendar_model.h
+++ b/ash/system/time/calendar_model.h
@@ -42,7 +42,7 @@
 // Controller of the `CalendarView`.
 class ASH_EXPORT CalendarModel {
  public:
-  explicit CalendarModel(const std::set<base::Time>& base_months);
+  CalendarModel();
   CalendarModel(const CalendarModel& other) = delete;
   CalendarModel& operator=(const CalendarModel& other) = delete;
   virtual ~CalendarModel();
@@ -72,6 +72,9 @@
   // Requests events that fall in |months|.
   void FetchEvents(const std::set<base::Time>& months);
 
+  // Requests events that fall in |num_months| months surrounding |day|.
+  void FetchEventsSurrounding(int num_months, const base::Time day);
+
   // Same as `FindEvents`, except that return of any events on `day` constitutes
   // "use" in the most-recently-used sense, so the month that includes day will
   // then be promoted to most-recently-used status.  Use this to get events if
diff --git a/ash/system/time/calendar_model_unittest.cc b/ash/system/time/calendar_model_unittest.cc
index af135af8..adcdd665 100644
--- a/ash/system/time/calendar_model_unittest.cc
+++ b/ash/system/time/calendar_model_unittest.cc
@@ -48,8 +48,7 @@
       /*thread_ticks_override=*/nullptr);
 
   // 0 months out.
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 0UL, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 0);
   EXPECT_EQ(1UL, months.size());
   EXPECT_TRUE(months.find(start_of_month) != months.end());
 
@@ -62,8 +61,7 @@
   result =
       base::Time::FromString("01 Nov 2009 00:00 GMT", &start_of_next_month);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1UL, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   EXPECT_EQ(3UL, months.size());
   EXPECT_TRUE(months.find(start_of_month) != months.end());
   EXPECT_TRUE(months.find(start_of_previous_month) != months.end());
@@ -78,8 +76,7 @@
   result =
       base::Time::FromString("01 Dec 2009 00:00 GMT", &start_of_next_month_2);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 2UL, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 2);
   EXPECT_EQ(5UL, months.size());
   EXPECT_TRUE(months.find(start_of_month) != months.end());
   EXPECT_TRUE(months.find(start_of_previous_month) != months.end());
@@ -96,8 +93,7 @@
   result =
       base::Time::FromString("01 Jan 2010 00:00 GMT", &start_of_next_month_3);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 3UL, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 3);
   EXPECT_EQ(7UL, months.size());
   EXPECT_TRUE(months.find(start_of_month) != months.end());
   EXPECT_TRUE(months.find(start_of_previous_month) != months.end());
@@ -112,8 +108,7 @@
 // events that CalendarModel queries via the Google calendar API.
 class TestableCalendarModel : public CalendarModel {
  public:
-  explicit TestableCalendarModel(const std::set<base::Time> non_prunable_months)
-      : CalendarModel(non_prunable_months) {}
+  TestableCalendarModel() = default;
   TestableCalendarModel(const TestableCalendarModel& other) = delete;
   TestableCalendarModel& operator=(const TestableCalendarModel& other) = delete;
   ~TestableCalendarModel() override = default;
@@ -169,12 +164,11 @@
 
  protected:
   void MaybeFetchMonth(base::Time start_of_month) override {
-    if (IsMonthAlreadyFetched(start_of_month))
+    // Early return if the month has already been fetched or no events (not even
+    // an empty list) have been injected.
+    if (IsMonthAlreadyFetched(start_of_month) || !injected_events_.get())
       return;
 
-    // Month has officially been fetched.
-    MarkMonthAsFetched(start_of_month);
-
     std::unique_ptr<google_apis::calendar::EventList> fetched_events =
         std::make_unique<google_apis::calendar::EventList>();
     base::Time::Exploded exp_month_start;
@@ -276,9 +270,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // An event fetcher was instantiated, and there are no events for today (or
   // any day).
@@ -292,7 +286,7 @@
   const char* kId = "id_0";
   const char* kSummary = "summary_0";
 
-  // Current date is just kStartTime.
+  // Current date is just `kStartTime`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime, &current_date);
   DCHECK(result);
@@ -303,9 +297,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Set up list of events to inject.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -315,7 +309,7 @@
       calendar_test_utils::CreateEvent(kId, kSummary, kStartTime, kEndTime);
   SingleDayEventList events;
 
-  // Haven't injected anything yet, so no events on kStartTime0.
+  // Haven't injected anything yet, so no events on `kStartTime0`.
   events.clear();
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime, &events));
   EXPECT_TRUE(events.empty());
@@ -345,7 +339,7 @@
   const char* kId1 = "id_1";
   const char* kSummary1 = "summary_1";
 
-  // Current date is just kStartTime0.
+  // Current date is just `kStartTime0`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime0, &current_date);
   DCHECK(result);
@@ -356,9 +350,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Get ready to inject two events.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -370,7 +364,8 @@
       calendar_test_utils::CreateEvent(kId1, kSummary1, kStartTime1, kEndTime1);
   SingleDayEventList events;
 
-  // Haven't injected anything yet, so no events on kStartTime0 or kStartTime1.
+  // Haven't injected anything yet, so no events on `kStartTime0` or
+  // `kStartTime1`.
   events.clear();
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime0, &events));
   EXPECT_TRUE(events.empty());
@@ -403,7 +398,7 @@
   const char* kId1 = "id_1";
   const char* kSummary1 = "summary_1";
 
-  // Current date is just kStartTime0.
+  // Current date is just `kStartTime0`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime0, &current_date);
   DCHECK(result);
@@ -414,9 +409,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Get ready to inject two events.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -443,8 +438,8 @@
   EXPECT_EQ(1, EventsNumberOfDay(kStartTime1, &events));
 
   // Adjusts the time with -10 hours.
-  // kStartTime0 "23 Oct 2009 11:30" -> "23 Oct 2009 1:30".
-  // kStartTime1 "24 Oct 2009 07:30" -> "23 Oct 2009 21:30"
+  // `kStartTime0` "23 Oct 2009 11:30" -> "23 Oct 2009 1:30".
+  // `kStartTime1` "24 Oct 2009 07:30" -> "23 Oct 2009 21:30"
   // Both events should be on the 23rd.
   calendar_model_->RedistributeEvents(/*time_difference_minutes=*/-10 * 60);
   events.clear();
@@ -454,8 +449,8 @@
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime1, &events));
 
   // Adjusts the time with +15 hours.
-  // kStartTime0 "23 Oct 2009 11:30" -> "24 Oct 2009 2:30".
-  // kStartTime1 "24 Oct 2009 07:30" -> "24 Oct 2009 22:30"
+  // `kStartTime0` "23 Oct 2009 11:30" -> "24 Oct 2009 2:30".
+  // `kStartTime1` "24 Oct 2009 07:30" -> "24 Oct 2009 22:30"
   // Both events should be on the 24rd.
   calendar_model_->RedistributeEvents(/*time_difference_minutes=*/15 * 60);
   events.clear();
@@ -479,7 +474,7 @@
   const char* kId2 = "id_2";
   const char* kSummary2 = "summary_2";
 
-  // Current date is just kStartTime0.
+  // Current date is just `kStartTime0`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime0, &current_date);
   DCHECK(result);
@@ -490,9 +485,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Set up list of events to inject.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -502,7 +497,7 @@
       calendar_test_utils::CreateEvent(kId0, kSummary0, kStartTime0, kEndTime0);
   SingleDayEventList events;
 
-  // No events at kStartTime0.
+  // No events at `kStartTime0`.
   events.clear();
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime0, &events));
   EXPECT_TRUE(events.empty());
@@ -565,7 +560,7 @@
   const char* kId2 = "id_2";
   const char* kSummary2 = "summary_2";
 
-  // Current date is just kStartTime1.
+  // Current date is just `kStartTime1`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime1, &current_date);
   DCHECK(result);
@@ -576,9 +571,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Set up list of events to inject.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -700,9 +695,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Get our event list ready.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -776,8 +771,7 @@
   // Advance us to `kStartTime2` and fetch again.
   result = base::Time::FromString(kStartTime2, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
 
   // Now `kStartTime3` should be cached.
@@ -788,40 +782,35 @@
   // to prune.
   result = base::Time::FromString(kStartTime3, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime4, &events));
 
   result = base::Time::FromString(kStartTime4, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime5, &events));
 
   result = base::Time::FromString(kStartTime5, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime6, &events));
 
   result = base::Time::FromString(kStartTime6, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime7, &events));
 
   result = base::Time::FromString(kStartTime7, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime8, &events));
@@ -836,8 +825,7 @@
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime0, &events));
   result = base::Time::FromString(kStartTime8, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime9, &events));
@@ -849,8 +837,7 @@
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime1, &events));
   result = base::Time::FromString(kStartTime9, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime10, &events));
@@ -862,8 +849,7 @@
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime2, &events));
   result = base::Time::FromString(kStartTime10, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime11, &events));
@@ -875,8 +861,7 @@
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime3, &events));
   result = base::Time::FromString(kStartTime11, &current_date);
   DCHECK(result);
-  months.clear();
-  calendar_utils::GetSurroundingMonthsUTC(current_date, 1, months);
+  months = calendar_utils::GetSurroundingMonthsUTC(current_date, 1);
   calendar_model_->FetchEvents(months);
   events.clear();
   EXPECT_EQ(1, EventsNumberOfDayInternal(kStartTime12, &events));
@@ -891,7 +876,7 @@
   const char* kSummary = "summary_0";
   base::HistogramTester histogram_tester;
 
-  // Current date is just kStartTime.
+  // Current date is just `kStartTime`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime, &current_date);
   DCHECK(result);
@@ -902,9 +887,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Set up list of events to inject.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -914,7 +899,7 @@
       calendar_test_utils::CreateEvent(kId, kSummary, kStartTime, kEndTime);
   SingleDayEventList events;
 
-  // Haven't injected anything yet, so no events on kStartTime0.
+  // Haven't injected anything yet, so no events on `kStartTime0`.
   events.clear();
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime, &events));
   EXPECT_TRUE(events.empty());
@@ -941,7 +926,7 @@
   const char* kSummary = "summary_0";
   base::HistogramTester histogram_tester;
 
-  // Current date is just kStartTime.
+  // Current date is just `kStartTime`.
   base::Time current_date;
   bool result = base::Time::FromString(kStartTime, &current_date);
   DCHECK(result);
@@ -952,9 +937,9 @@
       /*thread_ticks_override=*/nullptr);
 
   base::Time now = base::Time::Now();
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(now, 1, months);
-  calendar_model_ = std::make_unique<TestableCalendarModel>(months);
+  std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(now, 1);
+  calendar_model_ = std::make_unique<TestableCalendarModel>();
+  calendar_model_->FetchEvents(months);
 
   // Set up list of events to inject.
   std::unique_ptr<google_apis::calendar::EventList> event_list =
@@ -964,7 +949,7 @@
       calendar_test_utils::CreateEvent(kId, kSummary, kStartTime, kEndTime);
   SingleDayEventList events;
 
-  // Haven't injected anything yet, so no events on kStartTime0.
+  // Haven't injected anything yet, so no events on `kStartTime0`.
   events.clear();
   EXPECT_EQ(0, EventsNumberOfDay(kStartTime, &events));
   EXPECT_TRUE(events.empty());
diff --git a/ash/system/time/calendar_utils.cc b/ash/system/time/calendar_utils.cc
index e701c3a..f47d961 100644
--- a/ash/system/time/calendar_utils.cc
+++ b/ash/system/time/calendar_utils.cc
@@ -33,30 +33,28 @@
          base::TimeFormatWithPattern(date_b.value(), "dd MM YYYY");
 }
 
-ASH_EXPORT void GetSurroundingMonthsUTC(const base::Time& selected_date,
-                                        unsigned int num_months_out,
-                                        std::set<base::Time>& months_) {
-  // Make the output is empty before we start.
-  months_.clear();
+ASH_EXPORT std::set<base::Time> GetSurroundingMonthsUTC(
+    const base::Time& selected_date,
+    int num_months_out) {
+  std::set<base::Time> months;
 
   // First month is the one that contains |selected_date|.
   base::Time selected_date_start =
       calendar_utils::GetStartOfMonthUTC(selected_date);
-  months_.emplace(selected_date_start);
+  months.emplace(selected_date_start);
 
-  // Add |num_months_out| before.
-  base::Time current = selected_date_start;
-  for (unsigned int i = 0; i < num_months_out; ++i) {
-    current = calendar_utils::GetStartOfPreviousMonthUTC(current);
-    months_.emplace(current);
+  // Add |num_months_out| before and after.
+  base::Time current_forward = selected_date_start;
+  base::Time current_backward = selected_date_start;
+  for (int i = 0; i < num_months_out; ++i) {
+    current_forward = calendar_utils::GetStartOfNextMonthUTC(current_forward);
+    months.emplace(current_forward);
+    current_backward =
+        calendar_utils::GetStartOfPreviousMonthUTC(current_backward);
+    months.emplace(current_backward);
   }
 
-  // Add |num_months_out| after.
-  current = selected_date_start;
-  for (unsigned int i = 0; i < num_months_out; ++i) {
-    current = calendar_utils::GetStartOfNextMonthUTC(current);
-    months_.emplace(current);
-  }
+  return months;
 }
 
 base::Time::Exploded GetExplodedLocal(const base::Time& date) {
diff --git a/ash/system/time/calendar_utils.h b/ash/system/time/calendar_utils.h
index 37b5984..23ced191 100644
--- a/ash/system/time/calendar_utils.h
+++ b/ash/system/time/calendar_utils.h
@@ -54,9 +54,8 @@
 
 // Returns the set of months that includes |selected_date| and
 // |num_months_out| before and after.
-void GetSurroundingMonthsUTC(const base::Time& selected_date,
-                             unsigned int num_months_out,
-                             std::set<base::Time>& months_);
+std::set<base::Time> GetSurroundingMonthsUTC(const base::Time& selected_date,
+                                             int num_months_out);
 
 // Gets the given `date`'s `Exploded` instance, in local time.
 base::Time::Exploded GetExplodedLocal(const base::Time& date);
diff --git a/ash/system/time/calendar_view_controller.cc b/ash/system/time/calendar_view_controller.cc
index 4e78941..b09a190 100644
--- a/ash/system/time/calendar_view_controller.cc
+++ b/ash/system/time/calendar_view_controller.cc
@@ -187,11 +187,9 @@
 }
 
 void CalendarViewController::FetchEvents() {
-  std::set<base::Time> months;
-  calendar_utils::GetSurroundingMonthsUTC(
-      GetOnScreenMonthFirstDayUTC().UTCMidnight(),
-      CalendarModel::kNumSurroundingMonthsCached, months);
-  Shell::Get()->system_tray_model()->calendar_model()->FetchEvents(months);
+  Shell::Get()->system_tray_model()->calendar_model()->FetchEventsSurrounding(
+      CalendarModel::kNumSurroundingMonthsCached,
+      GetOnScreenMonthFirstDayUTC().UTCMidnight());
 }
 
 SingleDayEventList CalendarViewController::SelectedDateEvents() {
diff --git a/ash/test/layer_animation_stopped_waiter.h b/ash/test/layer_animation_stopped_waiter.h
index 7ea6766..cfddabb5 100644
--- a/ash/test/layer_animation_stopped_waiter.h
+++ b/ash/test/layer_animation_stopped_waiter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,8 @@
 
 namespace ash {
 
-// A class capable of waiting until a layer has stopped animating.
+// A class capable of waiting until a layer has stopped animating. Supports
+// animations that delete the layer on completion.
 class LayerAnimationStoppedWaiter : public ui::LayerAnimationObserver {
  public:
   LayerAnimationStoppedWaiter();
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
index de272c8..e13cb35 100644
--- a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
@@ -38,8 +38,7 @@
   // AshTestBase::Test:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kEcheSWA, features::kPhoneHubRecentApps,
-                              features::kEcheCustomWidget},
+        /*enabled_features=*/{features::kEcheSWA, features::kEcheCustomWidget},
         /*disabled_features=*/{});
 
     DCHECK(test_web_view_factory_.get());
diff --git a/ash/webui/eche_app_ui/test/eche_app_ui_browsertest.js b/ash/webui/eche_app_ui/test/eche_app_ui_browsertest.js
index 3742aed..972be610 100644
--- a/ash/webui/eche_app_ui/test/eche_app_ui_browsertest.js
+++ b/ash/webui/eche_app_ui/test/eche_app_ui_browsertest.js
@@ -22,9 +22,7 @@
 
   /** @override */
   get featureList() {
-    return {
-      enabled: ['ash::features::kEcheSWA', 'ash::features::kPhoneHubRecentApps']
-    };
+    return {enabled: ['ash::features::kEcheSWA']};
   }
 };
 
diff --git a/ash/webui/multidevice_debug/BUILD.gn b/ash/webui/multidevice_debug/BUILD.gn
index 92c2a438..65a7ef8 100644
--- a/ash/webui/multidevice_debug/BUILD.gn
+++ b/ash/webui/multidevice_debug/BUILD.gn
@@ -18,6 +18,8 @@
 
   deps = [
     "//ash/constants",
+    "//ash/services/device_sync/proto",
+    "//ash/services/device_sync/proto:util",
     "//ash/services/multidevice_setup/public/mojom",
     "//ash/services/secure_channel/public/cpp/client",
     "//ash/services/secure_channel/public/mojom",
@@ -25,8 +27,6 @@
     "//base",
     "//base:i18n",
     "//chromeos/components/multidevice/logging",
-    "//chromeos/services/device_sync/proto",
-    "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp",
     "//components/prefs",
     "//components/resources",
diff --git a/ash/webui/multidevice_debug/DEPS b/ash/webui/multidevice_debug/DEPS
index ab6fb6d..4d3b396c 100644
--- a/ash/webui/multidevice_debug/DEPS
+++ b/ash/webui/multidevice_debug/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
+  "+ash/services/device_sync/proto",
   "+ash/services/multidevice_setup/public/mojom",
-  "+chromeos/services/device_sync/proto",
   "+chromeos/services/device_sync/public/cpp",
   "+device/bluetooth",
 ]
diff --git a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
index a8a448b7..3a9dd40 100644
--- a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
+++ b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
@@ -9,6 +9,7 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/i18n/time_formatting.h"
@@ -18,7 +19,6 @@
 #include "base/values.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui.h"
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
index 99ed417..bfa12427 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
@@ -33,7 +33,8 @@
     </toggle-row>
     <!-- TODO(b/202853013) placeholder string -->
     <ambient-preview>Image Preview</ambient-preview>
-    <topic-source-list selected-topic-source="[[topicSource_]]"
+    <topic-source-list hidden="[[!shouldShowMainSettings_(path)]]"
+        selected-topic-source="[[topicSource_]]"
         has-google-photos-albums="[[hasGooglePhotosAlbums_(albums_)]]">
     </topic-source-list>
     <ambient-weather-unit
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/topic_source_list_element.ts b/ash/webui/personalization_app/resources/trusted/ambient/topic_source_list_element.ts
index d126b51..941a665 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/topic_source_list_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/ambient/topic_source_list_element.ts
@@ -10,7 +10,7 @@
 import 'chrome://personalization/trusted/ambient/topic_source_item_element.js';
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 
-import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {TopicSource} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
@@ -26,6 +26,12 @@
 
   static get properties() {
     return {
+      hidden: {
+        type: Boolean,
+        reflectToAttribute: true,
+        observer: 'onHiddenChanged_',
+      },
+
       topicSources: {
         type: Array,
         value: [TopicSource.kGooglePhotos, TopicSource.kArtGallery],
@@ -37,10 +43,24 @@
     };
   }
 
+  hidden: boolean;
   topicSources: Array<TopicSource>;
   selectedTopicSource: TopicSource;
   hasGooglePhotosAlbums: boolean;
 
+  private onHiddenChanged_(hidden: boolean) {
+    if (hidden) {
+      return;
+    }
+
+    // When iron-list items change while their parent element is hidden, the
+    // iron-list will render incorrectly. Force relayout by invalidating the
+    // iron-list when this element becomes visible.
+    afterNextRender(this, () => {
+      this.shadowRoot!.querySelector('iron-list')!.fire('iron-resize');
+    });
+  }
+
   private isSelected_(
       topicSource: TopicSource, selectedTopicSource: TopicSource) {
     return selectedTopicSource === topicSource;
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.html b/ash/webui/personalization_app/resources/untrusted/collections_grid.html
index cf297fa..22078bb 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.html
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.html
@@ -52,7 +52,8 @@
           <div class$="[[getClassForImagesContainer_(item)]]">
             <template is="dom-repeat" items="[[item.preview]]" as="preview">
               <img is="cr-auto-img" auto-src="[[preview.url]]"
-                  aria-hidden="true" on-load="onImgLoad_" on-error="onImgLoad_">
+                  aria-hidden="true" on-load="onImgLoad_" on-error="onImgLoad_"
+                  with-cookies="[[isGooglePhotosTile_(item)]]">
             </template>
           </div>
           <div class="photo-text-container" hidden>
diff --git a/ash/wm/desks/templates/desks_templates_icon_container.cc b/ash/wm/desks/templates/desks_templates_icon_container.cc
index 4bdf472e..93e4075 100644
--- a/ash/wm/desks/templates/desks_templates_icon_container.cc
+++ b/ash/wm/desks/templates/desks_templates_icon_container.cc
@@ -30,11 +30,14 @@
 
 // Given a map of unique icon identifiers to icon info, returns a vector of the
 // same key, value pair ordered by icons' activation index.
-std::vector<IconIdentifierAndIconInfo> SortIconIdentifierToIconInfo(
-    std::map<std::string, IconInfo>& icon_identifier_to_icon_info) {
+std::vector<DesksTemplatesIconContainer::IconIdentifierAndIconInfo>
+SortIconIdentifierToIconInfo(
+    std::map<std::string, DesksTemplatesIconContainer::IconInfo>&
+        icon_identifier_to_icon_info) {
   // Create a vector using `sorted_icon_identifier_to_icon_info` that contains
   // pairs of identifiers and counts. This will initially be unsorted.
-  std::vector<IconIdentifierAndIconInfo> sorted_icon_identifier_to_icon_info;
+  std::vector<DesksTemplatesIconContainer::IconIdentifierAndIconInfo>
+      sorted_icon_identifier_to_icon_info;
 
   for (const auto& entry : icon_identifier_to_icon_info) {
     sorted_icon_identifier_to_icon_info.emplace_back(entry.first,
@@ -47,8 +50,10 @@
   std::sort(
       sorted_icon_identifier_to_icon_info.begin(),
       sorted_icon_identifier_to_icon_info.end(),
-      [&icon_identifier_to_icon_info](const IconIdentifierAndIconInfo& data_1,
-                                      const IconIdentifierAndIconInfo& data_2) {
+      [&icon_identifier_to_icon_info](
+          const DesksTemplatesIconContainer::IconIdentifierAndIconInfo& data_1,
+          const DesksTemplatesIconContainer::IconIdentifierAndIconInfo&
+              data_2) {
         return icon_identifier_to_icon_info.at(data_1.first).activation_index <
                icon_identifier_to_icon_info.at(data_2.first).activation_index;
       });
@@ -63,7 +68,8 @@
     const std::string& app_id,
     const std::string& identifier,
     int activation_index,
-    std::map<std::string, IconInfo>* out_icon_identifier_to_icon_info) {
+    std::map<std::string, DesksTemplatesIconContainer::IconInfo>*
+        out_icon_identifier_to_icon_info) {
   // A single app/site can have multiple windows so count their occurrences and
   // use the smallest activation index for sorting purposes.
   if (!base::Contains(*out_icon_identifier_to_icon_info, identifier)) {
@@ -82,7 +88,8 @@
 void InsertIconIdentifierToIconInfoFromLaunchList(
     const std::string& app_id,
     const app_restore::RestoreData::LaunchList& launch_list,
-    std::map<std::string, IconInfo>* out_icon_identifier_to_icon_info) {
+    std::map<std::string, DesksTemplatesIconContainer::IconInfo>*
+        out_icon_identifier_to_icon_info) {
   // We want to group active tabs and apps ahead of inactive tabs so offsets
   // inactive tabs activation index by `kInactiveTabOffset`. In almost every use
   // case, there should be no more than `kInactiveTabOffset` number of tabs +
@@ -184,7 +191,7 @@
 
     // Since there were no modifications to `app_id`, app id and icon identifier
     // are both `app_id`.
-    InsertIconIdentifierToIconInfo(/*app_id*/ app_id, /*identifier*/ app_id, i,
+    InsertIconIdentifierToIconInfo(/*app_id=*/app_id, /*identifier=*/app_id, i,
                                    &icon_identifier_to_icon_info);
   }
 
@@ -269,7 +276,7 @@
   // Set both `icon_identifier` and `app_id` to be empty strings for overflow
   // icon views, since only the count should matter.
   overflow_icon_view->SetIconIdentifierAndCount(
-      /*icon_identifier*/ std::string(), /*app_id*/ std::string(),
+      /*icon_identifier=*/std::string(), /*app_id=*/std::string(),
       icon_identifier_to_icon_info.size() - num_added_icons);
 }
 
diff --git a/ash/wm/desks/templates/desks_templates_icon_container.h b/ash/wm/desks/templates/desks_templates_icon_container.h
index de10e651..318ed73a 100644
--- a/ash/wm/desks/templates/desks_templates_icon_container.h
+++ b/ash/wm/desks/templates/desks_templates_icon_container.h
@@ -18,19 +18,9 @@
 
 namespace ash {
 
-// A struct for storing the various information used to determine which app
-// icons/favicons to display.
-struct IconInfo {
-  std::string app_id;
-  int activation_index;
-  int count;
-};
-
 class DeskTemplate;
 class DesksTemplatesIconView;
 
-using IconIdentifierAndIconInfo = std::pair<std::string, IconInfo>;
-
 // This class for determines which app icons/favicons to show for a desk
 // template and creates the according DesksTemplatesIconView's for them.
 // The last DesksTemplatesIconView in the layout is used for storing the
@@ -50,6 +40,16 @@
  public:
   METADATA_HEADER(DesksTemplatesIconContainer);
 
+  // A struct for storing the various information used to determine which app
+  // icons/favicons to display.
+  struct IconInfo {
+    std::string app_id;
+    int activation_index;
+    int count;
+  };
+
+  using IconIdentifierAndIconInfo = std::pair<std::string, IconInfo>;
+
   DesksTemplatesIconContainer();
   DesksTemplatesIconContainer(const DesksTemplatesIconContainer&) = delete;
   DesksTemplatesIconContainer& operator=(const DesksTemplatesIconContainer&) =
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 89ecd348..a9ec0f88 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1739,8 +1739,6 @@
   desks_templates_grid_widget_->Show();
 
   // Fade in the widget from its current opacity.
-  // TODO(crbug.com/1277160): Consider adding animate flag to determine whether
-  // to disable animations.
   PerformFadeInLayer(desks_templates_grid_widget_->GetLayer(),
                      /*animate=*/true);
 
@@ -1751,6 +1749,7 @@
                                         /*expanded_desks_bar_button=*/true);
   }
   desks_bar_view_->UpdateButtonsForDesksTemplatesGrid();
+  overview_session_->UpdateAccessibilityFocus();
 }
 
 void OverviewGrid::HideDesksTemplatesGrid(bool exit_overview) {
@@ -1796,6 +1795,7 @@
       /*animate=*/true,
       base::BindOnce(&OverviewGrid::OnDesksTemplatesGridFadedOut,
                      weak_ptr_factory_.GetWeakPtr()));
+  overview_session_->UpdateAccessibilityFocus();
 }
 
 bool OverviewGrid::IsShowingDesksTemplatesGrid() const {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 4aef923..2a7340b2 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220303.1.1
+7.20220303.2.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 4aef923..2a7340b2 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220303.1.1
+7.20220303.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 4aef923..2a7340b2 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220303.1.1
+7.20220303.2.1
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 3b07bf1..d597510 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -783,9 +783,12 @@
   helper.AlignMemory(alignof(SkScalar));
   helper.Write(op->x);
   helper.Write(op->y);
-  helper.Write(options.raw_draw);
+  int count = options.raw_draw ? op->slugs.size() : -1;
+  helper.Write(count);
   if (options.raw_draw) {
-    helper.Write(op->slug);
+    for (const auto& slug : op->slugs) {
+      helper.Write(slug);
+    }
   } else {
     helper.Write(op->blob);
   }
@@ -1353,10 +1356,13 @@
   deserializer.AlignMemory(alignof(SkScalar));
   deserializer.Read(&deserializer->x);
   deserializer.Read(&deserializer->y);
-  bool raw_draw = false;
-  deserializer.Read(&raw_draw);
-  if (raw_draw) {
-    deserializer.Read(&deserializer->slug);
+  int count = -1;
+  deserializer.Read(&count);
+  if (count >= 0) {
+    deserializer->slugs.resize(count);
+    for (auto& slug : deserializer->slugs) {
+      deserializer.Read(&slug);
+    }
   } else {
     deserializer.Read(&deserializer->blob);
   }
@@ -1808,17 +1814,24 @@
                                      const PlaybackParams& params) {
   if (op->node_id)
     SkPDF::SetNodeId(canvas, op->node_id);
-  flags->DrawToSk(canvas, [op, &params](SkCanvas* c, const SkPaint& p) {
+
+  bool analysis = params.raw_draw_analysis && op->slugs.empty();
+  size_t i = 0;
+  // flags may contain SkDrawLooper for shadow effect, so we need to convert
+  // SkTextBlob to slug for each run.
+  flags->DrawToSk(canvas, [op, analysis, &i](SkCanvas* c, const SkPaint& p) {
     if (op->blob) {
       c->drawTextBlob(op->blob.get(), op->x, op->y, p);
-      if (params.raw_draw_analysis) {
-        const_cast<DrawTextBlobOp*>(op)->slug =
-            GrSlug::ConvertBlob(c, *op->blob, {op->x, op->y}, p);
+      if (analysis) {
+        const_cast<DrawTextBlobOp*>(op)->slugs.push_back(
+            GrSlug::ConvertBlob(c, *op->blob, {op->x, op->y}, p));
       }
-    } else if (op->slug) {
-      op->slug->draw(c);
+    } else if (i < op->slugs.size() && op->slugs[i]) {
+      op->slugs[i]->draw(c);
+      ++i;
     }
   });
+
   if (op->node_id)
     SkPDF::SetNodeId(canvas, 0);
 }
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 3c7ded2b..80bf2c4 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -885,7 +885,7 @@
   HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<SkTextBlob> blob;
-  sk_sp<GrSlug> slug;
+  std::vector<sk_sp<GrSlug>> slugs;
   SkScalar x;
   SkScalar y;
   // This field isn't serialized.
diff --git a/chrome/VERSION b/chrome/VERSION
index 9e4e8744..27447f4 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=101
 MINOR=0
-BUILD=4923
+BUILD=4924
 PATCH=0
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java
deleted file mode 100644
index 7252d33..0000000
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.tasks.tab_management;
-
-import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.params.ParameterAnnotations;
-import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
-import org.chromium.base.test.params.ParameterSet;
-import org.chromium.base.test.params.ParameterizedRunner;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.tab_ui.R;
-import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
-import org.chromium.chrome.test.util.ChromeRenderTestRule;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.BlankUiTestActivityTestCase;
-import org.chromium.ui.test.util.NightModeTestUtils;
-import org.chromium.ui.test.util.UiRestriction;
-
-import java.io.IOException;
-import java.util.List;
-
-/** Render tests for PriceCardView. */
-@RunWith(ParameterizedRunner.class)
-@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
-public class PriceCardViewTest extends BlankUiTestActivityTestCase {
-    @ClassParameter
-    private static List<ParameterSet> sClassParams =
-            new NightModeTestUtils.NightModeParams().getParameters();
-
-    @Rule
-    public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
-
-    public PriceCardViewTest(boolean nightModeEnabled) {
-        NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
-        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
-    }
-
-    private PriceCardView mPriceCardView;
-
-    @Override
-    public void setUpTest() throws Exception {
-        super.setUpTest();
-
-        TabUiTestHelper.applyThemeOverlays(getActivity());
-        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ViewGroup view = new FrameLayout(getActivity());
-            getActivity().setContentView(view, params);
-
-            ViewGroup tabView = (ViewGroup) getActivity().getLayoutInflater().inflate(
-                    R.layout.closable_tab_grid_card_item, view, false);
-            tabView.setVisibility(View.VISIBLE);
-
-            view.addView(tabView);
-
-            mPriceCardView = tabView.findViewById(R.id.price_info_box_outer);
-        });
-    }
-
-    @Override
-    public void tearDownTest() throws Exception {
-        NightModeTestUtils.tearDownNightModeForBlankUiTestActivity();
-        super.tearDownTest();
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    public void testRenderPriceCardView() throws IOException {
-        mPriceCardView.setPriceStrings("$50", "$100");
-        mPriceCardView.setVisibility(View.VISIBLE);
-
-        CriteriaHelper.pollUiThread(() -> mPriceCardView.getVisibility() == View.VISIBLE);
-
-        mRenderTestRule.render(mPriceCardView, "price_card_view");
-    }
-}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index d8b5e2f3..ddd44d8 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -38,7 +38,6 @@
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardProviderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java",
-  "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingDialogTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingUtilitiesTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/RecyclerViewMatcherUtils.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
index ec586b3..2bd14bb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
@@ -6,12 +6,10 @@
 
 import android.Manifest.permission;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.download.DownloadCollectionBridge;
-import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.permissions.AndroidPermissionDelegate;
 import org.chromium.url.GURL;
@@ -167,15 +165,8 @@
                 new DownloadItem(true, info), true);
     }
 
-    /**
-     * Called when a download is started.
-     */
     @CalledByNative
-    private static void onDownloadStarted() {
-        if (!BrowserStartupController.getInstance().isFullBrowserStarted()) return;
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOAD_PROGRESS_INFOBAR)) return;
-        DownloadUtils.showDownloadStartToast(ContextUtils.getApplicationContext());
-    }
+    private static void onDownloadStarted() {}
 
     @NativeMethods
     interface Natives {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerImpl.java
index bc47194f..26e2cee8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerImpl.java
@@ -26,7 +26,6 @@
 import org.chromium.chrome.browser.download.dialogs.DownloadLaterDialogHelper;
 import org.chromium.chrome.browser.download.dialogs.DownloadLaterDialogHelper.Source;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.util.date.CalendarUtils;
@@ -408,8 +407,6 @@
      */
     private void computeNextStepForUpdate(OfflineItem updatedItem, boolean forceShowDownloadStarted,
             boolean userCancel, boolean itemWasRemoved) {
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOAD_PROGRESS_INFOBAR)) return;
-
         if (updatedItem != null && mIgnoredItems.contains(updatedItem.id)) return;
 
         preProcessUpdatedItem(updatedItem);
@@ -720,7 +717,6 @@
      */
     @VisibleForTesting
     protected void showMessage(@UiState int state, DownloadProgressMessageUiData info) {
-        assert ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOAD_PROGRESS_MESSAGE);
         if (mDelegate.maybeSwitchToFocusedActivity()) {
             closePreviousMessage();
         }
@@ -769,11 +765,6 @@
         mPropertyModel.set(MessageBannerProperties.ON_DISMISSED, this::onMessageDismissed);
         mPropertyModel.set(MessageBannerProperties.ON_PRIMARY_ACTION,
                 () -> onPrimaryAction(info.id, info.schedule));
-        if (getMessageDismissDurationMs() > 0) {
-            mPropertyModel.set(
-                    MessageBannerProperties.DISMISSAL_DURATION, getMessageDismissDurationMs());
-        }
-
         final MessageDispatcher dispatcher = getMessageDispatcher();
         mDismissRunnable = () -> {
             if (dispatcher == null) return;
@@ -950,13 +941,6 @@
                 });
     }
 
-    private long getMessageDismissDurationMs() {
-        return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
-                       ChromeFeatureList.DOWNLOAD_PROGRESS_MESSAGE,
-                       "message_dismiss_duration_seconds", -1)
-                * 1000;
-    }
-
     private static void recordMessageState(@UiState int state, DownloadProgressMessageUiData info) {
         int shownState = -1;
         int multipleDownloadState = -1;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentIntegrationTest.java
index d0eaea85..125ab78 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentIntegrationTest.java
@@ -7,6 +7,8 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 
+import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
+
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -42,6 +44,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
@@ -225,6 +228,7 @@
     @LargeTest
     @Feature({"ConversionMeasurement"})
     @Features.EnableFeatures(ChromeFeatureList.APP_TO_WEB_ATTRIBUTION)
+    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
     public void testConversionIntentEnabled_CCT_preload_sameUrl() {
         AttributionReporter.setInstanceForTesting(mAttributionReporter);
         CustomTabsConnection connection = CustomTabsTestUtils.setUpConnection();
@@ -255,6 +259,7 @@
     @LargeTest
     @Feature({"ConversionMeasurement"})
     @Features.EnableFeatures(ChromeFeatureList.APP_TO_WEB_ATTRIBUTION)
+    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
     public void testConversionIntentEnabled_CCT_preload_differentUrl() {
         AttributionReporter.setInstanceForTesting(mAttributionReporter);
         CustomTabsConnection connection = CustomTabsTestUtils.setUpConnection();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerTest.java
index 3def713..aeb3031b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMessageUiControllerTest.java
@@ -21,10 +21,8 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.messages.MessageDispatcher;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.OfflineItem;
@@ -41,7 +39,6 @@
  * state of the downloads in the current chrome session.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@Features.EnableFeatures(ChromeFeatureList.DOWNLOAD_PROGRESS_MESSAGE)
 @Batch(Batch.PER_CLASS)
 public class DownloadMessageUiControllerTest {
     @Rule
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index 4052859..1545a04 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -121,12 +121,31 @@
   <message name="IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_SYSTEM" desc="Text for System app type">
     System App
   </message>
+  <message name="IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM" desc="Text for Chrome OS System app type">
+    Chrome OS System App
+  </message>
+  <message name="IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_WEB_STORE" desc="Text for Chrome Web Store installation source">
+    Chrome Web Store
+  </message>
+  <message name="IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_PLAY_STORE" desc="Text for Google Play Store installation source">
+    Google Play Store
+  </message>
+  <message name="IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_BROWSER" desc="Text for Chrome browser installation source">
+    Chrome browser
+  </message>
+  <message name="IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_AND_SOURCE_COMBINED" desc="Full text for installation source and app type line">
+    <ph name="APP_TYPE">$1<ex>Web App</ex></ph> installed from <ph name="INSTALL_SOURCE">$2<ex>Chrome browser</ex></ph>
+  </message>
 
   <!-- File Handling -->
   <message name="IDS_APP_MANAGEMENT_FILE_HANDLING_HEADER" desc="Main text for toggling a web app's ability to use the File Handling API. This controls whether the app can appear in an 'Open With' list in a file's context menu.">
     Suggest app when opening files on your computer
   </message>
 
+  <message name="IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE" desc="Title for a dialog that shows all the supported file types (file extensions) that are handled by an app. Only shown when there are 5 or more file types.">
+    Supported file types
+  </message>
+
   <if expr="is_win">
     <message name="IDS_APP_MANAGEMENT_FILE_HANDLING_SET_DEFAULTS_LINK" desc="Further explanation of the File Handling API, including text describing the purpose of the toggle (referencing the Windows File Explorer) and a link to change Windows' default filetype associations.">
       You can open and edit supported files with this app from the File Explorer or other apps. To control which files open in this app by default, go to <ph name="BEGIN_LINK">&lt;a href="#"&gt;</ph>Windows settings<ph name="END_LINK">&lt;/a&gt;</ph>.
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_BROWSER.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_BROWSER.png.sha1
new file mode 100644
index 0000000..67b470a
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_BROWSER.png.sha1
@@ -0,0 +1 @@
+0c9b75e18bd17eedeebc8efb285dff38ae7ec10a
\ No newline at end of file
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_PLAY_STORE.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_PLAY_STORE.png.sha1
new file mode 100644
index 0000000..2afed2ff
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_PLAY_STORE.png.sha1
@@ -0,0 +1 @@
+904a17d7048e3154a5b69d4730f43b442d7350de
\ No newline at end of file
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_WEB_STORE.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_WEB_STORE.png.sha1
new file mode 100644
index 0000000..4851a5bd
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_WEB_STORE.png.sha1
@@ -0,0 +1 @@
+0e622c8054e2004a7c444357a09e13a37fb4c645
\ No newline at end of file
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_AND_SOURCE_COMBINED.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_AND_SOURCE_COMBINED.png.sha1
new file mode 100644
index 0000000..67b470a
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_AND_SOURCE_COMBINED.png.sha1
@@ -0,0 +1 @@
+0c9b75e18bd17eedeebc8efb285dff38ae7ec10a
\ No newline at end of file
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM.png.sha1
new file mode 100644
index 0000000..274388b
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM.png.sha1
@@ -0,0 +1 @@
+a17e42ff6783203a042048d6b828640731cf0343
\ No newline at end of file
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..306eeeb
--- /dev/null
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+7dac243a4f87230ccdd73bdd7a1b3f243af9712d
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 746238b..60ee582 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -319,6 +319,12 @@
   <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INVALID" desc="Label informing the user that the code used to install an eSIM profile is invalid.">
     Invalid code. Please try again.
   </message>
+  <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_SUBTITLE" desc="Subtitle for input informing the user that the code used to install an eSIM profile must follow a specific format.">
+    Your entry should have the format LPA:1$&lt;smdp address&gt;$&lt;activation code&gt;
+  </message>
+  <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_ERROR" desc="Error label for input informing the user that the code used to install an eSIM profile is invalid and must follow a specific format.">
+    Invalid code. Your entry should have the format LPA:1$&lt;smdp address&gt;$&lt;activation code&gt;
+  </message>
   <message name="IDS_CELLULAR_SETUP_ESTABLISH_NETWORK_CONNECTION" desc="Message, informing user that a network connection is being established during cellular setup">
     Establishing network connection ...
   </message>
@@ -4188,7 +4194,7 @@
   </message>
   <message name="IDS_HW_DATA_COLLECTION_CONTENT" desc="Content of the hardware data collection notice screen.">
     <ph name="BEGIN_PARAGRAPH1">&lt;p&gt;</ph>To provide the best experience, <ph name="DEVICE_OS">$1<ex>CloudReady 2.0</ex></ph> collects hardware data about devices and shares it with Google to determine which updates should be delivered. Optionally, you can allow Google to use this data for additional purposes like support and improvements to the <ph name="DEVICE_OS">$1<ex>CloudReady 2.0</ex></ph> experience and service.<ph name="END_PARAGRAPH1">&lt;/p&gt;</ph>
-    <ph name="BEGIN_PARAGRAPH2">&lt;p&gt;</ph>You can log in on this device and visit the CLOUDREADY_HARDWARE_INFO section in chrome://system to see the data sent to Google for update filtering, as well as any other instances where you choose to share data with Google.<ph name="END_PARAGRAPH2">&lt;/p&gt;</ph>
+    <ph name="BEGIN_PARAGRAPH2">&lt;p&gt;</ph>You can log in on this device and visit the CHROMEOSFLEX_HARDWARE_INFO section in chrome://system to see the data sent to Google for update filtering, as well as any other instances where you choose to share data with Google.<ph name="END_PARAGRAPH2">&lt;/p&gt;</ph>
     <ph name="BEGIN_PARAGRAPH3">&lt;p&gt;</ph>For more details on data <ph name="DEVICE_OS">$1<ex>CloudReady 2.0</ex></ph> may share with Google, and how it is used visit g.co/flex/HWDataCollection.<ph name="END_PARAGRAPH3">&lt;/p&gt;</ph>
   </message>
   <message name="IDS_HW_DATA_COLLECTION_USAGE_INFO" desc="Checkbox description for additional hardware data usage. This message is followed by a 'Learn more' link.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_ERROR.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_ERROR.png.sha1
new file mode 100644
index 0000000..589925a5
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_ERROR.png.sha1
@@ -0,0 +1 @@
+287e5e5412ffc53650d57ebe270d465788b59625
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_SUBTITLE.png.sha1
new file mode 100644
index 0000000..451a99f0
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+cc6ba64b78c992e9d5880984d6179cdd2fae033a
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_HW_DATA_COLLECTION_CONTENT.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_HW_DATA_COLLECTION_CONTENT.png.sha1
index 6dac3e2f..fa4da8f 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_HW_DATA_COLLECTION_CONTENT.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_HW_DATA_COLLECTION_CONTENT.png.sha1
@@ -1 +1 @@
-641b38d403637860d333d15522d37671d2dfeca8
\ No newline at end of file
+9b41853621279e4bfe8a91dd1995498f241786ab
\ No newline at end of file
diff --git a/chrome/app/extensions_strings.grdp b/chrome/app/extensions_strings.grdp
index 1fd3660..e3934a9 100644
--- a/chrome/app/extensions_strings.grdp
+++ b/chrome/app/extensions_strings.grdp
@@ -76,6 +76,12 @@
   <message name="IDS_EXTENSIONS_ERROR_PAGE_HEADING" desc="The heading of the page displaying an extension's errors.">
     Errors
   </message>
+  <message name="IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS" desc="The label for the dialog option to always allow extension to run on a site.">
+    Allow all extensions to read and change <ph name="PERMITTED_SITE">$1</ph>
+  </message>
+  <message name="IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS" desc="The label for the dialog option to prevent extensions from running on a site.">
+    Don't allow any extensions on <ph name="RESTRICTED_SITE">$1</ph>
+  </message>
   <message name="IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION" desc="The label indicating that an error was caused within an anonymous function in the code.">
     anonymous function
   </message>
@@ -403,6 +409,15 @@
   <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_SITE_DIALOG_TITLE" desc="The title of the dialog used to allow a user to edit an existing site. Used for the permitted and restricted site lists.">
     Edit site
   </message>
+  <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL" desc="The label for the option used to allow a user to edit a site's URL. Used in the site permissions page.">
+    Edit site URL
+  </message>
+  <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE" desc="The label for the option used to allow a user to edit a site's permissions. Used in the site permissions page.">
+    Edit site permissions for <ph name="SITE">$1</ph>
+  </message>
+  <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS" desc="The label for the option used to allow a user to edit a site's permissions. Used in the site permissions page.">
+    Edit site permissions
+  </message>
   <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_DIALOG_INPUT_ERROR" desc="The error message shown to the user when they entered an invalid site address into the input field.">
     Not a valid web address
   </message>
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS.png.sha1
new file mode 100644
index 0000000..c4b6927
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS.png.sha1
@@ -0,0 +1 @@
+db70326d7b4eb011c9b472d5b24d78a3170187b7
\ No newline at end of file
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS.png.sha1
new file mode 100644
index 0000000..c4b6927
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS.png.sha1
@@ -0,0 +1 @@
+db70326d7b4eb011c9b472d5b24d78a3170187b7
\ No newline at end of file
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS.png.sha1
new file mode 100644
index 0000000..a7d3535
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS.png.sha1
@@ -0,0 +1 @@
+ffb1f1bb0e6d7aaca1b12de744bb82290a130e21
\ No newline at end of file
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..a7d3535
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+ffb1f1bb0e6d7aaca1b12de744bb82290a130e21
\ No newline at end of file
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL.png.sha1
new file mode 100644
index 0000000..a7d3535
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL.png.sha1
@@ -0,0 +1 @@
+ffb1f1bb0e6d7aaca1b12de744bb82290a130e21
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 84d3f14d..f1ad996 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2386,7 +2386,6 @@
     "//ui/surface",
     "//ui/web_dialogs",
     "//ui/webui",
-    "//ui/webui/resources/cr_components/app_management:mojo_bindings",
     "//ui/webui/resources/cr_components/color_change_listener:mojom",
     "//ui/webui/resources/cr_components/customize_themes:mojom",
     "//ui/webui/resources/cr_components/most_visited:mojom",
@@ -4472,6 +4471,7 @@
       "//services/device/public/cpp/hid",
       "//third_party/sqlite",
       "//third_party/zxcvbn-cpp",
+      "//ui/webui/resources/cr_components/app_management:mojo_bindings",
     ]
     public_deps += [
       "//chrome/common:buildflags",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a7ccbf0..2f94d1a62 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2630,6 +2630,9 @@
 constexpr char kBorealisForceBetaClientInternalName[] =
     "borealis-force-beta-client";
 constexpr char kBorealisLinuxModeInternalName[] = "borealis-linux-mode";
+// This differs slightly from its symbol's name since "enabled" is used
+// internally to refer to whether borealis is installed or not.
+constexpr char kBorealisPermittedInternalName[] = "borealis-enabled";
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_ANDROID)
@@ -2731,25 +2734,28 @@
 // sample.
 
 const FeatureEntry::FeatureParam kSnoopingProtectionPrecision[] = {
-    {"SnoopingProtection_filter_config_case", "3"},
-    {"SnoopingProtection_average_window_size", "3"},
-    {"SnoopingProtection_positive_score_threshold", "-30"},
-    {"SnoopingProtection_negative_score_threshold", "-30"},
-    {"SnoopingProtection_default_uncertain_score", "-128"}};
+    {"SnoopingProtection_filter_config_case", "2"},
+    {"SnoopingProtection_positive_count_threshold", "1"},
+    {"SnoopingProtection_negative_count_threshold", "1"},
+    {"SnoopingProtection_uncertain_count_threshold", "1"},
+    {"SnoopingProtection_positive_score_threshold", "-50"},
+    {"SnoopingProtection_negative_score_threshold", "-50"}};
 
 const FeatureEntry::FeatureParam kSnoopingProtectionConfidence[] = {
-    {"SnoopingProtection_filter_config_case", "3"},
-    {"SnoopingProtection_average_window_size", "5"},
-    {"SnoopingProtection_positive_score_threshold", "-30"},
-    {"SnoopingProtection_negative_score_threshold", "-30"},
-    {"SnoopingProtection_default_uncertain_score", "-128"}};
+    {"SnoopingProtection_filter_config_case", "2"},
+    {"SnoopingProtection_positive_count_threshold", "2"},
+    {"SnoopingProtection_negative_count_threshold", "2"},
+    {"SnoopingProtection_uncertain_count_threshold", "2"},
+    {"SnoopingProtection_positive_score_threshold", "-50"},
+    {"SnoopingProtection_negative_score_threshold", "-50"}};
 
 const FeatureEntry::FeatureParam kSnoopingProtectionRecall[] = {
-    {"SnoopingProtection_filter_config_case", "3"},
-    {"SnoopingProtection_average_window_size", "3"},
+    {"SnoopingProtection_filter_config_case", "2"},
+    {"SnoopingProtection_positive_count_threshold", "1"},
+    {"SnoopingProtection_negative_count_threshold", "1"},
+    {"SnoopingProtection_uncertain_count_threshold", "1"},
     {"SnoopingProtection_positive_score_threshold", "-70"},
-    {"SnoopingProtection_negative_score_threshold", "-70"},
-    {"SnoopingProtection_default_uncertain_score", "-128"}};
+    {"SnoopingProtection_negative_score_threshold", "-70"}};
 
 const FeatureEntry::FeatureVariation kSnoopingProtectionVariations[] = {
     {"Precise", kSnoopingProtectionPrecision,
@@ -5389,11 +5395,6 @@
      flag_descriptions::kDownloadAutoResumptionNativeDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(download::features::kDownloadAutoResumptionNative)},
 
-    {"download-progress-message",
-     flag_descriptions::kDownloadProgressMessageName,
-     flag_descriptions::kDownloadProgressMessageDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kDownloadProgressMessage)},
-
     {"download-later", flag_descriptions::kDownloadLaterName,
      flag_descriptions::kDownloadLaterDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(download::features::kDownloadLater)},
@@ -7727,10 +7728,6 @@
     {"enable-phone-hub-camera-roll", flag_descriptions::kPhoneHubCameraRollName,
      flag_descriptions::kPhoneHubCameraRollDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kPhoneHubCameraRoll)},
-
-    {"enable-phone-hub-recent-apps", flag_descriptions::kPhoneHubRecentAppsName,
-     flag_descriptions::kPhoneHubRecentAppsDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kPhoneHubRecentApps)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     {"sameparty-cookies-considered-first-party",
@@ -7758,6 +7755,9 @@
     {kBorealisLinuxModeInternalName, flag_descriptions::kBorealisLinuxModeName,
      flag_descriptions::kBorealisLinuxModeDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kBorealisLinuxMode)},
+    {kBorealisPermittedInternalName, flag_descriptions::kBorealisPermittedName,
+     flag_descriptions::kBorealisPermittedDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kBorealisPermitted)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     {"https-only-mode-setting", flag_descriptions::kHttpsOnlyModeName,
@@ -8266,6 +8266,13 @@
      flag_descriptions::kReduceUserAgentMinorVersionDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kReduceUserAgentMinorVersion)},
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {"enable-variable-refresh-rate",
+     flag_descriptions::kEnableVariableRefreshRateName,
+     flag_descriptions::kEnableVariableRefreshRateDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kEnableVariableRefreshRate)},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
@@ -8372,6 +8379,10 @@
     return !base::FeatureList::IsEnabled(features::kBorealis);
   }
 
+  if (!strcmp(kBorealisPermittedInternalName, entry.internal_name)) {
+    return !base::FeatureList::IsEnabled(features::kBorealis);
+  }
+
   // Only show full screen preview flag if wallpaper flag is enabled.
   if (!strcmp(kWallpaperFullScreenPreviewInternalName, entry.internal_name))
     return !ash::features::IsWallpaperWebUIEnabled();
diff --git a/chrome/browser/ash/borealis/borealis_app_launcher.cc b/chrome/browser/ash/borealis/borealis_app_launcher.cc
index 4d0d82b..6bf896c 100644
--- a/chrome/browser/ash/borealis/borealis_app_launcher.cc
+++ b/chrome/browser/ash/borealis/borealis_app_launcher.cc
@@ -87,6 +87,12 @@
 void BorealisAppLauncher::Launch(std::string app_id,
                                  const std::vector<std::string>& args,
                                  OnLaunchedCallback callback) {
+  if (borealis::BorealisService::GetForProfile(profile_)
+          ->Features()
+          .MightBeAllowed() != BorealisFeatures::AllowStatus::kAllowed) {
+    std::move(callback).Run(LaunchResult::kError);
+    return;
+  }
   if (!borealis::BorealisService::GetForProfile(profile_)
            ->Features()
            .IsEnabled()) {
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index 7a06a44..8b095641 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -7,8 +7,10 @@
 #include <string>
 
 #include "ash/components/settings/cros_settings_names.h"
+#include "ash/constants/ash_features.h"
 #include "base/callback.h"
 #include "base/cpu.h"
+#include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
@@ -185,6 +187,9 @@
   if (c == version_info::Channel::STABLE || c == version_info::Channel::BETA)
     return AllowStatus::kBlockedOnBetaStable;
 
+  if (!base::FeatureList::IsEnabled(chromeos::features::kBorealisPermitted))
+    return AllowStatus::kBlockedByFlag;
+
   return AllowStatus::kAllowed;
 }
 
@@ -219,6 +224,9 @@
     case AllowStatus::kBlockedOnBetaStable:
       return os << "Your ChromeOS channel must be set to Dev or Canary "
                    "to run Borealis";
+    case AllowStatus::kBlockedByFlag:
+      return os << "Borealis is still being worked on. You must set the "
+                   "#borealis-enabled feature flag.";
     case AllowStatus::kUnsupportedModel:
       return os << "Borealis is not supported on this model hardware";
     case AllowStatus::kHardwareChecksFailed:
diff --git a/chrome/browser/ash/borealis/borealis_features.h b/chrome/browser/ash/borealis/borealis_features.h
index adbcf59..959c32a 100644
--- a/chrome/browser/ash/borealis/borealis_features.h
+++ b/chrome/browser/ash/borealis/borealis_features.h
@@ -29,6 +29,7 @@
     kVmPolicyBlocked,
     kUserPrefBlocked,
     kBlockedOnBetaStable,
+    kBlockedByFlag,
     kUnsupportedModel,
     kHardwareChecksFailed,
   };
diff --git a/chrome/browser/ash/borealis/borealis_features_unittest.cc b/chrome/browser/ash/borealis/borealis_features_unittest.cc
index edaf33cf..e98a7cb 100644
--- a/chrome/browser/ash/borealis/borealis_features_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_features_unittest.cc
@@ -28,11 +28,7 @@
   BorealisFeaturesTest()
       : user_manager_(new ash::FakeChromeUserManager()),
         scoped_user_manager_(base::WrapUnique(user_manager_)) {
-    features_.InitAndEnableFeature(features::kBorealis);
-    AccountId account_id =
-        AccountId::FromUserEmail(profile_.GetProfileUserName());
-    user_manager_->AddUserWithAffiliation(account_id, /*is_affiliated=*/false);
-    user_manager_->LoginUser(account_id);
+    AllowBorealis(&profile_, &features_, user_manager_, /*also_enable=*/false);
   }
 
  protected:
diff --git a/chrome/browser/ash/borealis/borealis_installer_impl.cc b/chrome/browser/ash/borealis/borealis_installer_impl.cc
index f2659157..40b515f 100644
--- a/chrome/browser/ash/borealis/borealis_installer_impl.cc
+++ b/chrome/browser/ash/borealis/borealis_installer_impl.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/vm_applications/apps.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
@@ -84,8 +85,10 @@
       return;
     }
     SetState(InstallingState::kInstallingDlc);
+    dlcservice::InstallRequest install_request;
+    install_request.set_id(kBorealisDlcName);
     chromeos::DlcserviceClient::Get()->Install(
-        kBorealisDlcName,
+        install_request,
         base::BindOnce(&Installation::OnDlcInstallationCompleted,
                        weak_factory_.GetWeakPtr()),
         base::BindRepeating(&Installation::OnDlcInstallationProgressUpdated,
diff --git a/chrome/browser/ash/borealis/borealis_task.cc b/chrome/browser/ash/borealis/borealis_task.cc
index d6a308d..c801412 100644
--- a/chrome/browser/ash/borealis/borealis_task.cc
+++ b/chrome/browser/ash/borealis/borealis_task.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace borealis {
@@ -84,8 +85,10 @@
 void MountDlc::RunInternal(BorealisContext* context) {
   // TODO(b/172279567): Ensure the DLC is present before trying to install,
   // otherwise we will silently download borealis here.
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kBorealisDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kBorealisDlcName,
+      install_request,
       base::BindOnce(&MountDlc::OnMountDlc, weak_factory_.GetWeakPtr(),
                      context),
       base::DoNothing());
diff --git a/chrome/browser/ash/borealis/testing/features.cc b/chrome/browser/ash/borealis/testing/features.cc
index 20e7b70..1a368c4 100644
--- a/chrome/browser/ash/borealis/testing/features.cc
+++ b/chrome/browser/ash/borealis/testing/features.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/borealis/testing/features.h"
 
+#include "ash/constants/ash_features.h"
 #include "chrome/browser/ash/borealis/borealis_prefs.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/common/chrome_features.h"
@@ -11,11 +12,12 @@
 
 namespace borealis {
 
-void AllowBorealis(TestingProfile* profile,
+void AllowBorealis(Profile* profile,
                    base::test::ScopedFeatureList* features,
                    ash::FakeChromeUserManager* user_manager,
                    bool also_enable) {
-  features->InitAndEnableFeature(features::kBorealis);
+  features->InitWithFeatures(
+      {features::kBorealis, chromeos::features::kBorealisPermitted}, {});
   AccountId account_id =
       AccountId::FromUserEmail(profile->GetProfileUserName());
   user_manager->AddUserWithAffiliation(account_id, /*is_affiliated=*/false);
@@ -24,8 +26,7 @@
                                   also_enable);
 }
 
-ScopedAllowBorealis::ScopedAllowBorealis(TestingProfile* profile,
-                                         bool also_enable)
+ScopedAllowBorealis::ScopedAllowBorealis(Profile* profile, bool also_enable)
     : profile_(profile),
       user_manager_(std::make_unique<ash::FakeChromeUserManager>()) {
   AllowBorealis(profile_, &features_,
diff --git a/chrome/browser/ash/borealis/testing/features.h b/chrome/browser/ash/borealis/testing/features.h
index a1c7f3a..a829382 100644
--- a/chrome/browser/ash/borealis/testing/features.h
+++ b/chrome/browser/ash/borealis/testing/features.h
@@ -12,22 +12,22 @@
 class FakeChromeUserManager;
 }
 
-class TestingProfile;
+class Profile;
 
 namespace borealis {
 
-void AllowBorealis(TestingProfile* profile,
+void AllowBorealis(Profile* profile,
                    base::test::ScopedFeatureList* features,
                    ash::FakeChromeUserManager* user_manager,
                    bool also_enable);
 
 class ScopedAllowBorealis {
  public:
-  ScopedAllowBorealis(TestingProfile* profile, bool also_enable);
+  ScopedAllowBorealis(Profile* profile, bool also_enable);
   ~ScopedAllowBorealis();
 
  private:
-  TestingProfile* profile_;
+  Profile* profile_;
   base::test::ScopedFeatureList features_;
   user_manager::ScopedUserManager user_manager_;
 };
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index f65db69..75cedbd 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -143,7 +143,8 @@
                                       user_id_hash);
     crosapi::browser_util::ClearProfileMigrationCompletedForUser(
         g_browser_process->local_state(), user_id_hash);
-    // TODO(ythjkt): Also clear move migration resume step here.
+    MoveMigrator::ClearResumeStepForUser(g_browser_process->local_state(),
+                                         user_id_hash);
     MoveMigrator::ClearResumeAttemptCountForUser(
         g_browser_process->local_state(), user_id_hash);
     return false;
diff --git a/chrome/browser/ash/crosapi/move_migrator.cc b/chrome/browser/ash/crosapi/move_migrator.cc
index da729e5..b9015a0b 100644
--- a/chrome/browser/ash/crosapi/move_migrator.cc
+++ b/chrome/browser/ash/crosapi/move_migrator.cc
@@ -178,6 +178,14 @@
 }
 
 // static
+void MoveMigrator::ClearResumeStepForUser(PrefService* local_state,
+                                          const std::string& user_id_hash) {
+  DictionaryPrefUpdate update(local_state, kMoveMigrationResumeStepPref);
+  base::Value* dict = update.Get();
+  dict->RemoveKey(user_id_hash);
+}
+
+// static
 MoveMigrator::PreMigrationCleanUpResult MoveMigrator::PreMigrationCleanUp(
     const base::FilePath& original_profile_dir) {
   LOG(WARNING) << "Running PreMigrationCleanUp()";
diff --git a/chrome/browser/ash/crosapi/move_migrator.h b/chrome/browser/ash/crosapi/move_migrator.h
index 20ecdb9..ba8b3753 100644
--- a/chrome/browser/ash/crosapi/move_migrator.h
+++ b/chrome/browser/ash/crosapi/move_migrator.h
@@ -101,6 +101,10 @@
   static void ClearResumeAttemptCountForUser(PrefService* local_state,
                                              const std::string& user_id_hash);
 
+  // Clears `ResumeStep` for user stored in `kMoveMigrationResumeStepPref`.
+  static void ClearResumeStepForUser(PrefService* local_state,
+                                     const std::string& user_id_hash);
+
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
  private:
diff --git a/chrome/browser/ash/crostini/termina_installer.cc b/chrome/browser/ash/crostini/termina_installer.cc
index 065c455..7a815a4 100644
--- a/chrome/browser/ash/crostini/termina_installer.cc
+++ b/chrome/browser/ash/crostini/termina_installer.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "content/public/browser/network_service_instance.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -101,8 +102,10 @@
 void TerminaInstaller::InstallDlc(
     base::OnceCallback<void(InstallResult)> callback,
     bool is_initial_install) {
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kCrostiniDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kCrostiniDlcName,
+      install_request,
       base::BindOnce(&TerminaInstaller::OnInstallDlc,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      is_initial_install),
diff --git a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
index 834f0f0..920c85f1 100644
--- a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
+++ b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
@@ -8,6 +8,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
 #include "base/linux_util.h"
@@ -24,7 +25,6 @@
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_constants.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/gcm_driver/instance_id/instance_id_profile_service.h"
diff --git a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h
index 483bae91..ba22246 100644
--- a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h
+++ b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h
@@ -7,13 +7,13 @@
 
 #include <list>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/system/sys_info.h"
 // TODO(https://crbug.com/1164001): move to forward declaration.
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
 #include "chromeos/services/device_sync/public/cpp/client_app_metadata_provider.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/cryptauth/gcm_device_info_provider_impl.h b/chrome/browser/ash/cryptauth/gcm_device_info_provider_impl.h
index b02f5a01..8374fd9 100644
--- a/chrome/browser/ash/cryptauth/gcm_device_info_provider_impl.h
+++ b/chrome/browser/ash/cryptauth/gcm_device_info_provider_impl.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_ASH_CRYPTAUTH_GCM_DEVICE_INFO_PROVIDER_IMPL_H_
 #define CHROME_BROWSER_ASH_CRYPTAUTH_GCM_DEVICE_INFO_PROVIDER_IMPL_H_
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/no_destructor.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index 0eea693..0313a68 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -33,20 +33,23 @@
   complete_callback_ = std::move(complete_callback);
 
   VLOG(1) << "Executing EXTRACT_ARCHIVE IO task";
-  // TODO(crbug.com/953256) Generalize for multiple files.
-  if (source_urls_.size() == 1) {
-    if (!chromeos::FileSystemBackend::CanHandleURL(source_urls_[0])) {
-      progress_.state = State::kError;
-    } else {
-      base::FilePath source_file = source_urls_[0].path();
-      if (chromeos::FileSystemBackend::CanHandleURL(parent_folder_)) {
-        base::FilePath destination_directory = parent_folder_.path();
-        unzip::Unzip(unzip::LaunchUnzipper(), source_file,
-                     destination_directory,
-                     base::BindOnce(&ExtractIOTask::ZipExtractCallback,
-                                    weak_ptr_factory_.GetWeakPtr()));
-      } else {
+  for (const auto& source_url : source_urls_) {
+    base::FilePath source_file = source_url.path();
+    if (source_file.MatchesExtension(".zip")) {
+      if (!chromeos::FileSystemBackend::CanHandleURL(source_url)) {
         progress_.state = State::kError;
+        // TODO(crbug.com/953256) Report progress error.
+      } else {
+        // TODO(crbug.com/953256) Perform this check only once.
+        if (chromeos::FileSystemBackend::CanHandleURL(parent_folder_)) {
+          base::FilePath destination_directory = parent_folder_.path();
+          unzip::Unzip(unzip::LaunchUnzipper(), source_file,
+                       destination_directory,
+                       base::BindOnce(&ExtractIOTask::ZipExtractCallback,
+                                      weak_ptr_factory_.GetWeakPtr()));
+        } else {
+          progress_.state = State::kError;
+        }
       }
     }
   }
diff --git a/chrome/browser/ash/input_method/OWNERS b/chrome/browser/ash/input_method/OWNERS
index 46f0ec88..8173caf 100644
--- a/chrome/browser/ash/input_method/OWNERS
+++ b/chrome/browser/ash/input_method/OWNERS
@@ -4,4 +4,3 @@
 shuchen@chromium.org
 yhanada@chromium.org
 jiwan@chromium.org
-myy@chromium.org
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
index 0b61cac..cbb894c 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
@@ -9,13 +9,13 @@
 #include <string>
 
 #include "ash/components/proximity_auth/screenlock_bridge.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "base/callback.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 // TODO(https://crbug.com/1164001): move to forward declaration
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
index a07525c..b9049529 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
@@ -14,6 +14,7 @@
 #include "ash/components/proximity_auth/fake_lock_handler.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
 #include "base/bind.h"
@@ -38,7 +39,6 @@
 #include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "components/account_id/account_id.h"
 #include "components/signin/public/identity_manager/account_info.h"
diff --git a/chrome/browser/ash/network_change_manager_client_browsertest.cc b/chrome/browser/ash/network_change_manager_client_browsertest.cc
index be5dfb2..a3b4f1b9 100644
--- a/chrome/browser/ash/network_change_manager_client_browsertest.cc
+++ b/chrome/browser/ash/network_change_manager_client_browsertest.cc
@@ -158,6 +158,7 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
+  IgnoreNetworkServiceCrashes();
   network_service_test->SimulateCrash();
 
   service_client()->AddService("wifi", "wifi", "wifi", shill::kTypeWifi,
diff --git a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
index 617c092..5b732a9 100644
--- a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
+++ b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
@@ -165,7 +165,6 @@
 void PhoneHubManagerFactory::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   MultideviceSetupStateUpdater::RegisterPrefs(registry);
-  CameraRollManagerImpl::RegisterPrefs(registry);
   MultideviceFeatureAccessManagerImpl::RegisterPrefs(registry);
   OnboardingUiTrackerImpl::RegisterPrefs(registry);
   ScreenLockManagerImpl::RegisterPrefs(registry);
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
index 840b6448..19d0478b 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/download/public/background_service/background_download_service.h"
 #include "components/download/public/background_service/download_metadata.h"
 #include "components/prefs/pref_service.h"
@@ -467,8 +468,10 @@
     return;
   }
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      "pita",
+      install_request,
       base::BindOnce(&PluginVmInstaller::OnDlcDownloadCompleted,
                      weak_ptr_factory_.GetWeakPtr()),
       base::BindRepeating(&PluginVmInstaller::OnDlcDownloadProgressUpdated,
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
index 030a41fd..768a97a 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
@@ -24,6 +24,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/prefs/pref_service.h"
@@ -416,8 +417,10 @@
     base::OnceCallback<void(bool default_vm_exists)> success_callback,
     base::OnceClosure error_callback) {
   LOG_FUNCTION_CALL();
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      "pita",
+      install_request,
       base::BindOnce(&PluginVmManagerImpl::OnInstallPluginVmDlc,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(success_callback), std::move(error_callback)),
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
index 052ccb80..61a0736 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
@@ -33,6 +33,7 @@
 
 namespace plugin_vm {
 
+const char kPitaDlc[] = "pita";
 const char kPluginVmShelfAppId[] = "lgjpclljbbmphhnalkeplcmnjpfmmaek";
 const char kPluginVmName[] = "PvmDefault";
 const char kChromeOSBaseDirectoryDisplayText[] = "Network \u203a ChromeOS";
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.h b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
index d3489ee..1f865c2 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.h
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
@@ -26,6 +26,9 @@
 
 class PluginVmPolicySubscription;
 
+// Name of the pita DLC.
+extern const char kPitaDlc[];
+
 // This is used by both the Plugin VM app and its installer.
 // Generated as crx_file::id_util::GenerateId("org.chromium.plugin_vm");
 extern const char kPluginVmShelfAppId[];
diff --git a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
index f519acf..df23034 100644
--- a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
@@ -24,8 +24,7 @@
  public:
   EcheAppIntegrationTest() {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{chromeos::features::kEcheSWA,
-                              chromeos::features::kPhoneHubRecentApps},
+        /*enabled_features=*/{chromeos::features::kEcheSWA},
         /*disabled_features=*/{});
   }
 
@@ -148,8 +147,7 @@
   EcheAppEnableResizingTest() {
     scoped_feature_list_.InitWithFeatures(
         /*enabled_features=*/{chromeos::features::kEcheSWA,
-                              chromeos::features::kEcheSWAResizing,
-                              chromeos::features::kPhoneHubRecentApps},
+                              chromeos::features::kEcheSWAResizing},
         /*disabled_features=*/{});
   }
 
diff --git a/chrome/browser/ash/web_applications/projector_app/untrusted_projector_ui_config.cc b/chrome/browser/ash/web_applications/projector_app/untrusted_projector_ui_config.cc
index 80feb480..67d3cc2f 100644
--- a/chrome/browser/ash/web_applications/projector_app/untrusted_projector_ui_config.cc
+++ b/chrome/browser/ash/web_applications/projector_app/untrusted_projector_ui_config.cc
@@ -21,6 +21,8 @@
   version_info::Channel channel = chrome::GetChannel();
   source->AddBoolean("isDevChannel", channel == version_info::Channel::DEV);
   source->AddBoolean("isDebugMode", ash::features::IsProjectorAppDebugMode());
+  source->AddBoolean("isExcludeTranscriptEnabled",
+                     ash::features::IsProjectorExcludeTranscriptEnabled());
 }
 
 UntrustedProjectorUIConfig::UntrustedProjectorUIConfig()
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
index 72175c2..067f402 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
@@ -787,15 +787,8 @@
   EXPECT_TRUE(called);
 }
 
-// TODO(https://crbug.com/1299762): Re-enable on non-mac platforms once flaky
-// timeouts are fixed.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_BlockLargeFiles BlockLargeFiles
-#else
-#define MAYBE_BlockLargeFiles DISABLED_BlockLargeFiles
-#endif
 IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
-                       MAYBE_BlockLargeFiles) {
+                       BlockLargeFiles) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
   // Set up delegate and upload service.
@@ -828,9 +821,10 @@
   CreateFilesForTest({"large.doc"}, {std::string()}, &data);
 
   // Write data to the file in chunks to avoid memory allocation errors.
-  constexpr int64_t kLargeSize = 3ll * 1024ll * 1024ll * 1024ll;
+  constexpr int64_t kChunkSize = 50 * 1024 * 1024;  // 100 MB
+  constexpr int64_t kLargeSize = 42 * kChunkSize;   // ~2.1 GB, just over maxint
   int64_t total_size = 0;
-  std::string chunk = std::string(48 * 1024 * 1024, 'a');
+  std::string chunk = std::string(kChunkSize, 'a');
   base::File file(created_file_paths()[0],
                   base::File::FLAG_OPEN | base::File::FLAG_WRITE);
   while (total_size != kLargeSize) {
@@ -845,10 +839,10 @@
   validator.ExpectUnscannedFileEvent(
       /*url*/ "about:blank",
       /*filename*/ created_file_paths()[0].AsUTF8Unsafe(),
-      // python3 -c "print('a' * (3 * 1024 * 1024 * 1024), end='')" |\
+      // python3 -c "print('a' * (42 * 50 * 1024 * 1024), end='')" |\
       // sha256sum |  tr '[:lower:]' '[:upper:]'
       /*sha*/
-      "EFC8F27580ADA5D86CFC4451A10A142364DE63A6D70F778F1AC47B284F9D50AA",
+      "E061612733D5D991F3BD676A51F77B1F0C824282909B7C1C89BD1612FC52E073",
       /*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
       /*reason*/ "FILE_TOO_LARGE",
       /*mimetypes*/ DocMimeTypes(),
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
index 90bd57d..a05077b 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
@@ -37,7 +37,6 @@
     ]
     deps += [
       "//chrome/browser/enterprise/connectors/device_trust/key_management/common:constants",
-      "//mojo/core:shared_library",
       "//services/device/public/mojom",
       "//services/network/public/cpp",
       "//services/network/public/mojom",
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
index 493ef74..5cdc52f 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
@@ -24,7 +24,6 @@
     sources = [ "linux_key_network_delegate.cc" ]
 
     deps += [
-      "//mojo/core:shared_library",
       "//mojo/public/cpp/bindings",
       "//mojo/public/cpp/platform",
       "//services/network/public/cpp",
diff --git a/chrome/browser/enterprise/connectors/file_system/OWNERS b/chrome/browser/enterprise/connectors/file_system/OWNERS
index bb703a0..258354a 100644
--- a/chrome/browser/enterprise/connectors/file_system/OWNERS
+++ b/chrome/browser/enterprise/connectors/file_system/OWNERS
@@ -1,2 +1 @@
 rogerta@chromium.org
-alicego@google.com
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 344f5fd1e..39abb5e 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -621,7 +621,7 @@
   {
     "name": "borealis-big-gl",
     "owners": [ "danielng" ],
-    "expiry_milestone": 103
+    "expiry_milestone": 110
   },
   {
     "name": "borealis-disk-management",
@@ -629,14 +629,19 @@
     "expiry_milestone": 103
   },
   {
+    "name": "borealis-enabled",
+    "owners": [ "hollingum" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "borealis-force-beta-client",
     "owners": [ "hollingum" ],
-    "expiry_milestone": 103
+    "expiry_milestone": 110
   },
   {
     "name": "borealis-linux-mode",
     "owners": [ "hollingum" ],
-    "expiry_milestone": 103
+    "expiry_milestone": 110
   },
   {
     "name": "bruschetta",
@@ -1396,11 +1401,6 @@
     "expiry_milestone": 102
   },
   {
-    "name": "download-progress-message",
-    "owners": [ "shaktisahu" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "download-range",
     "owners": [ "xingliu" ],
     "expiry_milestone": 105
@@ -2439,12 +2439,12 @@
   {
     "name": "enable-migrate-default-chrome-app-to-web-apps-gsuite",
     "owners": [ "alancutter", "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 104
   },
   {
     "name": "enable-migrate-default-chrome-app-to-web-apps-non-gsuite",
     "owners": [ "alancutter", "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 104
   },
   {
     "name": "enable-mixed-content-download-dialog",
@@ -2618,11 +2618,6 @@
     "expiry_milestone": 106
   },
   {
-    "name": "enable-phone-hub-recent-apps",
-    "owners": [ "dhnishi", "cros-recentapps@google.com" ],
-    "expiry_milestone": 101
-  },
-  {
     "name": "enable-pixel-canvas-recording",
     "owners": [ "malaykeshav", "oshima" ],
     "expiry_milestone": 95
@@ -3005,6 +3000,11 @@
     "expiry_milestone": 100
   },
   {
+    "name": "enable-variable-refresh-rate",
+    "owners": [ "aswolfers", "chromeos-gfx-compositor@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "enable-virtual-keyboard",
     "owners": [ "//ash/keyboard/OWNERS" ],
     // Useful for debugging the virtual keyboard on non-tablet devices.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index be2deff..9116fdb 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -506,6 +506,15 @@
 const char kBorealisLinuxModeDescription[] =
     "Do not run ChromeOS-specific code in the client.";
 
+// For UX reasons we prefer "enabled", but that is used internally to refer to
+// whether borealis is installed or not, so the name of the variable is a bit
+// different to the user-facing name.
+const char kBorealisPermittedName[] = "Borealis Enabled";
+const char kBorealisPermittedDescription[] =
+    "Allows Borealis to run on your device. Borealis may still be blocked for "
+    "other reasons, including: administrator settings, device hardware "
+    "capabilities, or other security measures.";
+
 const char kBypassAppBannerEngagementChecksName[] =
     "Bypass user engagement checks";
 const char kBypassAppBannerEngagementChecksDescription[] =
@@ -1046,10 +1055,6 @@
 const char kDownloadLaterDebugOnWifiNameDescription[] =
     "Show download later dialog on WIFI.";
 
-const char kDownloadProgressMessageName[] = "Show download progress message";
-const char kDownloadProgressMessageDescription[] =
-    "Shows download progress message UI.";
-
 const char kDownloadRangeName[] = "Enable download range support";
 const char kDownloadRangeDescription[] =
     "Enables arbitrary download range request support.";
@@ -4274,10 +4279,10 @@
     "When enabled, newly installed apps will not capture links clicked in the "
     "browser.";
 
-const char kDesksTemplatesName[] = "Desks Templates";
+const char kDesksTemplatesName[] = "Desk Templates";
 const char kDesksTemplatesDescription[] =
     "Streamline workflows by saving a group of applications and windows as a "
-    "launchable template.";
+    "launchable template in a new desk";
 
 const char kDesksTrackpadSwipeImprovementsName[] =
     "Experiment: Trackpad swiping to switch desks.";
@@ -4696,6 +4701,11 @@
 const char kEnablePalmSuppressionDescription[] =
     "If enabled, suppresses touch when a stylus is on a touchscreen.";
 
+const char kEnableVariableRefreshRateName[] = "Enable Variable Refresh Rate";
+const char kEnableVariableRefreshRateDescription[] =
+    "Enable the variable refresh rate (Adaptive Sync) setting for capable "
+    "displays.";
+
 const char kDisableQuickAnswersV2TranslationName[] =
     "Disable Quick Answers Translation";
 const char kDisableQuickAnswersV2TranslationDescription[] =
@@ -5208,11 +5218,6 @@
     "Enables the Camera Roll feature in Phone Hub, which allows users to "
     "access recent photos and videos taken on a connected Android device.";
 
-const char kPhoneHubRecentAppsName[] = "Recent Apps in Phone Hub";
-const char kPhoneHubRecentAppsDescription[] =
-    "Enables the Recent Apps feature in Phone Hub, which allows users to "
-    "relaunch a recently streamed app.";
-
 const char kProductivityLauncherName[] =
     "Productivity experiment: App Launcher";
 const char kProductivityLauncherDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index c87cced..f9614164 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -283,6 +283,9 @@
 extern const char kBorealisLinuxModeName[];
 extern const char kBorealisLinuxModeDescription[];
 
+extern const char kBorealisPermittedName[];
+extern const char kBorealisPermittedDescription[];
+
 extern const char kBypassAppBannerEngagementChecksName[];
 extern const char kBypassAppBannerEngagementChecksDescription[];
 
@@ -648,9 +651,6 @@
 extern const char kDownloadLaterDebugOnWifiName[];
 extern const char kDownloadLaterDebugOnWifiNameDescription[];
 
-extern const char kDownloadProgressMessageName[];
-extern const char kDownloadProgressMessageDescription[];
-
 extern const char kDownloadRangeName[];
 extern const char kDownloadRangeDescription[];
 
@@ -2742,6 +2742,9 @@
 extern const char kEnableSuggestedLocalFilesName[];
 extern const char kEnableSuggestedLocalFilesDescription[];
 
+extern const char kEnableVariableRefreshRateName[];
+extern const char kEnableVariableRefreshRateDescription[];
+
 extern const char kEnableWireGuardName[];
 extern const char kEnableWireGuardDescription[];
 
@@ -2999,9 +3002,6 @@
 extern const char kPhoneHubCameraRollName[];
 extern const char kPhoneHubCameraRollDescription[];
 
-extern const char kPhoneHubRecentAppsName[];
-extern const char kPhoneHubRecentAppsDescription[];
-
 extern const char kProductivityLauncherName[];
 extern const char kProductivityLauncherDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 3a029ef..9196b8f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -220,8 +220,6 @@
     &kDisableCompositedProgressBar,
     &kDownloadFileProvider,
     &kDownloadNotificationBadge,
-    &kDownloadProgressInfoBar,
-    &kDownloadProgressMessage,
     &kDownloadRename,
     &kDuetTabStripIntegrationAndroid,
     &kDynamicColorAndroid,
@@ -611,12 +609,6 @@
 extern const base::Feature kDownloadHomeForExternalApp{
     "DownloadHomeForExternalApp", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kDownloadProgressInfoBar{"DownloadProgressInfoBar",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kDownloadProgressMessage{"DownloadProgressMessage",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kDownloadFileProvider{"DownloadFileProvider",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 473f902..93e2616 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -87,8 +87,6 @@
 extern const base::Feature kDownloadFileProvider;
 extern const base::Feature kDownloadHomeForExternalApp;
 extern const base::Feature kDownloadNotificationBadge;
-extern const base::Feature kDownloadProgressInfoBar;
-extern const base::Feature kDownloadProgressMessage;
 extern const base::Feature kDownloadRename;
 extern const base::Feature kDuetTabStripIntegrationAndroid;
 extern const base::Feature kDynamicColorAndroid;
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 fb8caca..b7c2d19 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
@@ -317,8 +317,6 @@
     public static final String DNS_OVER_HTTPS = "DnsOverHttps";
     public static final String DOWNLOAD_FILE_PROVIDER = "DownloadFileProvider";
     public static final String DOWNLOAD_NOTIFICATION_BADGE = "DownloadNotificationBadge";
-    public static final String DOWNLOAD_PROGRESS_INFOBAR = "DownloadProgressInfoBar";
-    public static final String DOWNLOAD_PROGRESS_MESSAGE = "DownloadProgressMessage";
     public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground";
     public static final String DOWNLOADS_AUTO_RESUMPTION_NATIVE = "DownloadsAutoResumptionNative";
     public static final String DOWNLOAD_OFFLINE_CONTENT_PROVIDER =
diff --git a/chrome/browser/media/OWNERS b/chrome/browser/media/OWNERS
index a33cf7e..b3ff4290 100644
--- a/chrome/browser/media/OWNERS
+++ b/chrome/browser/media/OWNERS
@@ -6,6 +6,9 @@
 per-file cast_*=mfoltz@chromium.org
 per-file cast_*=jophba@chromium.org
 
+# For Media Engagement changes.
+per-file *media_engagement*=evliu@google.com
+
 # For IPC security review
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.cc b/chrome/browser/metrics/power/power_metrics_reporter.cc
index bb5a0a74..625f288 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter.cc
@@ -11,6 +11,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h"
@@ -317,6 +318,33 @@
         coalition_resource_usage_rate->cpu_time_per_second, kMaxCPUProportion);
   }
 }
+
+void PowerMetricsReporter::MaybeEmitHighCPUTraceEvent(
+    const UsageScenarioDataStore::IntervalData& short_interval_data,
+    absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate) {
+  if (!coalition_resource_usage_rate.has_value())
+    return;
+  // A trace event is emitted when CPU usage exceeds the 95th percentile.
+  // 7 day aggregation ending on February 22nd 2022 from "PerformanceMonitor
+  // .ResourceCoalition.CPUTime2_10sec.AllTabsHidden_NoVideoCaptureOrAudio"
+  constexpr double kHighCPUUsageThreshold_AllTabsHidden = 0.1433;
+
+  if (short_interval_data.max_visible_window_count == 0 &&
+      short_interval_data.time_playing_audio.is_zero() &&
+      short_interval_data.time_capturing_video.is_zero() &&
+      coalition_resource_usage_rate->cpu_time_per_second >=
+          kHighCPUUsageThreshold_AllTabsHidden) {
+    const base::TimeTicks now = base::TimeTicks::Now();
+    constexpr char kEventTitle[] = "High CPU - All tabs Hidden";
+
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+        "browser", kEventTitle, TRACE_ID_LOCAL(this),
+        short_interval_begin_time_);
+    TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("browser", kEventTitle,
+                                                   TRACE_ID_LOCAL(this), now);
+  }
+  short_interval_begin_time_ = base::TimeTicks();
+}
 #endif  // BUILDFLAG(IS_MAC)
 
 void PowerMetricsReporter::ReportBatteryHistograms(
@@ -419,6 +447,8 @@
       short_usage_scenario_data_store_->ResetIntervalData();
   ReportShortIntervalHistograms(short_interval_data, long_interval_data,
                                 short_interval_resource_usage_rate);
+  MaybeEmitHighCPUTraceEvent(short_interval_data,
+                             short_interval_resource_usage_rate);
 #endif  // BUILDFLAG(IS_MAC)
 
   if (on_battery_sampled_for_testing_)
@@ -655,6 +685,7 @@
 
 #if BUILDFLAG(IS_MAC)
 void PowerMetricsReporter::OnShortIntervalBegin() {
+  short_interval_begin_time_ = base::TimeTicks::Now();
   short_usage_scenario_data_store_->ResetIntervalData();
   coalition_resource_usage_provider_->StartShortInterval();
 }
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.h b/chrome/browser/metrics/power/power_metrics_reporter.h
index 53e3b2f3..5222a938 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.h
+++ b/chrome/browser/metrics/power/power_metrics_reporter.h
@@ -52,6 +52,11 @@
 
   static constexpr base::TimeDelta kShortIntervalDuration = base::Seconds(10);
 
+  // Used to calculate the duration of a short interval. Set from at the
+  // beginning of a short interval (OnShortIntervalBegin()) and reset at the end
+  // (ReportShortIntervalHistograms()).
+  base::TimeTicks short_interval_begin_time_;
+
   // Use the default arguments in production. In tests, use arguments to provide
   // mocks. |(short|long)_usage_scenario_data_store| are queried to determine
   // the scenario for short and long reporting intervals. They must outlive the
@@ -128,6 +133,11 @@
       const UsageScenarioDataStore::IntervalData& short_interval_data,
       const UsageScenarioDataStore::IntervalData& long_interval_data,
       absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate);
+
+  // Emit trace event when CPU usage is high for 10 secondes or more.
+  void MaybeEmitHighCPUTraceEvent(
+      const UsageScenarioDataStore::IntervalData& short_interval_data,
+      absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate);
 #endif  // BUILDFLAG(IS_MAC)
 
   // Report battery metrics to histograms with |suffixes|.
diff --git a/chrome/browser/navigation_predictor/OWNERS b/chrome/browser/navigation_predictor/OWNERS
index 2783dea..47071b4 100644
--- a/chrome/browser/navigation_predictor/OWNERS
+++ b/chrome/browser/navigation_predictor/OWNERS
@@ -1 +1,2 @@
 file://components/data_reduction_proxy/OWNERS
+spelchat@chromium.org
diff --git a/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.cc b/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.cc
index 72bbed5..1d73e38 100644
--- a/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.cc
+++ b/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.cc
@@ -19,7 +19,6 @@
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/reporting/util/status.h"
-#include "components/reporting/util/statusor.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -36,9 +35,13 @@
 namespace reporting {
 namespace {
 
-// policy::CloudPolicyClient is retrieved in two different ways for ChromeOS and
-// non-ChromeOS browsers. This function should be called on the UI thread, and
-// if it isn't will recall itself to do so.
+// The |policy::CloudPolicyClient| object is retrieved in two different ways for
+// ChromeOS and non-ChromeOS browsers. This function should be called on the UI
+// thread, and if it isn't, it recalls itself to do so.
+//
+// This functions applies |get_client_cb| to the retrieved
+// |policy::CloudPolicyClient| object.
+//
 // TODO(chromium:1078512) Wrap CloudPolicyClient in a new object so that its
 // methods and retrieval are accessed on the correct thread.
 void GetCloudPolicyClient(CloudPolicyClientResultCb get_client_cb) {
diff --git a/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.h b/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.h
index 60f1dbe..aca58fa 100644
--- a/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.h
+++ b/chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.h
@@ -17,6 +17,9 @@
 using GetCloudPolicyClientCallback =
     base::RepeatingCallback<void(CloudPolicyClientResultCb)>;
 
+// Returns the address of the function, |GetCloudPolicyClient|, which retrieves
+// and operates on the |policy::CloudPolicyClient| object. See
+// |GetCloudPolicyClient| for details.
 GetCloudPolicyClientCallback GetCloudPolicyClientCb();
 
 }  // namespace reporting
diff --git a/chrome/browser/prefetch/prefetch_proxy/OWNERS b/chrome/browser/prefetch/prefetch_proxy/OWNERS
index 12f637f..c2d0a486 100644
--- a/chrome/browser/prefetch/prefetch_proxy/OWNERS
+++ b/chrome/browser/prefetch/prefetch_proxy/OWNERS
@@ -1 +1,2 @@
-robertogden@chromium.org
+curranmax@chromium.org
+spelchat@chromium.org
diff --git a/chrome/browser/resources/access_code_cast/access_code_cast.html b/chrome/browser/resources/access_code_cast/access_code_cast.html
index 7588a35d..91afd54 100644
--- a/chrome/browser/resources/access_code_cast/access_code_cast.html
+++ b/chrome/browser/resources/access_code_cast/access_code_cast.html
@@ -34,6 +34,11 @@
     background-color: transparent;
   }
 
+  a[href] {
+    color: var(--cr-link-color);
+    text-decoration: none;
+  }
+
   cr-dialog::part(dialog) {
     border-radius: 0;
     height: 100%;
@@ -48,7 +53,10 @@
   <div slot="title" class="title-1">$i18n{dialogTitle}</div>
   <div slot="body">
     <div id="codeInputView">
-      <div class="body-1">$i18n{accessCodeMessage}</div>
+      <div class="body-1">
+        $i18n{accessCodeMessage}
+        <a href="$i18n{learnMoreUrl}" target="_blank">$i18n{learnMore}</a>
+      </div>
       <div class="space-2"></div>
       <div class="center-content">
         <c2c-passcode-input
diff --git a/chrome/browser/resources/access_code_cast/access_code_cast.ts b/chrome/browser/resources/access_code_cast/access_code_cast.ts
index 35710b0..54ff8e3 100644
--- a/chrome/browser/resources/access_code_cast/access_code_cast.ts
+++ b/chrome/browser/resources/access_code_cast/access_code_cast.ts
@@ -58,7 +58,12 @@
       accessCode: {
         type: String,
         value: '',
-        observer: 'accessCodeChange'
+        observer: 'castStateChange'
+      },
+      canCast: {
+        type: Boolean,
+        value: true,
+        observer: 'castStateChange'
       }
     };
   }
@@ -78,7 +83,6 @@
     super();
     this.listenerIds = [];
     this.router = BrowserProxy.getInstance().callbackRouter;
-    this.canCast = true;
     this.inputLabel = this.i18n('inputLabel');
 
     this.accessCode = '';
@@ -136,7 +140,7 @@
       return;
     }
 
-    this.canCast = false;
+    this.set('canCast', false);
     this.$.errorMessage.setNoError();
 
     const method = this.state === PageState.CODE_INPUT ? 
@@ -148,7 +152,7 @@
 
     if (addResult !== AddSinkResultCode.OK) {
       this.$.errorMessage.setAddSinkError(addResult);
-      this.canCast = true;
+      this.set('canCast', true);
       return;
     }
 
@@ -158,7 +162,7 @@
 
     if (castResult !== RouteRequestResultCode.OK) {
       this.$.errorMessage.setCastError(castResult);
-      this.canCast = true;
+      this.set('canCast', true);
       return;
     }
 
@@ -169,7 +173,7 @@
     this.accessCode = value;
   }
 
-  private accessCodeChange() {
+  private castStateChange() {
     this.submitDisabled = !this.canCast ||
         this.accessCode.length !== AccessCodeCastElement.ACCESS_CODE_LENGTH;
   }
diff --git a/chrome/browser/resources/access_code_cast/error_message/error_message.html b/chrome/browser/resources/access_code_cast/error_message/error_message.html
index 59b0515..cb4934a 100644
--- a/chrome/browser/resources/access_code_cast/error_message/error_message.html
+++ b/chrome/browser/resources/access_code_cast/error_message/error_message.html
@@ -1,6 +1,16 @@
 <style include="cr-shared-style">
+  :host {
+    --error-message-color: var(--google-red-600);
+  }
+
+  @media (prefers-color-scheme: dark) {
+    :host {
+      --error-message-color: var(--google-red-300);
+    }
+  }
+
   .error {
-    color: var(--google-red-600);
+    color: var(--error-message-color);
     text-align: center;
   }
 </style>
diff --git a/chrome/browser/resources/app_settings/app.html b/chrome/browser/resources/app_settings/app.html
index 367769f..1a04d98 100644
--- a/chrome/browser/resources/app_settings/app.html
+++ b/chrome/browser/resources/app_settings/app.html
@@ -81,8 +81,6 @@
     </div>
     <app-management-file-handling-item
         class="permission-card-row separated-row"
-        file-handling-header="$i18n{appManagementFileHandlingHeader}"
-        file-handling-set-defaults="$i18n{fileHandlingSetDefaults}"
         app="[[app_]]">
     </app-management-file-handling-item>
     <app-management-more-permissions-item
diff --git a/chrome/browser/resources/app_settings/app.ts b/chrome/browser/resources/app_settings/app.ts
index c42ce81..6de81db 100644
--- a/chrome/browser/resources/app_settings/app.ts
+++ b/chrome/browser/resources/app_settings/app.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import './strings.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 import 'chrome://resources/cr_components/app_management/file_handling_item.js';
 import 'chrome://resources/cr_components/app_management/more_permissions_item.js';
diff --git a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
index 4d149e2..484a605 100644
--- a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
+++ b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
@@ -84,7 +84,12 @@
 
   const deviceDetailsPage =
       /** @type {!DeviceDetailsPage} */ (pageManager.registeredPages.get(id));
-  assert(deviceDetailsPage, 'Device Details page must exist');
+
+  // The device details page does not necessarily exist, return early if it is
+  // not found.
+  if (!deviceDetailsPage) {
+    return;
+  }
 
   deviceDetailsPage.disconnect();
   deviceDetailsPage.pageDiv.parentNode.removeChild(deviceDetailsPage.pageDiv);
diff --git a/chrome/browser/resources/bluetooth_internals/sidebar.js b/chrome/browser/resources/bluetooth_internals/sidebar.js
index 3a5f67aa..0b5a325e 100644
--- a/chrome/browser/resources/bluetooth_internals/sidebar.js
+++ b/chrome/browser/resources/bluetooth_internals/sidebar.js
@@ -85,7 +85,13 @@
   removeItem(pageName) {
     pageName = pageName.toLowerCase();
     const query = 'li[data-page-name="' + pageName + '"]';
-    this.sidebarList_.removeChild(this.sidebarList_.querySelector(query));
+    const selection = this.sidebarList_.querySelector(query);
+
+    // Devices are only added to the sidebar when the user pressed "Inspect" on
+    // them in the main table. Only try to remove the element if it exists.
+    if (selection) {
+      this.sidebarList_.removeChild(selection);
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index d3462bb..b598754 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -40,7 +40,7 @@
   "background/color.js",
   "background/command_handler_interface.js",
   "background/custom_automation_event.js",
-  "background/desktop_automation_handler.js",
+  "background/desktop_automation_interface.js",
   "background/editing/editable_line.js",
   "background/editing/editing.js",
   "background/editing/intent_handler.js",
@@ -67,7 +67,6 @@
   "background/panel_command.js",
   "background/phonetic_data.js",
   "background/prefs.js",
-  "background/smart_sticky_mode.js",
   "background/user_action_monitor.js",
   "braille/bluetooth_braille_display_manager.js",
   "braille/bluetooth_braille_display_ui.js",
@@ -120,6 +119,7 @@
   "background/background.js",
   "background/braille_command_handler.js",
   "background/command_handler.js",
+  "background/desktop_automation_handler.js",
   "background/download_handler.js",
   "background/earcon_engine.js",
   "background/earcons.js",
@@ -132,6 +132,7 @@
   "background/range_automation_handler.js",
   "background/page_load_sound_handler.js",
   "background/pointer_handler.js",
+  "background/smart_sticky_mode.js",
 ]
 
 # Closure library modules needed by chromevox.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 1696d16..459485d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -6,6 +6,7 @@
 
 import {BrailleCommandHandler} from './braille_command_handler.js';
 import {CommandHandler} from './command_handler.js';
+import {DesktopAutomationHandler} from './desktop_automation_handler.js';
 import {DownloadHandler} from './download_handler.js';
 import {Earcons} from './earcons.js';
 import {FindHandler} from './find_handler.js';
@@ -237,7 +238,7 @@
       if (pageRootStart !== pageRootEnd || pageRootStart !== curRootStart ||
           pageRootEnd !== curRootEnd) {
         o.format('@end_selection');
-        DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction(
+        DesktopAutomationInterface.instance.ignoreDocumentSelectionFromAction(
             false);
         this.pageSel_ = null;
       } else {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 5c4fe10..a78f7118 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -462,14 +462,14 @@
     await new Promise(resolve => {
       this.listenOnce(nonEditable, 'focus', resolve);
     });
-    assertTrue(!DesktopAutomationHandler.instance.textEditHandler);
+    assertTrue(!DesktopAutomationInterface.instance.textEditHandler);
 
     editable.focus();
     await new Promise(resolve => {
       this.listenOnce(editable, 'focus', resolve);
     });
     assertNotNullNorUndefined(
-        DesktopAutomationHandler.instance.textEditHandler);
+        DesktopAutomationInterface.instance.textEditHandler);
   });
 });
 
@@ -719,7 +719,7 @@
 
         running = true;
         const suppressFocusActionOutput = function() {
-          DesktopAutomationHandler.announceActions = false;
+          BaseAutomationHandler.announceActions = false;
         };
         const beforeButton =
             rootNode.find({role: RoleType.BUTTON, name: 'Before'});
@@ -754,7 +754,7 @@
 TEST_F('ChromeVoxBackgroundTest', 'SelectOptionSelected', function() {
   // Undoes the ChromeVoxNextE2E call setting this to true. The doDefault action
   // should always be read.
-  DesktopAutomationHandler.announceActions = false;
+  BaseAutomationHandler.announceActions = false;
   const mockFeedback = this.createMockFeedback();
   const site = `
     <p>start</p>
@@ -1460,7 +1460,7 @@
 #endif
 `,
     'ChromeVoxBackgroundTest', 'MAYBE_TextSelectionAndLiveRegion', function() {
-      DesktopAutomationHandler.announceActions = true;
+      BaseAutomationHandler.announceActions = true;
       const mockFeedback = this.createMockFeedback();
       this.runWithLoadedTree(
           `
@@ -1587,12 +1587,14 @@
   `;
   this.runWithLoadedTree(site, function(root) {
     const assertBeginning = function(expected) {
-      const textEditHandler = DesktopAutomationHandler.instance.textEditHandler;
+      const textEditHandler =
+          DesktopAutomationInterface.instance.textEditHandler;
       assertNotNullNorUndefined(textEditHandler);
       assertEquals(expected, textEditHandler.isSelectionOnFirstLine());
     };
     const assertEnd = function(expected) {
-      const textEditHandler = DesktopAutomationHandler.instance.textEditHandler;
+      const textEditHandler =
+          DesktopAutomationInterface.instance.textEditHandler;
       assertNotNullNorUndefined(textEditHandler);
       assertEquals(expected, textEditHandler.isSelectionOnLastLine());
     };
@@ -2838,13 +2840,13 @@
 
     const evt2 = new CustomAutomationEvent(EventType.FOCUS, group2);
     const currentRange = ChromeVoxState.instance.currentRange;
-    DesktopAutomationHandler.instance.onFocus(evt2);
+    DesktopAutomationInterface.instance.onFocus(evt2);
     assertEquals(currentRange, ChromeVoxState.instance.currentRange);
 
     const evt1 = new CustomAutomationEvent(EventType.FOCUS, group1);
     mockFeedback
-        .call(DesktopAutomationHandler.instance.onFocus.bind(
-            DesktopAutomationHandler.instance, evt1))
+        .call(DesktopAutomationInterface.instance.onFocus.bind(
+            DesktopAutomationInterface.instance, evt1))
         .expectSpeech('hello')
         .replay();
   });
@@ -3050,8 +3052,8 @@
     const button = root.find({role: RoleType.BUTTON});
     const alertEvt = new CustomAutomationEvent(EventType.ALERT, button);
     mockFeedback
-        .call(DesktopAutomationHandler.instance.onAlert.bind(
-            DesktopAutomationHandler.instance, alertEvt))
+        .call(DesktopAutomationInterface.instance.onAlert.bind(
+            DesktopAutomationInterface.instance, alertEvt))
         .call(() => assertFalse(mockFeedback.utteranceInQueue('Alert')))
         .replay();
   });
@@ -3069,8 +3071,8 @@
     const button = root.find({role: RoleType.BUTTON});
     const alertEvt = new CustomAutomationEvent(EventType.ALERT, button);
     mockFeedback
-        .call(DesktopAutomationHandler.instance.onAlert.bind(
-            DesktopAutomationHandler.instance, alertEvt))
+        .call(DesktopAutomationInterface.instance.onAlert.bind(
+            DesktopAutomationInterface.instance, alertEvt))
         .expectNextSpeechUtteranceIsNot('Alert')
         .expectSpeech('hello world')
         .replay();
@@ -3523,7 +3525,7 @@
     </script>
   `;
   this.runWithLoadedTree(site, function(root) {
-    DesktopAutomationHandler.announceActions = false;
+    BaseAutomationHandler.announceActions = false;
     mockFeedback.expectSpeech('Start')
         .call(doCmd('nextObject'))
         .expectSpeech('Click me')
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
index cba1a00..0e04a0a8 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
@@ -128,10 +128,16 @@
    * @return {boolean}
    */
   static disallowEventFromAction(evt) {
-    return !DesktopAutomationHandler.announceActions &&
+    return !BaseAutomationHandler.announceActions &&
         evt.eventFrom === 'action' &&
         evt.eventFromAction !== ActionType.DO_DEFAULT &&
         evt.eventFromAction !== ActionType.SHOW_CONTEXT_MENU;
   }
 };
+
+/**
+ * Controls announcement of non-user-initiated events.
+ * @type {boolean}
+ */
+BaseAutomationHandler.announceActions = false;
 });  // goog.scope
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
index 22d700ff..cc8e66f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
@@ -167,7 +167,7 @@
     return true;
   }
 
-  const textEditHandler = DesktopAutomationHandler.instance.textEditHandler;
+  const textEditHandler = DesktopAutomationInterface.instance.textEditHandler;
   if (!textEditHandler || current.start.node !== textEditHandler.node) {
     return true;
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 900d039..16ffe1e 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview ChromeVox commands.
  */
-
+import {SmartStickyMode} from './smart_sticky_mode.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationEvent = chrome.automation.AutomationEvent;
@@ -414,8 +414,8 @@
         break;
       case 'nativeNextCharacter':
       case 'nativePreviousCharacter':
-        if (DesktopAutomationHandler.instance.textEditHandler) {
-          DesktopAutomationHandler.instance.textEditHandler
+        if (DesktopAutomationInterface.instance.textEditHandler) {
+          DesktopAutomationInterface.instance.textEditHandler
               .injectInferredIntents([{
                 command: chrome.automation.IntentCommandType.MOVE_SELECTION,
                 textBoundary: chrome.automation.IntentTextBoundaryType.CHARACTER
@@ -435,8 +435,8 @@
         break;
       case 'nativeNextWord':
       case 'nativePreviousWord':
-        if (DesktopAutomationHandler.instance.textEditHandler) {
-          DesktopAutomationHandler.instance.textEditHandler
+        if (DesktopAutomationInterface.instance.textEditHandler) {
+          DesktopAutomationInterface.instance.textEditHandler
               .injectInferredIntents([{
                 command: chrome.automation.IntentCommandType.MOVE_SELECTION,
                 textBoundary: command === 'nativeNextWord' ?
@@ -890,7 +890,7 @@
         if (!ChromeVoxState.instance.pageSel_) {
           ChromeVoxState.instance.pageSel_ =
               ChromeVoxState.instance.currentRange;
-          DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction(
+          DesktopAutomationInterface.instance.ignoreDocumentSelectionFromAction(
               true);
         } else {
           const root = ChromeVoxState.instance.currentRange.start.node.root;
@@ -905,8 +905,8 @@
                     .format('@end_selection')
                     .withSpeechAndBraille(sel, sel, OutputEventType.NAVIGATE)
                     .go();
-            DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction(
-                false);
+            DesktopAutomationInterface.instance
+                .ignoreDocumentSelectionFromAction(false);
           }
           ChromeVoxState.instance.pageSel_ = null;
           return false;
@@ -1367,7 +1367,7 @@
       return true;
     }
 
-    const textEditHandler = DesktopAutomationHandler.instance.textEditHandler;
+    const textEditHandler = DesktopAutomationInterface.instance.textEditHandler;
     if (!textEditHandler ||
         !AutomationUtil.isDescendantOf(
             ChromeVoxState.instance.currentRange.start.node,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index 8e2e17f..cdadf70 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -6,17 +6,6 @@
  * @fileoverview Handles automation from a desktop automation node.
  */
 
-goog.provide('DesktopAutomationHandler');
-
-goog.require('AutoScrollHandler');
-goog.require('AutomationObjectConstructorInstaller');
-goog.require('BaseAutomationHandler');
-goog.require('ChromeVoxState');
-goog.require('CommandHandlerInterface');
-goog.require('CustomAutomationEvent');
-goog.require('editing.TextEditHandler');
-
-goog.scope(function() {
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
@@ -24,7 +13,7 @@
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
 
-DesktopAutomationHandler = class extends BaseAutomationHandler {
+export class DesktopAutomationHandler extends DesktopAutomationInterface {
   /**
    * @param {!AutomationNode} node
    */
@@ -429,6 +418,7 @@
   /**
    * Sets whether document selections from actions should be ignored.
    * @param {boolean} val
+   * @override
    */
   ignoreDocumentSelectionFromAction(val) {
     this.shouldIgnoreDocumentSelectionFromAction_ = val;
@@ -861,15 +851,16 @@
    * Initializes global state for DesktopAutomationHandler.
    */
   static init() {
-    if (DesktopAutomationHandler.instance) {
-      throw new Error('DesktopAutomationHandler.instance already exists.');
+    if (DesktopAutomationInterface.instance) {
+      throw new Error('DesktopAutomationInterface.instance already exists.');
     }
 
     chrome.automation.getDesktop(function(desktop) {
-      DesktopAutomationHandler.instance = new DesktopAutomationHandler(desktop);
+      DesktopAutomationInterface.instance =
+          new DesktopAutomationHandler(desktop);
     });
   }
-};
+}
 
 /**
  * Time to wait until processing more value changed events.
@@ -896,16 +887,3 @@
  * @const {number}
  */
 DesktopAutomationHandler.ATTRIBUTE_DELAY_MS = 1500;
-
-/**
- * Controls announcement of non-user-initiated events.
- * @type {boolean}
- */
-DesktopAutomationHandler.announceActions = false;
-
-/**
- * Global instance.
- * @type {DesktopAutomationHandler}
- */
-DesktopAutomationHandler.instance;
-});  // goog.scope
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 0d2ccb5..6d0d10fe 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -16,9 +16,12 @@
 
     window.press = this.press;
 
+    await importModule(
+        'DesktopAutomationHandler',
+        '/chromevox/background/desktop_automation_handler.js');
     await new Promise(r => {
       chrome.automation.getDesktop(desktop => {
-        this.handler_ = new DesktopAutomationHandler(desktop);
+        this.handler_ = DesktopAutomationInterface.instance;
         r();
       });
     });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
new file mode 100644
index 0000000..ad4c0cc
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
@@ -0,0 +1,28 @@
+// 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.
+
+/**
+ * @fileoverview Interface to prevent circular dependencies.
+ */
+
+goog.provide('DesktopAutomationInterface');
+
+goog.require('BaseAutomationHandler');
+goog.require('editing.TextEditHandler');
+
+DesktopAutomationInterface = class extends BaseAutomationHandler {
+  /** @type {editing.TextEditHandler} */
+  get textEditHandler() {}
+
+  /**
+   * Sets whether document selections from actions should be ignored.
+   * @param {boolean} val
+   */
+  ignoreDocumentSelectionFromAction(val) {}
+};
+
+/**
+ * @type {DesktopAutomationInterface}
+ */
+DesktopAutomationInterface.instance;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
index 82fcee8..d213e232 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
@@ -23,7 +23,7 @@
 
   waitForEditableEvent() {
     return new Promise(resolve => {
-      DesktopAutomationHandler.instance.textEditHandler_.onEvent = (e) =>
+      DesktopAutomationInterface.instance.textEditHandler_.onEvent = (e) =>
           resolve(e);
     });
   }
@@ -1907,7 +1907,7 @@
         // The initial real input is a simple non-rich text field.
         assertEquals(
             'AutomationEditableText',
-            DesktopAutomationHandler.instance.textEditHandler.editableText_
+            DesktopAutomationInterface.instance.textEditHandler.editableText_
                 .constructor.name,
             'Real text field was not a non-rich text.');
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 0ab33ad..4121444 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -9,19 +9,24 @@
 goog.require('AutomationPredicate');
 goog.require('AutomationUtil');
 goog.require('AutoScrollHandler');
+goog.require('AutomationObjectConstructorInstaller');
 goog.require('BackgroundKeyboardHandler');
+goog.require('BaseAutomationHandler');
 goog.require('BrailleCommandData');
 goog.require('BrailleKeyCommand');
+goog.require('ChromeVox');
 goog.require('ChromeVoxBackground');
 goog.require('ChromeVoxEditableTextBase');
 goog.require('ChromeVoxKbHandler');
 goog.require('ChromeVoxPrefs');
 goog.require('ChromeVoxState');
+goog.require('ChromeVoxStateObserver');
 goog.require('Color');
 goog.require('CommandHandlerInterface');
 goog.require('CommandStore');
 goog.require('CustomAutomationEvent');
-goog.require('DesktopAutomationHandler');
+goog.require('DesktopAutomationInterface');
+goog.require('editing.TextEditHandler');
 goog.require('EventGenerator');
 goog.require('EventSourceState');
 goog.require('ExtensionBridge');
@@ -37,7 +42,7 @@
 goog.require('OutputEventType');
 goog.require('PanelCommand');
 goog.require('PhoneticData');
-goog.require('SmartStickyMode');
 goog.require('TreeDumper');
+goog.require('TreePathRecoveryStrategy');
 goog.require('constants');
 goog.require('cursors.Cursor');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
index 26436fa..657247a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
@@ -187,9 +187,10 @@
     }
 
     Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
-    DesktopAutomationHandler.instance.onEventDefault(new CustomAutomationEvent(
-        EventType.HOVER, target,
-        {eventFromAction: chrome.automation.ActionType.HIT_TEST}));
+    DesktopAutomationInterface.instance.onEventDefault(
+        new CustomAutomationEvent(
+            EventType.HOVER, target,
+            {eventFromAction: chrome.automation.ActionType.HIT_TEST}));
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
index ae657c6..23e7ca6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview Handles automation from ChromeVox's current range.
  */
+import {DesktopAutomationHandler} from './desktop_automation_handler.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
index 995049a..d262c4f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
@@ -8,14 +8,8 @@
  * an editable.
  */
 
-goog.provide('SmartStickyMode');
-
-goog.require('AutomationUtil');
-goog.require('ChromeVox');
-goog.require('ChromeVoxState');
-
 /** @implements {ChromeVoxStateObserver} */
-SmartStickyMode = class {
+export class SmartStickyMode {
   constructor() {
     /** @private {boolean} */
     this.ignoreRangeChanges_ = false;
@@ -32,7 +26,11 @@
     ChromeVoxState.addObserver(this);
   }
 
-  /** @override */
+  /**
+   * @param {?cursors.Range} newRange
+   * @param {boolean=} opt_fromEditing
+   * @override
+   */
   onCurrentRangeChanged(newRange, opt_fromEditing) {
     if (!newRange || this.ignoreRangeChanges_ ||
         ChromeVoxState.isReadingContinuously || opt_fromEditing ||
@@ -188,4 +186,4 @@
 
     return null;
   }
-};
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js
index acadf6f..d232361 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode_test.js
@@ -10,8 +10,10 @@
  */
 ChromeVoxSmartStickyModeTest = class extends ChromeVoxNextE2ETest {
   /** @override */
-  setUp() {
-    super.setUp();
+  async setUpDeferred() {
+    await super.setUpDeferred();
+    await importModule(
+        'SmartStickyMode', '/chromevox/background/smart_sticky_mode.js');
     this.ssm_ = new SmartStickyMode();
     // Deregister from actual range changes.
     ChromeVoxState.removeObserver(this.ssm_);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index 7572cb26..c22d291 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -371,8 +371,8 @@
       // not the keymap. There are commands that don't have a key binding (e.g.
       // commands for touch).
 
-      // Get the key map from the background page.
-      const keymap = bkgnd['KeyMap']['get']();
+      // Get the key map.
+      const keymap = KeyMap.get();
 
       // Make a copy of the key bindings, get the localized title of each
       // command, and then sort them.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
index e9c1de9..92c2017 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
@@ -26,7 +26,7 @@
     }
 
     // For tests, enable announcement of events we trigger via automation.
-    DesktopAutomationHandler.announceActions = true;
+    BaseAutomationHandler.announceActions = true;
 
     this.originalOutputContextValues_ = {};
     for (const role in OutputRoleInfo) {
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
index 488ffd8..ded69b6 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
@@ -54,8 +54,7 @@
     outline: none;
     padding: 0;
     position: absolute;
-    right: calc(2px
-      + 2*var(--emoji-picker-side-padding) - var(--emoji-picker-width));
+    right: 0;
     top: calc(var(--emoji-group-heading-padding-top)
       + var(--emoji-group-heading-padding-bottom)
       + var(--emoji-group-heading-size));
@@ -93,21 +92,21 @@
 
 <template is="dom-if" if="[[group]]">
   <div id="heading" role="heading" aria-level="2" tabindex="-1">
-  <div id="heading-left">[[group]]</div>
-  <template is="dom-if" if="[[clearable]]">
-    <cr-icon-button id="show-clear" iron-icon="emoji_picker:more_horizontal"
-      on-click="onClearClick">
-    </cr-icon-button>
-  </template>
+    <div id="heading-left">[[group]]</div>
+    <template is="dom-if" if="[[clearable]]">
+      <cr-icon-button id="show-clear" iron-icon="emoji_picker:more_horizontal"
+        on-click="onClearClick">
+      </cr-icon-button>
+    </template>
+  </div>
 </template>
-</div>
   <template is = "dom-if" if="[[showClearRecents]]">
   <button id="clear-recents" on-click="onClearRecentsClick">
     <div id="clear-recents-hover">
     Clear recently used emojis
     </div>
   </button>
-  </template>
+</template>
 <div id="emoji">
   <div id="fake-focus-target" tabindex="-1"></div>
   <template is="dom-repeat" items="[[data]]">
diff --git a/chrome/browser/resources/chromeos/login/images/spinner_dark.json b/chrome/browser/resources/chromeos/login/images/spinner_dark.json
new file mode 100644
index 0000000..2ef152cc
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/spinner_dark.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":119,"w":823,"h":823,"nm":"Spinner_01","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"01","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[0.916]},"o":{"x":[0.606],"y":[0.17]},"t":0,"s":[0]},{"t":119,"s":[360]}],"ix":10},"p":{"a":0,"k":[411.5,411.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[790,790],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.724],"y":[0]},"t":25,"s":[0]},{"t":119,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.606],"y":[0.316]},"t":0,"s":[0.2]},{"t":119,"s":[99.8]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.266666666667,0.301960784314,0.364705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"02","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[0.916]},"o":{"x":[0.762],"y":[0.221]},"t":0,"s":[0]},{"t":119,"s":[360]}],"ix":10},"p":{"a":0,"k":[411.5,411.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[790,790],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.009],"y":[0.883]},"o":{"x":[0.777],"y":[0]},"t":25,"s":[0]},{"t":119,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.606],"y":[0.316]},"t":0,"s":[0]},{"t":119,"s":[99.8]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.337254901961,0.4,0.505882352941,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":29,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"03","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[0.916]},"o":{"x":[0.792],"y":[0.23]},"t":0,"s":[0]},{"t":119,"s":[360]}],"ix":10},"p":{"a":0,"k":[411.5,411.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[790,790],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.184],"y":[0.831]},"o":{"x":[0.724],"y":[0]},"t":25,"s":[0]},{"t":119,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.606],"y":[0.316]},"t":0,"s":[0]},{"t":119,"s":[99.8]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.349019607843,0.474509803922,0.686274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":28,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"04","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[0.916]},"o":{"x":[0.829],"y":[0.225]},"t":0,"s":[0]},{"t":119,"s":[360]}],"ix":10},"p":{"a":0,"k":[411.5,411.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[790,790],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.246],"y":[0.755]},"o":{"x":[0.724],"y":[0]},"t":25,"s":[0]},{"t":119,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.606],"y":[0.316]},"t":0,"s":[0]},{"t":119,"s":[99.8]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":27,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/oobe_animation_resources.grdp b/chrome/browser/resources/chromeos/login/oobe_animation_resources.grdp
index 4686833..c7fd422 100644
--- a/chrome/browser/resources/chromeos/login/oobe_animation_resources.grdp
+++ b/chrome/browser/resources/chromeos/login/oobe_animation_resources.grdp
@@ -12,4 +12,5 @@
 
   <!-- Loading step animation. -->
   <include type="BINDATA" compress="gzip" name="IDR_LOGIN_SPINNER_ANIMATION" resource_path="spinner.json" file="images/spinner.json" />
+  <include type="BINDATA" compress="gzip" name="IDR_LOGIN_SPINNER_DARK_ANIMATION" resource_path="spinner_dark.json" file="images/spinner_dark.json" />
 </grit-part>
diff --git a/chrome/browser/resources/extensions/extensions.gni b/chrome/browser/resources/extensions/extensions.gni
index 8ba8354..2583eb26 100644
--- a/chrome/browser/resources/extensions/extensions.gni
+++ b/chrome/browser/resources/extensions/extensions.gni
@@ -52,7 +52,8 @@
   "sidebar.ts",
   "site_permissions.ts",
   "site_permissions_by_site.ts",
-  "site_permissions_edit_dialog.ts",
+  "site_permissions_edit_permissions_dialog.ts",
+  "site_permissions_edit_url_dialog.ts",
   "site_permissions_list.ts",
   "toggle_row.ts",
   "toolbar.ts",
diff --git a/chrome/browser/resources/extensions/extensions.ts b/chrome/browser/resources/extensions/extensions.ts
index c6f5b77..d3b4ec5 100644
--- a/chrome/browser/resources/extensions/extensions.ts
+++ b/chrome/browser/resources/extensions/extensions.ts
@@ -40,7 +40,8 @@
 export {ExtensionsSidebarElement} from './sidebar.js';
 export {ExtensionsSitePermissionsElement} from './site_permissions.js';
 export {ExtensionsSitePermissionsBySiteElement} from './site_permissions_by_site.js';
-export {getSitePermissionsPatternFromSite, SitePermissionsEditDialogElement} from './site_permissions_edit_dialog.js';
+export {SitePermissionsEditPermissionsDialogElement} from './site_permissions_edit_permissions_dialog.js';
+export {getSitePermissionsPatternFromSite, SitePermissionsEditUrlDialogElement} from './site_permissions_edit_url_dialog.js';
 export {ExtensionsSitePermissionsListElement} from './site_permissions_list.js';
 export {SiteSettingsMixin} from './site_settings_mixin.js';
 export {ExtensionsToggleRowElement} from './toggle_row.js';
diff --git a/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html b/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html
new file mode 100644
index 0000000..7f89b94
--- /dev/null
+++ b/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html
@@ -0,0 +1,28 @@
+<style include="cr-shared-style">
+  cr-radio-group {
+    width: 100%;
+  }
+</style>
+<cr-dialog id="dialog" show-on-attach>
+  <div slot="title">[[computeDialogTitle_(site)]]</div>
+  <div slot="body">
+    <cr-radio-group selected="{{siteSet_}}">
+      <cr-radio-button
+          name="[[userSiteSetEnum_.PERMITTED]]"
+          label="[[getPermittedSiteLabel_(site)]]">
+      </cr-radio-button>
+      <cr-radio-button
+          name="[[userSiteSetEnum_.RESTRICTED]]"
+          label="[[getRestrictedSiteLabel_(site)]]">
+      </cr-radio-button>
+    </cr-radio-group>
+  </div>
+  <div slot="button-container">
+    <cr-button class="cancel-button" on-click="onCancelClick_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button class="action-button" id="submit" on-click="onSubmitClick_">
+      $i18n{save}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts b/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts
new file mode 100644
index 0000000..54bd27c
--- /dev/null
+++ b/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts
@@ -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.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import './strings.m.js';
+
+import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import {I18nMixin} from 'chrome://resources/js/i18n_mixin.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {SiteSettingsDelegate} from './site_settings_mixin.js';
+
+export interface SitePermissionsEditPermissionsDialogElement {
+  $: {
+    dialog: CrDialogElement,
+    submit: CrButtonElement,
+  };
+}
+
+const SitePermissionsEditPermissionsDialogElementBase =
+    I18nMixin(PolymerElement);
+
+export class SitePermissionsEditPermissionsDialogElement extends
+    SitePermissionsEditPermissionsDialogElementBase {
+  static get is() {
+    return 'site-permissions-edit-permissions-dialog';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      delegate: Object,
+
+      /**
+       * The current siteSet for `site`, as stored in the backend. Specifies
+       * whether `site` is a user specified permitted or restricted site.
+       */
+      originalSiteSet: String,
+
+      /**
+       * The url of the site whose permissions are currently being edited.
+       */
+      site: String,
+
+      /**
+       * The temporary siteSet for `site` as displayed in the dialog. Will be
+       * saved to the backend when the dialog is submitted.
+       */
+      siteSet_: String,
+
+      userSiteSetEnum_: {
+        type: Object,
+        value: chrome.developerPrivate.UserSiteSet,
+      },
+    };
+  }
+
+  delegate: SiteSettingsDelegate;
+  originalSiteSet: chrome.developerPrivate.UserSiteSet;
+  site: string;
+  private siteSet_: chrome.developerPrivate.UserSiteSet;
+
+  connectedCallback() {
+    super.connectedCallback();
+    this.siteSet_ = this.originalSiteSet;
+  }
+
+  private onCancelClick_() {
+    this.$.dialog.cancel();
+  }
+
+  private onSubmitClick_() {
+    if (this.siteSet_ === this.originalSiteSet) {
+      this.$.dialog.close();
+      return;
+    }
+
+    this.delegate.addUserSpecifiedSite(this.siteSet_, this.site).then(() => {
+      this.$.dialog.close();
+    });
+  }
+
+  private computeDialogTitle_(): string {
+    return this.i18n('sitePermissionsEditPermissionsDialogTitle', this.site);
+  }
+
+  private getPermittedSiteLabel_(): string {
+    return this.i18n('editSitePermissionsAllowAllExtensions', this.site);
+  }
+
+  private getRestrictedSiteLabel_(): string {
+    return this.i18n('editSitePermissionsRestrictExtensions', this.site);
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'site-permissions-edit-permissions-dialog':
+        SitePermissionsEditPermissionsDialogElement;
+  }
+}
+
+customElements.define(
+    SitePermissionsEditPermissionsDialogElement.is,
+    SitePermissionsEditPermissionsDialogElement);
diff --git a/chrome/browser/resources/extensions/site_permissions_edit_dialog.html b/chrome/browser/resources/extensions/site_permissions_edit_url_dialog.html
similarity index 100%
rename from chrome/browser/resources/extensions/site_permissions_edit_dialog.html
rename to chrome/browser/resources/extensions/site_permissions_edit_url_dialog.html
diff --git a/chrome/browser/resources/extensions/site_permissions_edit_dialog.ts b/chrome/browser/resources/extensions/site_permissions_edit_url_dialog.ts
similarity index 93%
rename from chrome/browser/resources/extensions/site_permissions_edit_dialog.ts
rename to chrome/browser/resources/extensions/site_permissions_edit_url_dialog.ts
index 09a8681..bc77d621 100644
--- a/chrome/browser/resources/extensions/site_permissions_edit_dialog.ts
+++ b/chrome/browser/resources/extensions/site_permissions_edit_url_dialog.ts
@@ -42,16 +42,16 @@
   return scheme + host + port;
 }
 
-export interface SitePermissionsEditDialogElement {
+export interface SitePermissionsEditUrlDialogElement {
   $: {
     dialog: CrDialogElement,
     submit: CrButtonElement,
   };
 }
 
-export class SitePermissionsEditDialogElement extends PolymerElement {
+export class SitePermissionsEditUrlDialogElement extends PolymerElement {
   static get is() {
-    return 'site-permissions-edit-dialog';
+    return 'site-permissions-edit-url-dialog';
   }
 
   static get template() {
@@ -171,9 +171,10 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'site-permissions-edit-dialog': SitePermissionsEditDialogElement;
+    'site-permissions-edit-url-dialog': SitePermissionsEditUrlDialogElement;
   }
 }
 
 customElements.define(
-    SitePermissionsEditDialogElement.is, SitePermissionsEditDialogElement);
+    SitePermissionsEditUrlDialogElement.is,
+    SitePermissionsEditUrlDialogElement);
diff --git a/chrome/browser/resources/extensions/site_permissions_list.html b/chrome/browser/resources/extensions/site_permissions_list.html
index c0a4cd5..d3eb25f6 100644
--- a/chrome/browser/resources/extensions/site_permissions_list.html
+++ b/chrome/browser/resources/extensions/site_permissions_list.html
@@ -44,8 +44,6 @@
       <div class="site-favicon"
           style$="background-image:[[getFaviconUrl_(item)]]"></div>
       <span class="site">[[item]]</span>
-      <cr-icon-button class="subpage-arrow no-overlap"></cr-icon-button>
-      <div class="separator"></div>
       <cr-icon-button class="icon-more-vert no-overlap" on-click="onDotsClick_">
       </cr-icon-button>
     </div>
@@ -53,21 +51,34 @@
 </div>
 
 <cr-action-menu id="siteActionMenu">
-  <button class="dropdown-item" id="edit-site"
-      on-click="onActionMenuEditClick_">
-    $i18n{hostPermissionsEdit}
+  <button class="dropdown-item" id="edit-site-url"
+      on-click="onEditSiteUrlClick_">
+    $i18n{sitePermissionsEditUrl}
+  </button>
+  <button class="dropdown-item" id="edit-site-permissions"
+      on-click="onEditSitePermissionsClick_">
+    $i18n{sitePermissionsEditPermissions}
   </button>
   <button class="dropdown-item" id="remove-site"
-      on-click="onActionMenuRemoveClick_">
+      on-click="onRemoveSiteClick_">
     $i18n{remove}
   </button>
 </cr-action-menu>
 
-<template is="dom-if" if="[[showEditSiteDialog_]]" restamp>
-  <site-permissions-edit-dialog
+<template is="dom-if" if="[[showEditSiteUrlDialog_]]" restamp>
+  <site-permissions-edit-url-dialog
       delegate="[[delegate]]"
       site-to-edit="[[siteToEdit_]]"
       site-set="[[siteSet]]"
-      on-close="onEditSiteDialogClose_">
-  </site-permissions-edit-dialog>
+      on-close="onEditSiteUrlDialogClose_">
+  </site-permissions-edit-url-dialog>
+</template>
+
+<template is="dom-if" if="[[showEditSitePermissionsDialog_]]" restamp>
+  <site-permissions-edit-permissions-dialog
+      delegate="[[delegate]]"
+      site="[[siteToEdit_]]"
+      original-site-set="[[siteSet]]"
+      on-close="onEditSitePermissionsDialogClose_">
+  </site-permissions-edit-permissions-dialog>
 </template>
diff --git a/chrome/browser/resources/extensions/site_permissions_list.ts b/chrome/browser/resources/extensions/site_permissions_list.ts
index dc841b66..866b0ef 100644
--- a/chrome/browser/resources/extensions/site_permissions_list.ts
+++ b/chrome/browser/resources/extensions/site_permissions_list.ts
@@ -10,7 +10,8 @@
 import './strings.m.js';
 import './shared_style.js';
 import './shared_vars.js';
-import './site_permissions_edit_dialog.js';
+import './site_permissions_edit_permissions_dialog.js';
+import './site_permissions_edit_url_dialog.js';
 
 import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
@@ -44,7 +45,12 @@
       siteSet: String,
       sites: Array,
 
-      showEditSiteDialog_: {
+      showEditSiteUrlDialog_: {
+        type: Boolean,
+        value: false,
+      },
+
+      showEditSitePermissionsDialog_: {
         type: Boolean,
         value: false,
       },
@@ -64,7 +70,8 @@
   header: string;
   siteSet: chrome.developerPrivate.UserSiteSet;
   sites: Array<string>;
-  private showEditSiteDialog_: boolean;
+  private showEditSiteUrlDialog_: boolean;
+  private showEditSitePermissionsDialog_: boolean;
   private siteToEdit_: string|null;
 
   // The element to return focus to once the site input dialog closes. If
@@ -80,37 +87,56 @@
     return getFaviconUrl(url);
   }
 
-  private onAddSiteClick_() {
-    this.siteToEdit_ = null;
-    this.showEditSiteDialog_ = true;
+  private focusOnAnchor_() {
+    // Return focus to the three dots menu once a site has been edited.
+    // TODO(crbug.com/1298326): If the edited site is the only site in the
+    // list, focus is not on the three dots menu.
+    assert(this.siteToEditAnchorElement_, 'Site Anchor');
+    focusWithoutInk(this.siteToEditAnchorElement_);
+    this.siteToEditAnchorElement_ = null;
   }
 
-  private onEditSiteDialogClose_() {
-    this.showEditSiteDialog_ = false;
+  private onAddSiteClick_() {
+    assert(!this.showEditSitePermissionsDialog_);
+    this.siteToEdit_ = null;
+    this.showEditSiteUrlDialog_ = true;
+  }
+
+  private onEditSiteUrlDialogClose_() {
+    this.showEditSiteUrlDialog_ = false;
     if (this.siteToEdit_ !== null) {
-      // Return focus to the three dots menu once a site has been edited.
-      // TODO(crbug.com/1298326): If the edited site is the only site in the
-      // list, focus is not on the three dots menu.
-      assert(this.siteToEditAnchorElement_, 'Site Anchor');
-      focusWithoutInk(this.siteToEditAnchorElement_);
-      this.siteToEditAnchorElement_ = null;
+      this.focusOnAnchor_();
     }
     this.siteToEdit_ = null;
   }
 
+  private onEditSitePermissionsDialogClose_() {
+    this.showEditSitePermissionsDialog_ = false;
+    assert(this.siteToEdit_, 'Site To Edit');
+    this.focusOnAnchor_();
+    this.siteToEdit_ = null;
+  }
+
   private onDotsClick_(e: DomRepeatEvent<string>) {
     this.siteToEdit_ = e.model.item;
+    assert(!this.showEditSitePermissionsDialog_);
     this.$.siteActionMenu.showAt(e.target as HTMLElement);
     this.siteToEditAnchorElement_ = e.target as HTMLElement;
   }
 
-  private onActionMenuEditClick_() {
+  private onEditSitePermissionsClick_() {
     this.closeActionMenu_();
     assert(this.siteToEdit_ !== null);
-    this.showEditSiteDialog_ = true;
+    this.showEditSitePermissionsDialog_ = true;
   }
 
-  private onActionMenuRemoveClick_() {
+  private onEditSiteUrlClick_() {
+    this.closeActionMenu_();
+    assert(this.siteToEdit_ !== null);
+    this.showEditSiteUrlDialog_ = true;
+  }
+
+  private onRemoveSiteClick_() {
     assert(this.siteToEdit_, 'Site To Edit');
     this.delegate.removeUserSpecifiedSite(this.siteSet, this.siteToEdit_)
         .then(() => {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.html
index a4376f0..46e44363 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.html
@@ -5,6 +5,6 @@
     localized-string="$i18n{appManagementAppDetailsTitle}">
   </localized-link>
 </div>
-<div class="indented-permission-block">
-  [[getTypeString(app)]]
+<div class="indented-permission-block" id="type">
+  [[getTypeAndSourceString_(app)]]
 </div>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
index 4c33168a..ea124c09 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
@@ -4,7 +4,7 @@
 
 import '//resources/cr_components/localized_link/localized_link.js';
 
-import {AppType} from '//resources/cr_components/app_management/constants.js';
+import {AppType, InstallSource} from '//resources/cr_components/app_management/constants.js';
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -56,7 +56,7 @@
    * @returns {string}
    * @private
    */
-  getTypeString(app) {
+  getTypeString_(app) {
     switch (app.type) {
       case AppType.kArc:
         return this.i18n('appManagementAppDetailsTypeAndroid');
@@ -73,6 +73,45 @@
         return '';
     }
   }
+
+  /**
+   * Returns the string for the installation source.
+   *
+   * @param {!App} app
+   * @returns {string}
+   * @private
+   */
+  getInstallSourceString_(app) {
+    switch (app.installSource) {
+      case InstallSource.kChromeWebStore:
+        return this.i18n('appManagementAppDetailsInstallSourceWebStore');
+      case InstallSource.kPlayStore:
+        return this.i18n('appManagementAppDetailsInstallSourcePlayStore');
+      case InstallSource.kBrowser:
+        return this.i18n('appManagementAppDetailsInstallSourceBrowser');
+      default:
+        console.error('Install source not recognised.');
+        return '';
+    }
+  }
+
+  /**
+   * Returns the string for the app type.
+   *
+   * @param {!App} app
+   * @returns {string}
+   * @private
+   */
+  getTypeAndSourceString_(app) {
+    if (app.installSource === InstallSource.kSystem) {
+      return this.i18n('appManagementAppDetailsTypeCrosSystem');
+    } else if (app.installSource === InstallSource.kUnknown) {
+      return this.getTypeString_(app);
+    }
+    return this.i18n(
+        'appManagementAppDetailsTypeAndSourceCombined',
+        this.getTypeString_(app), this.getInstallSourceString_(app));
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS b/chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS
index c1c72420..b1d1296a 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS
@@ -1,4 +1,3 @@
-myy@chromium.org
 mlcui@google.com
 
 per-file *input*=jopalmer@chromium.org
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 98cc3ff..c5936c24 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -1077,6 +1077,8 @@
       {TP::COLOR_DOWNLOAD_SHELF, kColorDownloadShelf},
       {TP::COLOR_FRAME_ACTIVE, ui::kColorFrameActive},
       {TP::COLOR_FRAME_INACTIVE, ui::kColorFrameInactive},
+      {TP::COLOR_INFOBAR, kColorInfoBarBackground},
+      {TP::COLOR_INFOBAR_TEXT, kColorInfoBarForeground},
       {TP::COLOR_NTP_BACKGROUND, kColorNewTabPageBackground},
       {TP::COLOR_NTP_HEADER, kColorNewTabPageHeader},
       {TP::COLOR_NTP_LINK, kColorNewTabPageLink},
@@ -1084,8 +1086,6 @@
       {TP::COLOR_NTP_SECTION_BORDER, kColorNewTabPageSectionBorder},
       {TP::COLOR_NTP_SHORTCUT, kColorNewTabPageMostVisitedTileBackground},
       {TP::COLOR_NTP_TEXT, kColorNewTabPageText},
-      {TP::COLOR_INFOBAR, kColorInfoBarBackground},
-      {TP::COLOR_INFOBAR_TEXT, kColorInfoBarForeground},
       {TP::COLOR_OMNIBOX_TEXT, kColorOmniboxText},
       {TP::COLOR_OMNIBOX_BACKGROUND, kColorOmniboxBackground},
       {TP::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE,
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index c65d3b5..a76e6eb74 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3179,7 +3179,7 @@
         Chrome tips
       </message>
       <message name="IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_DEFAULT_BROWSER" desc="Notification body text educating user about setting chrome as default browser.">
-        Learn how to set chrome as your default
+        Learn how to set Chrome as your default
       </message>
       <message name="IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_SIGN_IN" desc="Notification body text educating user about signing in into chrome.">
         To get your Google stuff across devices, sign in
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_DEFAULT_BROWSER.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_DEFAULT_BROWSER.png.sha1
index 094cfe908..e7fc126 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_DEFAULT_BROWSER.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEATURE_NOTIFICATION_GUIDE_NOTIFICATION_MESSAGE_DEFAULT_BROWSER.png.sha1
@@ -1 +1 @@
-5630d298d323d03cdcb3cb95b8931d26b0aad86a
\ No newline at end of file
+bc3c3d9fec10ab130eebfe70288084f0577d9d3f
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/OWNERS b/chrome/browser/ui/app_list/OWNERS
index 8a1db8bf..2241bdf 100644
--- a/chrome/browser/ui/app_list/OWNERS
+++ b/chrome/browser/ui/app_list/OWNERS
@@ -1,4 +1,3 @@
-calamity@chromium.org
 dominickn@chromium.org
 jennyz@chromium.org
 khmel@chromium.org
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 496d318..56e62aa 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -464,7 +464,7 @@
       sync_item->item_name = *maybe_item_name;
     const std::string* maybe_parent_id =
         item.second.FindStringKey(kParentIdKey);
-    if (maybe_item_name)
+    if (maybe_parent_id)
       sync_item->parent_id = *maybe_parent_id;
 
     const std::string* position = item.second.FindStringKey(kPositionKey);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
index eb2bce52..ab64672 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/fileapi/file_change_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -372,6 +373,16 @@
     chromeos::MountError error_code,
     const file_manager::Volume& volume) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Drive FS may restart from time to time, so only remove items if drive is
+  // disabled.
+  if (volume.type() == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
+    const auto* drive_integration_service =
+        drive::DriveIntegrationServiceFactory::FindForProfile(profile());
+    if (drive_integration_service && drive_integration_service->is_enabled()) {
+      return;
+    }
+  }
   // Schedule task to remove items under the unmounted file path from the model.
   // During suspend, some volumes get unmounted - for example, drive FS. The
   // file system delegate gets shutdown to avoid removing items from unmounted
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.cc b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
index ec4bbf1..4d34d75 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_util.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
@@ -10,9 +10,12 @@
 #include "base/barrier_closure.h"
 #include "base/containers/contains.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ui/ash/thumbnail_loader.h"
+#include "components/account_id/account_id.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -27,17 +30,40 @@
 
 // Utilities -------------------------------------------------------------------
 
+bool ShouldSkipPathCheck(Profile* profile, const base::FilePath& path) {
+  // Drive FS may be in the middle of restarting, so if it is enabled but not
+  // mounted, assume any files in drive are valid.
+  const auto* drive_integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(profile);
+  return drive_integration_service && drive_integration_service->is_enabled() &&
+         !drive_integration_service->IsMounted() &&
+         drive_integration_service->GetMountPointPath().IsParent(path);
+}
+
 void FilePathValid(Profile* profile,
                    FilePathWithValidityRequirement file_path_with_requirement,
                    FilePathValidCallback callback) {
+  auto* user = ProfileHelper::Get()->GetUserByProfile(profile);
   file_manager::util::GetMetadataForPath(
       file_manager::util::GetFileManagerFileSystemContext(profile),
       file_path_with_requirement.first,
       storage::FileSystemOperation::GET_METADATA_FIELD_NONE,
       base::BindOnce(
-          [](FilePathValidCallback callback, ValidityRequirement requirement,
-             base::File::Error result, const base::File::Info& file_info) {
+          [](FilePathValidCallback callback,
+             FilePathWithValidityRequirement file_path_with_requirement,
+             AccountId account_id, base::File::Error result,
+             const base::File::Info& file_info) {
+            Profile* profile =
+                ProfileHelper::Get()->GetProfileByAccountId(account_id);
+            if (profile && ShouldSkipPathCheck(
+                               profile, file_path_with_requirement.first)) {
+              std::move(callback).Run(true);
+              return;
+            }
+
             bool valid = true;
+            const ValidityRequirement& requirement =
+                file_path_with_requirement.second;
             if (requirement.must_exist)
               valid = result == base::File::Error::FILE_OK;
             if (valid && requirement.must_be_newer_than) {
@@ -47,7 +73,8 @@
             }
             std::move(callback).Run(valid);
           },
-          std::move(callback), file_path_with_requirement.second));
+          std::move(callback), file_path_with_requirement,
+          user ? user->GetAccountId() : EmptyAccountId()));
 }
 
 void PartitionFilePathsByExistence(
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 4abe459..bdedcd21e 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -84,8 +84,9 @@
     const SkColor result_color =
         color_utils::HSLToSkColor(result, SkColorGetA(color));
     DVLOG(2) << "ColorTransform IncreaseLightness:"
-             << " Input: " << ui::SkColorName(color)
-             << " Result: " << ui::SkColorName(result_color);
+             << " Percent: " << percent
+             << " Transform Color: " << ui::SkColorName(color)
+             << " Result Color: " << ui::SkColorName(result_color);
     return result_color;
   };
   return base::BindRepeating(generator, std::move(input_transform), percent);
diff --git a/chrome/browser/ui/messages/DIR_METADATA b/chrome/browser/ui/messages/DIR_METADATA
index 2ae4e3f9..df92a4dd 100644
--- a/chrome/browser/ui/messages/DIR_METADATA
+++ b/chrome/browser/ui/messages/DIR_METADATA
@@ -1,4 +1 @@
-monorail {
-  component: "UI>Browser>Mobile>Messages"
-}
-os: ANDROID
+mixins: "//components/messages/COMMON_METADATA"
diff --git a/chrome/browser/ui/messages/OWNERS b/chrome/browser/ui/messages/OWNERS
index e2f466a..3a88845 100644
--- a/chrome/browser/ui/messages/OWNERS
+++ b/chrome/browser/ui/messages/OWNERS
@@ -1,6 +1 @@
-# Primary:
-
-# Secondary:
-dtrainor@chromium.org
-mdjones@chromium.org
-twellington@chromium.org
+file://components/messages/OWNERS
diff --git a/chrome/browser/ui/quick_answers/quick_answers_controller_impl.cc b/chrome/browser/ui/quick_answers/quick_answers_controller_impl.cc
index 9dad1b04..29b5efc 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_controller_impl.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_controller_impl.cc
@@ -274,7 +274,7 @@
 }
 
 void QuickAnswersControllerImpl::MaybeDismissQuickAnswersConsent() {
-  if (quick_answers_ui_controller_->is_showing_user_consent_view())
+  if (quick_answers_ui_controller_->IsShowingUserConsentView())
     QuickAnswersState::Get()->OnConsentResult(ConsentResultType::kDismiss);
   quick_answers_ui_controller_->CloseUserConsentView();
 }
@@ -283,7 +283,7 @@
     const std::u16string& intent_type,
     const std::u16string& intent_text) {
   // Show consent informing user about the feature if required.
-  if (!quick_answers_ui_controller_->is_showing_user_consent_view()) {
+  if (!quick_answers_ui_controller_->IsShowingUserConsentView()) {
     quick_answers_ui_controller_->CreateUserConsentView(
         anchor_bounds_, intent_type, intent_text);
     QuickAnswersState::Get()->StartConsent();
diff --git a/chrome/browser/ui/quick_answers/quick_answers_controller_unittest.cc b/chrome/browser/ui/quick_answers/quick_answers_controller_unittest.cc
index d43034aa..655846ce 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_controller_unittest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_controller_unittest.cc
@@ -81,14 +81,14 @@
     return static_cast<QuickAnswersControllerImpl*>(
                QuickAnswersController::Get())
         ->quick_answers_ui_controller()
-        ->quick_answers_view_for_testing();
+        ->quick_answers_view();
   }
 
   const views::View* GetConsentView() const {
     return static_cast<QuickAnswersControllerImpl*>(
                QuickAnswersController::Get())
         ->quick_answers_ui_controller()
-        ->consent_view_for_testing();
+        ->user_consent_view();
   }
 
   void AcceptConsent() {
@@ -120,16 +120,16 @@
   ShowView();
 
   // The feature is not eligible, nothing should be shown.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest, ShouldNotShowWithoutSetPending) {
   ShowView(/*set_visibility=*/false);
 
   // The visibility has not been set to pending, nothing should be shown.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
   EXPECT_EQ(controller()->GetVisibilityForTesting(),
             QuickAnswersVisibility::kClosed);
 }
@@ -138,28 +138,28 @@
        ShouldShowPendingQueryAfterUserAcceptsConsent) {
   ShowView();
   // Without user consent, only the user consent view should show.
-  EXPECT_TRUE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_TRUE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 
   controller()->OnUserConsentResult(true);
 
   // With user consent granted, the consent view should dismiss and the cached
   // quick answer query should show.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_TRUE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_TRUE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest, ShouldDismissIfUserRejectConsent) {
   ShowView();
   // Without user consent, only the user consent view should show.
-  EXPECT_TRUE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_TRUE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 
   controller()->OnUserConsentResult(false);
 
   // With user consent rejected, the views should dismiss.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest, UserConsentAlreadyAccepted) {
@@ -168,8 +168,8 @@
 
   // With user consent already accepted, only the quick answers view should
   // show.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_TRUE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_TRUE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest, UserConsentAlreadyRejected) {
@@ -177,26 +177,26 @@
   ShowView();
 
   // With user consent already rejected, nothing should show.
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest, DismissUserConsentView) {
   ShowConsentView();
-  EXPECT_TRUE(ui_controller()->is_showing_user_consent_view());
+  EXPECT_TRUE(ui_controller()->IsShowingUserConsentView());
 
   DismissQuickAnswers();
 
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
 }
 
 TEST_F(QuickAnswersControllerTest, DismissQuickAnswersView) {
   AcceptConsent();
   ShowView();
-  EXPECT_TRUE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_TRUE(ui_controller()->IsShowingQuickAnswersView());
 
   DismissQuickAnswers();
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersControllerTest,
diff --git a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
index 5a117a8..f2709d12 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc
@@ -8,8 +8,6 @@
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/quick_answers/quick_answers_controller_impl.h"
-#include "chrome/browser/ui/quick_answers/ui/quick_answers_view.h"
-#include "chrome/browser/ui/quick_answers/ui/user_consent_view.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -34,10 +32,7 @@
     QuickAnswersControllerImpl* controller)
     : controller_(controller) {}
 
-QuickAnswersUiController::~QuickAnswersUiController() {
-  quick_answers_view_ = nullptr;
-  user_consent_view_ = nullptr;
-}
+QuickAnswersUiController::~QuickAnswersUiController() = default;
 
 void QuickAnswersUiController::CreateQuickAnswersView(const gfx::Rect& bounds,
                                                       const std::string& title,
@@ -46,16 +41,19 @@
   // Currently there are timing issues that causes the quick answers view is not
   // dismissed. TODO(updowndota): Remove the special handling after the root
   // cause is found.
-  if (quick_answers_view_) {
+  if (IsShowingQuickAnswersView()) {
     LOG(ERROR) << "Quick answers view not dismissed.";
     CloseQuickAnswersView();
   }
 
-  DCHECK(!user_consent_view_);
+  DCHECK(!IsShowingUserConsentView());
   SetActiveQuery(query);
-  quick_answers_view_ = new QuickAnswersView(bounds, title, is_internal,
-                                             weak_factory_.GetWeakPtr());
-  quick_answers_view_->GetWidget()->ShowInactive();
+
+  // Owned by view hierarchy.
+  auto* const quick_answers_view = new QuickAnswersView(
+      bounds, title, is_internal, weak_factory_.GetWeakPtr());
+  quick_answers_view_tracker_.SetView(quick_answers_view);
+  quick_answers_view->GetWidget()->ShowInactive();
 }
 
 void QuickAnswersUiController::OnQuickAnswersViewPressed() {
@@ -70,9 +68,8 @@
 }
 
 bool QuickAnswersUiController::CloseQuickAnswersView() {
-  if (quick_answers_view_) {
-    quick_answers_view_->GetWidget()->Close();
-    quick_answers_view_ = nullptr;
+  if (IsShowingQuickAnswersView()) {
+    quick_answers_view()->GetWidget()->Close();
     return true;
   }
   return false;
@@ -85,12 +82,12 @@
 void QuickAnswersUiController::RenderQuickAnswersViewWithResult(
     const gfx::Rect& anchor_bounds,
     const QuickAnswer& quick_answer) {
-  if (!quick_answers_view_)
+  if (!IsShowingQuickAnswersView())
     return;
 
   // QuickAnswersView was initiated with a loading page and will be updated
   // when quick answers result from server side is ready.
-  quick_answers_view_->UpdateView(anchor_bounds, quick_answer);
+  quick_answers_view()->UpdateView(anchor_bounds, quick_answer);
 }
 
 void QuickAnswersUiController::SetActiveQuery(const std::string& query) {
@@ -98,36 +95,38 @@
 }
 
 void QuickAnswersUiController::ShowRetry() {
-  if (!quick_answers_view_)
+  if (!IsShowingQuickAnswersView())
     return;
 
-  quick_answers_view_->ShowRetryView();
+  quick_answers_view()->ShowRetryView();
 }
 
 void QuickAnswersUiController::UpdateQuickAnswersBounds(
     const gfx::Rect& anchor_bounds) {
-  if (quick_answers_view_)
-    quick_answers_view_->UpdateAnchorViewBounds(anchor_bounds);
+  if (IsShowingQuickAnswersView())
+    quick_answers_view()->UpdateAnchorViewBounds(anchor_bounds);
 
-  if (user_consent_view_)
-    user_consent_view_->UpdateAnchorViewBounds(anchor_bounds);
+  if (IsShowingUserConsentView())
+    user_consent_view()->UpdateAnchorViewBounds(anchor_bounds);
 }
 
 void QuickAnswersUiController::CreateUserConsentView(
     const gfx::Rect& anchor_bounds,
     const std::u16string& intent_type,
     const std::u16string& intent_text) {
-  DCHECK(!quick_answers_view_);
-  DCHECK(!user_consent_view_);
-  user_consent_view_ = new quick_answers::UserConsentView(
+  DCHECK(!IsShowingQuickAnswersView());
+  DCHECK(!IsShowingUserConsentView());
+
+  // Owned by view hierarchy.
+  auto* const user_consent_view = new quick_answers::UserConsentView(
       anchor_bounds, intent_type, intent_text, weak_factory_.GetWeakPtr());
-  user_consent_view_->GetWidget()->ShowInactive();
+  user_consent_view_tracker_.SetView(user_consent_view);
+  user_consent_view->GetWidget()->ShowInactive();
 }
 
 void QuickAnswersUiController::CloseUserConsentView() {
-  if (user_consent_view_) {
-    user_consent_view_->GetWidget()->Close();
-    user_consent_view_ = nullptr;
+  if (IsShowingUserConsentView()) {
+    user_consent_view()->GetWidget()->Close();
   }
 }
 
@@ -148,9 +147,19 @@
 }
 
 void QuickAnswersUiController::OnUserConsentResult(bool consented) {
-  DCHECK(user_consent_view_);
+  DCHECK(IsShowingUserConsentView());
   controller_->OnUserConsentResult(consented);
 
-  if (consented && quick_answers_view_)
-    quick_answers_view_->RequestFocus();
+  if (consented && IsShowingQuickAnswersView())
+    quick_answers_view()->RequestFocus();
+}
+
+bool QuickAnswersUiController::IsShowingUserConsentView() const {
+  return user_consent_view_tracker_.view() &&
+         !user_consent_view_tracker_.view()->GetWidget()->IsClosed();
+}
+
+bool QuickAnswersUiController::IsShowingQuickAnswersView() const {
+  return quick_answers_view_tracker_.view() &&
+         !quick_answers_view_tracker_.view()->GetWidget()->IsClosed();
 }
diff --git a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.h b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.h
index 26907dda..e904959 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.h
+++ b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.h
@@ -8,7 +8,11 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/quick_answers/ui/quick_answers_view.h"
+#include "chrome/browser/ui/quick_answers/ui/user_consent_view.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/views/view_tracker.h"
+#include "ui/views/widget/widget.h"
 
 class QuickAnswersView;
 class QuickAnswersControllerImpl;
@@ -62,18 +66,6 @@
   // Closes the user consent view.
   void CloseUserConsentView();
 
-  // Used by the controller to check if the user consent view is currently
-  // showing instead of QuickAnswers.
-  bool is_showing_user_consent_view() const {
-    return user_consent_view_ != nullptr;
-  }
-
-  // Used by the controller to check if the QuickAnswers view is currently
-  // showing.
-  bool is_showing_quick_answers_view() const {
-    return quick_answers_view_ != nullptr;
-  }
-
   // Invoked when user clicks the Dogfood button on Quick-Answers related views.
   void OnDogfoodButtonPressed();
 
@@ -87,19 +79,29 @@
   // Handle consent result from user consent view.
   void OnUserConsentResult(bool consented);
 
-  const QuickAnswersView* quick_answers_view_for_testing() const {
-    return quick_answers_view_;
+  // Used by the controller to check if the user consent view is currently
+  // showing instead of QuickAnswers.
+  bool IsShowingUserConsentView() const;
+
+  // Used by the controller to check if the QuickAnswers view is currently
+  // showing.
+  bool IsShowingQuickAnswersView() const;
+
+  QuickAnswersView* quick_answers_view() {
+    return static_cast<QuickAnswersView*>(quick_answers_view_tracker_.view());
   }
-  const quick_answers::UserConsentView* consent_view_for_testing() const {
-    return user_consent_view_;
+  quick_answers::UserConsentView* user_consent_view() {
+    return static_cast<quick_answers::UserConsentView*>(
+        user_consent_view_tracker_.view());
   }
 
  private:
   QuickAnswersControllerImpl* controller_ = nullptr;
 
-  // Owned by view hierarchy.
-  QuickAnswersView* quick_answers_view_ = nullptr;
-  quick_answers::UserConsentView* user_consent_view_ = nullptr;
+  // Trackers for quick answers and user consent view.
+  views::ViewTracker quick_answers_view_tracker_;
+  views::ViewTracker user_consent_view_tracker_;
+
   std::string query_;
 
   base::WeakPtrFactory<QuickAnswersUiController> weak_factory_{this};
diff --git a/chrome/browser/ui/quick_answers/quick_answers_ui_controller_unittest.cc b/chrome/browser/ui/quick_answers/quick_answers_ui_controller_unittest.cc
index d55f93a..bde82ca0 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_ui_controller_unittest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_ui_controller_unittest.cc
@@ -40,7 +40,7 @@
 };
 
 TEST_F(QuickAnswersUiControllerTest, TearDownWhileQuickAnswersViewShowing) {
-  EXPECT_FALSE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_FALSE(ui_controller()->IsShowingQuickAnswersView());
 
   // Set up a companion menu before creating the QuickAnswersView.
   CreateAndShowBasicMenu();
@@ -48,16 +48,16 @@
   ui_controller()->CreateQuickAnswersView(kDefaultAnchorBoundsInScreen,
                                           "default_title", "default_query",
                                           /*is_internal=*/false);
-  EXPECT_TRUE(ui_controller()->is_showing_quick_answers_view());
+  EXPECT_TRUE(ui_controller()->IsShowingQuickAnswersView());
 }
 
 TEST_F(QuickAnswersUiControllerTest, TearDownWhileConsentViewShowing) {
-  EXPECT_FALSE(ui_controller()->is_showing_user_consent_view());
+  EXPECT_FALSE(ui_controller()->IsShowingUserConsentView());
 
   // Set up a companion menu before creating the QuickAnswersView.
   CreateAndShowBasicMenu();
 
   ui_controller()->CreateUserConsentView(kDefaultAnchorBoundsInScreen,
                                          std::u16string(), std::u16string());
-  EXPECT_TRUE(ui_controller()->is_showing_user_consent_view());
+  EXPECT_TRUE(ui_controller()->IsShowingUserConsentView());
 }
diff --git a/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc b/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
index 0cdc5a0..48a73b4 100644
--- a/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/borealis/borealis_app_launcher.h"
@@ -55,7 +56,8 @@
 class BorealisInstallerViewBrowserTest : public DialogBrowserTest {
  public:
   BorealisInstallerViewBrowserTest() {
-    feature_list_.InitAndEnableFeature(features::kBorealis);
+    feature_list_.InitWithFeatures(
+        {features::kBorealis, chromeos::features::kBorealisPermitted}, {});
   }
 
   // DialogBrowserTest:
diff --git a/chrome/browser/ui/views/borealis/borealis_splash_screen_view_browsertest.cc b/chrome/browser/ui/views/borealis/borealis_splash_screen_view_browsertest.cc
index c086341..75913a3c 100644
--- a/chrome/browser/ui/views/borealis/borealis_splash_screen_view_browsertest.cc
+++ b/chrome/browser/ui/views/borealis/borealis_splash_screen_view_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/borealis/borealis_splash_screen_view.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/borealis/borealis_app_launcher.h"
@@ -57,7 +58,8 @@
 class BorealisSplashScreenViewBrowserTest : public DialogBrowserTest {
  public:
   BorealisSplashScreenViewBrowserTest() {
-    feature_list_.InitAndEnableFeature(features::kBorealis);
+    feature_list_.InitWithFeatures(
+        {features::kBorealis, chromeos::features::kBorealisPermitted}, {});
   }
 
   // DialogBrowserTest:
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index d069e6f2..70765e8 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -24,30 +24,13 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/button/button_controller.h"
+#include "ui/views/controls/progress_ring_utils.h"
 #include "ui/views/layout/layout_provider.h"
 
 namespace {
 
 constexpr int kProgressRingRadius = 7;
-
-// TODO(crbug.com/1282240): Move this helper function into ui/views/controls/
-// so it can be used by ring_progress_bar too.
-void DrawRing(gfx::Canvas* canvas,
-              const gfx::RectF& bounds,
-              SkColor color,
-              SkScalar sweep_angle) {
-  SkPath path;
-  path.addArc(gfx::RectFToSkRect(bounds), /*startAngle=*/-90,
-              /*sweepAngle=*/sweep_angle);
-
-  cc::PaintFlags flags;
-  flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
-  flags.setAntiAlias(true);
-  flags.setColor(color);
-  flags.setStrokeWidth(1.7f);
-
-  canvas->DrawPath(std::move(path), std::move(flags));
-}
+constexpr float kProgressRingStrokeWidth = 1.7f;
 
 }  // namespace
 
@@ -89,15 +72,12 @@
   int diameter = 2 * kProgressRingRadius;
   gfx::RectF ring_bounds(x, y, /*width=*/diameter, /*height=*/diameter);
 
-  // Draw the background ring that gets progressively filled.
-  DrawRing(
-      canvas, ring_bounds,
+  views::DrawProgressRing(
+      canvas, gfx::RectFToSkRect(ring_bounds),
       GetColorProvider()->GetColor(kColorDownloadToolbarButtonRingBackground),
-      /*sweep_angle=*/360);
-  // Draw the filled portion of the progress ring.
-  DrawRing(canvas, ring_bounds,
-           GetColorProvider()->GetColor(kColorDownloadToolbarButtonActive),
-           /*sweep_angle=*/360 * progress_info.progress_percentage / 100.0);
+      GetColorProvider()->GetColor(kColorDownloadToolbarButtonActive),
+      kProgressRingStrokeWidth, /*start_angle=*/-90,
+      /*sweep_angle=*/360 * progress_info.progress_percentage / 100.0);
 }
 
 void DownloadToolbarButtonView::Show() {
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 026ff21..69dfa56 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -95,6 +95,7 @@
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/progress_ring_utils.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/vector_icons.h"
@@ -1062,13 +1063,6 @@
     int percent_done) const {
   const SkColor color = GetColorProvider()->GetColor(ui::kColorThrobber);
 
-  // Draw background.
-  cc::PaintFlags bg_flags;
-  bg_flags.setColor(SkColorSetA(color, 0x33));
-  bg_flags.setStyle(cc::PaintFlags::kFill_Style);
-  bg_flags.setAntiAlias(true);
-  canvas->DrawCircle(bounds.CenterPoint(), bounds.width() / 2, bg_flags);
-
   // Calculate progress.
   SkScalar start_pos = SkIntToScalar(270);  // 12 o'clock
   SkScalar sweep_angle = SkDoubleToScalar(360 * percent_done / 100.0);
@@ -1080,15 +1074,9 @@
     sweep_angle = SkIntToScalar(50);
   }
 
-  // Draw progress.
-  SkPath progress;
-  progress.addArc(gfx::RectFToSkRect(bounds), start_pos, sweep_angle);
-  cc::PaintFlags progress_flags;
-  progress_flags.setColor(color);
-  progress_flags.setStyle(cc::PaintFlags::kStroke_Style);
-  progress_flags.setStrokeWidth(1.7f);
-  progress_flags.setAntiAlias(true);
-  canvas->DrawPath(progress, progress_flags);
+  views::DrawProgressRing(canvas, gfx::RectFToSkRect(bounds),
+                          SkColorSetA(color, 0x33), color,
+                          /*stroke_width=*/1.7f, start_pos, sweep_angle);
 }
 
 ui::ImageModel DownloadItemView::GetIcon() const {
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index a8abae0..10ef4e6 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -201,6 +201,10 @@
     return side_panel_coordinator_.get();
   }
 
+  SidePanelRegistry* global_side_panel_registry() {
+    return global_side_panel_registry_.get();
+  }
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   lens::LensSidePanelController* lens_side_panel_controller() {
     return lens_side_panel_controller_.get();
diff --git a/chrome/browser/ui/views/overlay/document_overlay_window_views_unittest.cc b/chrome/browser/ui/views/overlay/document_overlay_window_views_unittest.cc
index bae51b87..ec58249 100644
--- a/chrome/browser/ui/views/overlay/document_overlay_window_views_unittest.cc
+++ b/chrome/browser/ui/views/overlay/document_overlay_window_views_unittest.cc
@@ -315,13 +315,11 @@
 
   // If the maximum size increases then we should keep the existing window size.
   SetDisplayWorkArea({0, 0, 8000, 8000});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(1200, 800), overlay_window().GetBounds().size());
   EXPECT_EQ(gfx::Size(4000, 4000), overlay_window().GetMaximumSize());
 
   // If the maximum size decreases then we should shrink to fit.
   SetDisplayWorkArea({0, 0, 1000, 2000});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(500, 800), overlay_window().GetBounds().size());
   EXPECT_EQ(gfx::Size(500, 1000), overlay_window().GetMaximumSize());
 }
@@ -330,7 +328,6 @@
   ASSERT_EQ(gfx::Size(500, 500), overlay_window().GetMaximumSize());
 
   SetDisplayWorkArea({0, 0, 0, 0});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(500, 500), overlay_window().GetMaximumSize());
 }
 
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index d96a493..850b34e3 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -185,9 +185,13 @@
           base::Milliseconds(2500),
           base::BindRepeating(&OverlayWindowViews::UpdateControlsVisibility,
                               base::Unretained(this),
-                              false /* is_visible */)) {}
+                              false /* is_visible */)) {
+  display::Screen::GetScreen()->AddObserver(this);
+}
 
-OverlayWindowViews::~OverlayWindowViews() = default;
+OverlayWindowViews::~OverlayWindowViews() {
+  display::Screen::GetScreen()->RemoveObserver(this);
+}
 
 gfx::Size& OverlayWindowViews::GetNaturalSize() {
   return natural_size_;
@@ -492,6 +496,19 @@
          update_controls_bounds_timer_->IsRunning();
 }
 
+void OverlayWindowViews::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  // Some display metric changes, such as display scaling, can affect the work
+  // area, so max size needs to be updated.
+  if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_WORK_AREA &&
+      display.id() == display::Screen::GetScreen()
+                          ->GetDisplayNearestWindow(GetNativeWindow())
+                          .id()) {
+    UpdateMaxSize(GetWorkAreaForWindow());
+  }
+}
+
 gfx::Rect OverlayWindowViews::GetWorkAreaForWindow() const {
   return display::Screen::GetScreen()
       ->GetDisplayNearestWindow(
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index 1bb1423..b2ed2e7 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -11,6 +11,8 @@
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/video_picture_in_picture_window_controller.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/display/display.h"
+#include "ui/display/display_observer.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/widget/widget.h"
 
@@ -20,7 +22,8 @@
 //
 // This class is a views::Widget. The subclasses implement the needed
 // methods for their corresponding OverlayWindow subclass.
-class OverlayWindowViews : public views::Widget {
+class OverlayWindowViews : public views::Widget,
+                           public display::DisplayObserver {
  public:
   OverlayWindowViews(const OverlayWindowViews&) = delete;
   OverlayWindowViews& operator=(const OverlayWindowViews&) = delete;
@@ -99,6 +102,10 @@
     min_size_ = min_size;
   }
 
+  // display::DisplayObserver
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override;
+
  protected:
   OverlayWindowViews();
 
diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views_unittest.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views_unittest.cc
index a225051..4a8938d4 100644
--- a/chrome/browser/ui/views/overlay/video_overlay_window_views_unittest.cc
+++ b/chrome/browser/ui/views/overlay/video_overlay_window_views_unittest.cc
@@ -274,13 +274,11 @@
 
   // If the maximum size increases then we should keep the existing window size.
   SetDisplayWorkArea({0, 0, 8000, 8000});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(1200, 800), overlay_window().GetBounds().size());
   EXPECT_EQ(gfx::Size(4000, 4000), overlay_window().GetMaximumSize());
 
   // If the maximum size decreases then we should shrink to fit.
   SetDisplayWorkArea({0, 0, 1000, 2000});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(500, 800), overlay_window().GetBounds().size());
   EXPECT_EQ(gfx::Size(500, 1000), overlay_window().GetMaximumSize());
 }
@@ -289,7 +287,6 @@
   ASSERT_EQ(gfx::Size(500, 500), overlay_window().GetMaximumSize());
 
   SetDisplayWorkArea({0, 0, 0, 0});
-  overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(500, 500), overlay_window().GetMaximumSize());
 }
 
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator_unittest.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator_unittest.cc
new file mode 100644
index 0000000..e0ef87ce
--- /dev/null
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
+
+#include "base/feature_list.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
+#include "chrome/browser/ui/views/side_panel/side_panel.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_entry.h"
+
+class SidePanelCoordinatorTest : public TestWithBrowserView {
+ public:
+  void SetUp() override {
+    base::test::ScopedFeatureList features;
+    features.InitWithFeatures(
+        {features::kSidePanel, features::kUnifiedSidePanel}, {});
+    TestWithBrowserView::SetUp();
+
+    // Create an active web contents.
+    AddTab(browser_view()->browser(), GURL("about:blank"));
+    coordinator_ = browser_view()->side_panel_coordinator();
+  }
+
+  absl::optional<SidePanelEntry::Id> GetLastActiveEntry() {
+    return browser_view()->global_side_panel_registry()->last_active_entry();
+  }
+
+ protected:
+  raw_ptr<SidePanelCoordinator> coordinator_;
+};
+
+TEST_F(SidePanelCoordinatorTest, ToggleSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  coordinator_->Toggle();
+  EXPECT_FALSE(browser_view()->right_aligned_side_panel()->GetVisible());
+}
+
+TEST_F(SidePanelCoordinatorTest, SwitchBetweenSidePanelEntries) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  coordinator_->Show(SidePanelEntry::Id::kBookmarks);
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+  EXPECT_TRUE(GetLastActiveEntry().has_value());
+  EXPECT_EQ(GetLastActiveEntry().value(), SidePanelEntry::Id::kBookmarks);
+
+  coordinator_->Show(SidePanelEntry::Id::kReadingList);
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+  EXPECT_TRUE(GetLastActiveEntry().has_value());
+  EXPECT_EQ(GetLastActiveEntry().value(), SidePanelEntry::Id::kReadingList);
+}
diff --git a/chrome/browser/ui/views/user_education/OWNERS b/chrome/browser/ui/views/user_education/OWNERS
index 08d4f80..8878a22 100644
--- a/chrome/browser/ui/views/user_education/OWNERS
+++ b/chrome/browser/ui/views/user_education/OWNERS
@@ -2,6 +2,5 @@
 collinbaker@chromium.org
 
 # Backup
-bdea@chromium.org
 robliao@chromium.org
-dpenning@chromium.org
\ No newline at end of file
+dpenning@chromium.org
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index eb6cca8..7ab14ba 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -13,6 +13,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
@@ -47,6 +48,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/fenced_frame_test_util.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/theme_change_waiter.h"
 #include "extensions/test/test_extension_dir.h"
@@ -464,14 +466,22 @@
         browser(), std::move(web_app_info), start_url);
   }
 
-  void ToggleWindowControlsOverlayAndWait() {
-    auto* web_contents = helper()->browser_view()->GetActiveWebContents();
+  void ToggleWindowControlsOverlayAndWaitHelper(
+      content::WebContents* web_contents,
+      BrowserView* browser_view) {
     helper()->SetupGeometryChangeCallback(web_contents);
     content::TitleWatcher title_watcher(web_contents, u"ongeometrychange");
-    helper()->browser_view()->ToggleWindowControlsOverlayEnabled();
+    browser_view->ToggleWindowControlsOverlayEnabled();
     std::ignore = title_watcher.WaitAndGetTitle();
   }
 
+  // When toggling the WCO app initialized by the helper class.
+  void ToggleWindowControlsOverlayAndWait() {
+    ToggleWindowControlsOverlayAndWaitHelper(
+        helper()->browser_view()->GetActiveWebContents(),
+        helper()->browser_view());
+  }
+
   bool GetWindowControlOverlayVisibility() {
     auto* web_contents = helper()->browser_view()->GetActiveWebContents();
     return EvalJs(web_contents,
@@ -549,6 +559,24 @@
         "rect");
   }
 
+  // Opens a new popup window from |web_contents| on |target_url| and returns
+  // the Browser it opened in.
+  Browser* OpenPopup(content::WebContents* web_contents,
+                     const std::string& target_url) {
+    GURL target_gurl(target_url);
+    content::TestNavigationObserver nav_observer(target_gurl);
+    nav_observer.StartWatchingNewWebContents();
+
+    std::string script =
+        "window.open('" + target_url + "', '_blank', 'popup');";
+    EXPECT_TRUE(content::ExecuteScript(web_contents, script));
+    nav_observer.Wait();
+
+    Browser* popup = chrome::FindLastActive();
+    EXPECT_TRUE(popup);
+    return popup;
+  }
+
  protected:
   content::test::FencedFrameTestHelper fenced_frame_helper_;
 
@@ -657,6 +685,64 @@
   EXPECT_EQ(u"onresize", title_watcher2.WaitAndGetTitle());
 }
 
+// Test to ensure crbug.com/1298226 won't reproduce.
+IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
+                       PopupFromWcoAppToItself) {
+  InstallAndLaunchWebApp();
+  auto* wco_web_contents = helper()->browser_view()->GetActiveWebContents();
+
+  Browser* popup = OpenPopup(
+      wco_web_contents,
+      EvalJs(wco_web_contents, "window.location.href").ExtractString());
+
+  BrowserView* popup_browser_view =
+      BrowserView::GetBrowserViewForBrowser(popup);
+  content::WebContents* popup_web_contents =
+      popup_browser_view->GetActiveWebContents();
+
+  EXPECT_TRUE(
+      content::WaitForRenderFrameReady(popup_web_contents->GetMainFrame()));
+  EXPECT_FALSE(popup_browser_view->IsWindowControlsOverlayEnabled());
+  EXPECT_FALSE(EvalJs(popup_web_contents,
+                      "window.navigator.windowControlsOverlay.visible")
+                   .ExtractBool());
+
+  // When popup is opened (from a WCO app) pointing to itself, the popup also
+  // has WCO which can be toggled.
+  ToggleWindowControlsOverlayAndWaitHelper(popup_web_contents,
+                                           popup_browser_view);
+  EXPECT_TRUE(popup_browser_view->IsWindowControlsOverlayEnabled());
+  EXPECT_TRUE(EvalJs(popup_web_contents,
+                     "window.navigator.windowControlsOverlay.visible")
+                  .ExtractBool());
+}
+
+// Test to ensure crbug.com/1298237 won't reproduce.
+IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
+                       PopupFromWcoAppToAnyOtherWebsite) {
+  InstallAndLaunchWebApp();
+  // The initial WCO state doesn't matter, but to highlight that it's different,
+  // the script is run with the WCO initially toggled on.
+  ToggleWindowControlsOverlayAndWait();
+  EXPECT_TRUE(GetWindowControlOverlayVisibility());
+
+  Browser* popup = OpenPopup(helper()->browser_view()->GetActiveWebContents(),
+                             "https://google.com");
+  BrowserView* popup_browser_view =
+      BrowserView::GetBrowserViewForBrowser(popup);
+  content::WebContents* popup_web_contents =
+      popup_browser_view->GetActiveWebContents();
+  EXPECT_TRUE(
+      content::WaitForRenderFrameReady(popup_web_contents->GetMainFrame()));
+
+  // When popup is opened pointing to any other site, it will not know whether
+  // the popup app uses WCO or not. This test also ensures it does not crash.
+  EXPECT_FALSE(popup_browser_view->IsWindowControlsOverlayEnabled());
+  EXPECT_FALSE(EvalJs(popup_web_contents,
+                      "window.navigator.windowControlsOverlay.visible")
+                   .ExtractBool());
+}
+
 IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
                        WindowControlsOverlayRTL) {
   base::test::ScopedRestoreICUDefaultLocale test_locale("ar");
diff --git a/chrome/browser/ui/views/webauthn/ring_progress_bar.cc b/chrome/browser/ui/views/webauthn/ring_progress_bar.cc
index 8881a76..df7d65bb 100644
--- a/chrome/browser/ui/views/webauthn/ring_progress_bar.cc
+++ b/chrome/browser/ui/views/webauthn/ring_progress_bar.cc
@@ -15,6 +15,7 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/native_theme/native_theme.h"
+#include "ui/views/controls/progress_ring_utils.h"
 
 namespace {
 constexpr float kStrokeWidth = 4;
@@ -40,34 +41,19 @@
 
 void RingProgressBar::OnPaint(gfx::Canvas* canvas) {
   gfx::Rect content_bounds = GetContentsBounds();
-  // Draw the background ring that gets progressively filled.
-  gfx::PointF center(content_bounds.width() / 2, content_bounds.height() / 2);
   const float radius =
       (std::min(content_bounds.width(), content_bounds.height()) -
        kStrokeWidth) /
       2;
-  cc::PaintFlags background_flags;
-  background_flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
-  background_flags.setAntiAlias(true);
-  background_flags.setColor(kBackgroundColor);
-  background_flags.setStrokeWidth(kStrokeWidth);
-  canvas->DrawCircle(center, radius, std::move(background_flags));
 
-  // Draw the filled portion of the circle.
-  SkPath ring_path;
   SkRect bounds{/*fLeft=*/(content_bounds.width() / 2 - radius),
                 /*fTop=*/(content_bounds.height() / 2 - radius),
                 /*fRight=*/(content_bounds.width() / 2 + radius),
                 /*fBottom=*/(content_bounds.height() / 2 + radius)};
-  ring_path.addArc(
-      std::move(bounds), /*startAngle=*/-90,
-      /*sweepAngle=*/360 * animation_->CurrentValueBetween(initial_, target_));
-  cc::PaintFlags ring_flags;
-  ring_flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
-  ring_flags.setAntiAlias(true);
-  ring_flags.setColor(kRingColor);
-  ring_flags.setStrokeWidth(kStrokeWidth);
-  canvas->DrawPath(std::move(ring_path), std::move(ring_flags));
+  views::DrawProgressRing(
+      canvas, bounds, kBackgroundColor, kRingColor, kStrokeWidth,
+      /*start_angle=*/-90,
+      /*sweep_angle=*/360 * animation_->CurrentValueBetween(initial_, target_));
 }
 
 void RingProgressBar::AnimationProgressed(const gfx::Animation* animation) {
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
index 87ceb3a0..3079737 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/views/chrome_web_dialog_view.h"
 #include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/access_code_cast_resources.h"
 #include "chrome/grit/access_code_cast_resources_map.h"
@@ -213,12 +214,14 @@
       {"errorTooManyRequests", IDS_ACCESS_CODE_CAST_ERROR_TOO_MANY_REQUESTS},
       {"errorUnknown", IDS_ACCESS_CODE_CAST_ERROR_UNKNOWN},
       {"inputLabel", IDS_ACCESS_CODE_CAST_INPUT_ARIA_LABEL},
+      {"learnMore", IDS_LEARN_MORE},
       {"submit", IDS_ACCESS_CODE_CAST_SUBMIT},
       {"useCamera", IDS_ACCESS_CODE_CAST_USE_CAMERA},
   };
 
   source->AddLocalizedStrings(kStrings);
   source->AddBoolean("qrScannerEnabled", false);
+  source->AddString("learnMoreUrl", chrome::kAccessCodeCastLearnMoreURL);
 
   content::BrowserContext* browser_context =
       web_ui->GetWebContents()->GetBrowserContext();
diff --git a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
index 3de0c1ad..5c8a5e8 100644
--- a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
+++ b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
@@ -15,6 +15,7 @@
 #include "chrome/grit/app_settings_resources.h"
 #include "chrome/grit/app_settings_resources_map.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -26,6 +27,7 @@
 
 void AddAppManagementStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"close", IDS_CLOSE},
       {"title", IDS_WEB_APP_SETTINGS_TITLE},
       {"appManagementAppInstalledByPolicyLabel",
        IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
@@ -42,6 +44,8 @@
       {"appManagementRunOnOsLoginModeLabel",
        IDS_APP_MANAGEMENT_RUN_ON_OS_LOGIN},
       {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY},
+      {"fileHandlingOverflowDialogTitle",
+       IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE},
       {"fileHandlingSetDefaults",
        IDS_APP_MANAGEMENT_FILE_HANDLING_SET_DEFAULTS_LINK},
   };
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
index 2f68460..a4af147 100644
--- a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
@@ -74,6 +74,10 @@
     {"qrCodeRetry", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_RETRY},
     {"scanQrCodeLoading", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_LOADING},
     {"scanQrCodeInvalid", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INVALID},
+    {"scanQrCodeInputSubtitle",
+     IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_SUBTITLE},
+    {"scanQrCodeInputError",
+     IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_INPUT_ERROR},
     {"profileListPageMessage", IDS_CELLULAR_SETUP_PROFILE_LIST_PAGE_MESSAGE},
     {"eidPopupTitle", IDS_CELLULAR_SETUP_EID_POPUP_TITLE},
     {"eidPopupDescription", IDS_CELLULAR_SETUP_EID_POPUP_DESCRIPTION},
@@ -109,7 +113,9 @@
 
 const std::vector<const NamedResourceId>& GetResourceIdValues() {
   static const base::NoDestructor<std::vector<const NamedResourceId>>
-      named_resource_ids({{"spinner.json", IDR_LOGIN_SPINNER_ANIMATION}});
+      named_resource_ids(
+          {{"spinner.json", IDR_LOGIN_SPINNER_ANIMATION},
+           {"spinner_dark.json", IDR_LOGIN_SPINNER_DARK_ANIMATION}});
   return *named_resource_ids;
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
index 8740ab9..309f5bd 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -218,12 +218,6 @@
           base::Unretained(this)));
 
   web_ui()->RegisterDeprecatedMessageCallback(
-      "resetCameraRollOnboardingUiDismissed",
-      base::BindRepeating(&MultidevicePhoneHubHandler::
-                              HandleResetCameraRollOnboardingUiDismissed,
-                          base::Unretained(this)));
-
-  web_ui()->RegisterDeprecatedMessageCallback(
       "setFakeCameraRoll",
       base::BindRepeating(&MultidevicePhoneHubHandler::HandleSetFakeCameraRoll,
                           base::Unretained(this)));
@@ -301,13 +295,6 @@
   camera_roll_dict.SetBoolKey(
       "isCameraRollEnabled", fake_phone_hub_manager_->fake_camera_roll_manager()
                                  ->is_camera_roll_enabled());
-  camera_roll_dict.SetBoolKey(
-      "isOnboardingDismissed",
-      fake_phone_hub_manager_->fake_camera_roll_manager()
-          ->is_onboarding_dismissed());
-  camera_roll_dict.SetBoolKey(
-      "isLoadingViewShown", fake_phone_hub_manager_->fake_camera_roll_manager()
-                                ->is_loading_view_shown());
   FireWebUIListener("camera-roll-ui-view-state-updated", camera_roll_dict);
 }
 
@@ -610,14 +597,6 @@
   PA_LOG(VERBOSE) << "Reset kHasDismissedSetupRequiredUi pref";
 }
 
-void MultidevicePhoneHubHandler::HandleResetCameraRollOnboardingUiDismissed(
-    const base::ListValue* args) {
-  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
-  prefs->SetBoolean(
-      chromeos::phonehub::prefs::kHasDismissedCameraRollOnboardingUi, false);
-  PA_LOG(VERBOSE) << "Reset kHasDismissedCameraRollOnboardingUi pref";
-}
-
 void MultidevicePhoneHubHandler::HandleSetFakeCameraRoll(
     const base::ListValue* args) {
   const base::Value& camera_roll_dict = args->GetListDeprecated()[0];
@@ -630,13 +609,6 @@
   fake_phone_hub_manager_->fake_camera_roll_manager()
       ->SetIsCameraRollAvailableToBeEnabled(!*is_camera_roll_enabled);
 
-  absl::optional<bool> is_onboarding_dismissed =
-      camera_roll_dict.FindBoolKey("isOnboardingDismissed");
-  CHECK(is_onboarding_dismissed);
-
-  fake_phone_hub_manager_->fake_camera_roll_manager()
-      ->SetIsCameraRollOnboardingDismissed(*is_onboarding_dismissed);
-
   absl::optional<bool> is_file_access_granted =
       camera_roll_dict.FindBoolKey("isFileAccessGranted");
   CHECK(is_file_access_granted);
@@ -644,13 +616,6 @@
   fake_phone_hub_manager_->fake_camera_roll_manager()
       ->SetIsAndroidStorageGranted(*is_file_access_granted);
 
-  absl::optional<bool> is_loading_view_shown =
-      camera_roll_dict.FindBoolKey("isLoadingViewShown");
-  CHECK(is_loading_view_shown);
-
-  fake_phone_hub_manager_->fake_camera_roll_manager()
-      ->SetIsCameraRollLoadingViewShown(*is_loading_view_shown);
-
   absl::optional<int> number_of_thumbnails =
       camera_roll_dict.FindIntKey("numberOfThumbnails");
   CHECK(number_of_thumbnails);
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
index ed23ae4b..7bfb7b44 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h
@@ -76,7 +76,6 @@
   void HandleResetShouldShowOnboardingUi(const base::ListValue* args);
   void HandleResetHasMultideviceFeatureSetupUiBeenDismissed(
       const base::ListValue* args);
-  void HandleResetCameraRollOnboardingUiDismissed(const base::ListValue* args);
   void HandleSetFakeCameraRoll(const base::ListValue* args);
 
   void AddObservers();
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 0994e4ba..9679f756 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -111,6 +111,10 @@
     {"viewInStore", IDS_EXTENSIONS_ITEM_CHROME_WEB_STORE},
     {"extensionWebsite", IDS_EXTENSIONS_ITEM_EXTENSION_WEBSITE},
     {"dropToInstall", IDS_EXTENSIONS_INSTALL_DROP_TARGET},
+    {"editSitePermissionsAllowAllExtensions",
+     IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS},
+    {"editSitePermissionsRestrictExtensions",
+     IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS},
     {"errorsPageHeading", IDS_EXTENSIONS_ERROR_PAGE_HEADING},
     {"clearActivities", IDS_EXTENSIONS_CLEAR_ACTIVITIES},
     {"clearAll", IDS_EXTENSIONS_ERROR_CLEAR_ALL},
@@ -259,6 +263,11 @@
      IDS_EXTENSIONS_SITE_PERMISSIONS_DIALOG_INPUT_ERROR},
     {"sitePermissionsDialogInputLabel",
      IDS_EXTENSIONS_SITE_PERMISSIONS_DIALOG_INPUT_LABEL},
+    {"sitePermissionsEditPermissions",
+     IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS},
+    {"sitePermissionsEditPermissionsDialogTitle",
+     IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE},
+    {"sitePermissionsEditUrl", IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL},
     {"sitePermissionsViewAllSites",
      IDS_EXTENSIONS_SITE_PERMISSIONS_VIEW_ALL_SITES},
     {"permittedSites", IDS_EXTENSIONS_PERMITTED_SITES},
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 648aec2..b779cab 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service_factory.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/webui/browser_command/browser_command_handler.h"
 #include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
@@ -691,21 +690,21 @@
 
 void NewTabPageUI::OnThemeChanged() {
   std::unique_ptr<base::DictionaryValue> update(new base::DictionaryValue);
-  const Browser* browser = chrome::FindBrowserWithProfile(profile_);
-  DCHECK(browser);
-  const ui::ThemeProvider* theme_provider =
-      browser->window()->GetThemeProvider();
 
-  // `theme_provider` will be nullptr in unit tests. If you want to test NTP
-  // color, use webui::SetThemeProviderForTesting().
+  const ui::ThemeProvider* theme_provider =
+      webui::GetThemeProvider(web_contents_);
+  // TODO(crbug.com/1299925): Always mock theme provider in tests so that
+  // `theme_provider` is never nullptr.
   if (theme_provider) {
     auto background_color =
         theme_provider->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
     update->SetStringKey("backgroundColor",
                          skia::SkColorToHexString(background_color));
-    content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
-                                     std::move(update));
+  } else {
+    update->SetStringKey("backgroundColor", "");
   }
+  content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
+                                   std::move(update));
 }
 
 void NewTabPageUI::OnCustomBackgroundImageUpdated() {
diff --git a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
index 9757f9b6..77c62014 100644
--- a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
@@ -50,23 +50,25 @@
 void NewTabPageThirdPartyHandler::NotifyAboutTheme() {
   auto theme = new_tab_page_third_party::mojom::Theme::New();
   auto most_visited = most_visited::mojom::MostVisitedTheme::New();
-  const ui::ThemeProvider& theme_provider =
-      ThemeService::GetThemeProviderForProfile(profile_);
+  const ui::ThemeProvider* theme_provider =
+      webui::GetThemeProvider(web_contents_);
+  DCHECK(theme_provider);
   most_visited->background_color =
-      theme_provider.GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
+      theme_provider->GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
   most_visited->use_white_tile_icon =
       color_utils::IsDark(most_visited->background_color);
   most_visited->use_title_pill = false;
-  theme->text_color = theme_provider.GetColor(ThemeProperties::COLOR_NTP_TEXT);
+  theme->text_color = theme_provider->GetColor(ThemeProperties::COLOR_NTP_TEXT);
   most_visited->is_dark = !color_utils::IsDark(theme->text_color);
   theme->color_background = color_utils::SkColorToRgbaString(
-      GetThemeColor(webui::GetNativeTheme(web_contents_), theme_provider,
+      GetThemeColor(webui::GetNativeTheme(web_contents_), *theme_provider,
                     ThemeProperties::COLOR_NTP_BACKGROUND));
-  if (theme_provider.HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
-    theme->background_tiling = GetNewTabBackgroundTilingCSS(theme_provider);
-    theme->background_position = GetNewTabBackgroundPositionCSS(theme_provider);
+  if (theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
+    theme->background_tiling = GetNewTabBackgroundTilingCSS(*theme_provider);
+    theme->background_position =
+        GetNewTabBackgroundPositionCSS(*theme_provider);
     theme->has_custom_background =
-        theme_provider.HasCustomImage(IDR_THEME_NTP_BACKGROUND);
+        theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND);
     theme->id = profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
     most_visited->use_title_pill = true;
   }
diff --git a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
index 1d42d5c..a69f085 100644
--- a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
@@ -61,26 +61,39 @@
 
   source->AddLocalizedStrings(kStrings);
 
-  const ui::ThemeProvider& theme_provider =
-      ThemeService::GetThemeProviderForProfile(profile);
-  source->AddString("backgroundPosition",
-                    GetNewTabBackgroundPositionCSS(theme_provider));
-  source->AddString("backgroundTiling",
-                    GetNewTabBackgroundTilingCSS(theme_provider));
-  source->AddString("colorBackground",
-                    color_utils::SkColorToRgbaString(GetThemeColor(
-                        webui::GetNativeTheme(web_contents), theme_provider,
-                        ThemeProperties::COLOR_NTP_BACKGROUND)));
-  source->AddString("themeId",
-                    profile->GetPrefs()->GetString(prefs::kCurrentThemeID));
-  source->AddString("hascustombackground",
-                    theme_provider.HasCustomImage(IDR_THEME_NTP_BACKGROUND)
-                        ? "has-custom-background"
-                        : "");
-  source->AddString("isdark", !color_utils::IsDark(theme_provider.GetColor(
-                                  ThemeProperties::COLOR_NTP_TEXT))
-                                  ? "dark"
-                                  : "");
+  const ui::ThemeProvider* theme_provider =
+      webui::GetThemeProvider(web_contents);
+  // TODO(crbug.com/1299925): Always mock theme provider in tests so that
+  // `theme_provider` is never nullptr.
+  if (theme_provider) {
+    source->AddString("backgroundPosition",
+                      GetNewTabBackgroundPositionCSS(*theme_provider));
+    source->AddString("backgroundTiling",
+                      GetNewTabBackgroundTilingCSS(*theme_provider));
+    source->AddString("colorBackground",
+                      color_utils::SkColorToRgbaString(GetThemeColor(
+                          webui::GetNativeTheme(web_contents), *theme_provider,
+                          ThemeProperties::COLOR_NTP_BACKGROUND)));
+    // TODO(crbug.com/1056758): don't get theme id from profile.
+    source->AddString("themeId",
+                      profile->GetPrefs()->GetString(prefs::kCurrentThemeID));
+    source->AddString("hascustombackground",
+                      theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)
+                          ? "has-custom-background"
+                          : "");
+    source->AddString("isdark", !color_utils::IsDark(theme_provider->GetColor(
+                                    ThemeProperties::COLOR_NTP_TEXT))
+                                    ? "dark"
+                                    : "");
+  } else {
+    source->AddString("backgroundPosition", "");
+    source->AddString("backgroundTiling", "");
+    source->AddString("colorBackground", "");
+    source->AddString("themeId", "");
+    source->AddString("hascustombackground", "");
+    source->AddString("isdark", "");
+  }
+
   source->AddBoolean(
       "handleMostVisitedNavigationExplicitly",
       base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/webui/print_preview/data_request_filter.h b/chrome/browser/ui/webui/print_preview/data_request_filter.h
index 6a322919..a2c1d55a 100644
--- a/chrome/browser/ui/webui/print_preview/data_request_filter.h
+++ b/chrome/browser/ui/webui/print_preview/data_request_filter.h
@@ -28,15 +28,13 @@
 // Adds a request filter for serving preview PDF data.
 void AddDataRequestFilter(content::WebUIDataSource& source);
 
-// Parses a preview PDF data path (i.e., what comes after chrome://print/ or
+// Parses a preview PDF data path (i.e., what comes after
 // chrome-untrusted://print/).
 //
 // The format for requesting preview PDF data is as follows:
-//   chrome://print/<ui_id>/<page_index>/print.pdf
 //   chrome-untrusted://print/<ui_id>/<page_index>/print.pdf
 //
 // Example:
-//   chrome://print/123/10/print.pdf
 //   chrome-untrusted://print/123/10/print.pdf
 absl::optional<PrintPreviewIdAndPageIndex> ParseDataPath(
     const std::string& path);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 467ca270..c99f8ed 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/metrics_handler.h"
 #include "chrome/browser/ui/webui/plural_string_handler.h"
-#include "chrome/browser/ui/webui/print_preview/data_request_filter.h"
 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
 #include "chrome/browser/ui/webui/theme_source.h"
 #include "chrome/browser/ui/webui/webui_util.h"
@@ -370,9 +369,6 @@
 }
 
 void SetupPrintPreviewPlugin(content::WebUIDataSource* source) {
-  // TODO(crbug.com/1238829): Only serve PDF from chrome-untrusted://print. The
-  // legacy Pepper-based PDF plugin still requires this.
-  AddDataRequestFilter(*source);
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ChildSrc,
       "child-src 'self' chrome-untrusted://print;");
diff --git a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
index 4774000..550d847e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
@@ -181,6 +181,16 @@
        IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_WEB},
       {"appManagementAppDetailsTypeSystem",
        IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_SYSTEM},
+      {"appManagementAppDetailsTypeCrosSystem",
+       IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM},
+      {"appManagementAppDetailsInstallSourceWebStore",
+       IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_WEB_STORE},
+      {"appManagementAppDetailsInstallSourcePlayStore",
+       IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_PLAY_STORE},
+      {"appManagementAppDetailsInstallSourceBrowser",
+       IDS_APP_MANAGEMENT_APP_DETAILS_INSTALL_SOURCE_BROWSER},
+      {"appManagementAppDetailsTypeAndSourceCombined",
+       IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_AND_SOURCE_COMBINED},
       {"appManagementAppInstalledByPolicyLabel",
        IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
       {"appManagementCameraPermissionLabel", IDS_APP_MANAGEMENT_CAMERA},
diff --git a/chrome/browser/ui/webui/webui_util.cc b/chrome/browser/ui/webui/webui_util.cc
index 48f0b34..d265c95 100644
--- a/chrome/browser/ui/webui/webui_util.cc
+++ b/chrome/browser/ui/webui/webui_util.cc
@@ -26,6 +26,7 @@
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/widget/widget.h"
@@ -119,7 +120,20 @@
 
   auto* browser_window =
       BrowserWindow::FindBrowserWindowWithWebContents(web_contents);
-  return browser_window ? browser_window->GetThemeProvider() : nullptr;
+
+  if (browser_window)
+    return browser_window->GetThemeProvider();
+
+  // Fallback: get the theme provider from the last created browser.
+  // This is used in newly created tabs, e.g. NewTabPageUI. They need access to
+  // the theme browser before the WebContents is attached to a browser window.
+  // TODO(crbug.com/1298767): Remove this fallback by associating the
+  // WebContents during navigation.
+  BrowserList* browser_list = BrowserList::GetInstance();
+  const Browser* browser = browser_list->empty()
+                               ? nullptr
+                               : *std::prev(BrowserList::GetInstance()->end());
+  return browser ? browser->window()->GetThemeProvider() : nullptr;
 }
 
 void SetThemeProviderForTesting(const ui::ThemeProvider* theme_provider) {
diff --git a/chrome/browser/web_applications/extensions/extension_status_utils.cc b/chrome/browser/web_applications/extensions/extension_status_utils.cc
index 0d0b1a54..f0ba3da 100644
--- a/chrome/browser/web_applications/extensions/extension_status_utils.cc
+++ b/chrome/browser/web_applications/extensions/extension_status_utils.cc
@@ -107,9 +107,7 @@
   if (!app)
     return false;
 
-  // TODO(crbug.com/1235894): Figure out if "hosted apps" should be checked as
-  // well.
-  return (app->is_platform_app() || app->is_legacy_packaged_app()) &&
+  return app->is_app() &&
          !IsExtensionForceInstalled(context, extension_id, nullptr);
 }
 #endif
diff --git a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
index 9d772df..0740cc5 100644
--- a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
@@ -315,8 +315,9 @@
     auto ui_manager = std::make_unique<FakeWebAppUiManager>();
     ui_manager_ = ui_manager.get();
 
-    auto sync_bridge = std::make_unique<WebAppSyncBridge>(
-        &provider->GetDatabaseFactory(), registrar.get(), install_manager_);
+    auto sync_bridge = std::make_unique<WebAppSyncBridge>(registrar.get());
+    sync_bridge->SetSubsystems(&provider->GetDatabaseFactory(),
+                               install_manager_);
 
     provider->SetRegistrar(std::move(registrar));
     provider->SetSyncBridge(std::move(sync_bridge));
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc
index a25d3b25..93c99cb 100644
--- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_unittest.cc
@@ -145,8 +145,7 @@
     externally_installed_app_prefs_ =
         std::make_unique<ExternallyInstalledWebAppPrefs>(profile()->GetPrefs());
     icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), controller().registrar(), install_manager(),
-        base::MakeRefCounted<TestFileUtils>());
+        profile(), base::MakeRefCounted<TestFileUtils>());
     web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile());
     install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile());
     fake_externally_managed_app_manager_impl_ =
@@ -165,6 +164,8 @@
                                     &controller().os_integration_manager(),
                                     &install_finalizer());
 
+    icon_manager().SetSubsystems(&controller().registrar(), &install_manager());
+
     externally_managed_app_manager().SetSubsystems(
         &controller().registrar(), &ui_manager(), &install_finalizer(),
         &install_manager(), &controller().sync_bridge());
diff --git a/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc b/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
index 7131598..674adb59 100644
--- a/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
@@ -37,15 +37,15 @@
       /*url_handler_manager=*/nullptr);
 
   sync_bridge_ = std::make_unique<WebAppSyncBridge>(
-      database_factory_.get(), mutable_registrar_.get(), this,
-      mock_processor_.CreateForwardingProcessor());
+      mutable_registrar_.get(), mock_processor_.CreateForwardingProcessor());
+  sync_bridge_->SetSubsystems(database_factory_.get(), this);
   os_integration_manager_->SetSubsystems(sync_bridge_.get(),
                                          mutable_registrar_.get(),
                                          /*ui_manager=*/nullptr,
                                          /*icon_manager=*/nullptr);
   translation_manager_ = std::make_unique<WebAppTranslationManager>(
-      profile, /*install_manager=*/nullptr,
-      base::MakeRefCounted<TestFileUtils>());
+      profile, base::MakeRefCounted<TestFileUtils>());
+  translation_manager_->SetSubsystems(/*install_manager=*/nullptr);
 
   fake_externally_managed_app_manager_ =
       std::make_unique<FakeExternallyManagedAppManager>(profile);
diff --git a/chrome/browser/web_applications/web_app_icon_manager.cc b/chrome/browser/web_applications/web_app_icon_manager.cc
index e0377d7..e4499df 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager.cc
@@ -692,12 +692,8 @@
 }  // namespace
 
 WebAppIconManager::WebAppIconManager(Profile* profile,
-                                     WebAppRegistrar& registrar,
-                                     WebAppInstallManager& install_manager,
                                      scoped_refptr<FileUtilsWrapper> utils)
-    : registrar_(registrar),
-      install_manager_(install_manager),
-      utils_(std::move(utils)),
+    : utils_(std::move(utils)),
       icon_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
@@ -708,6 +704,12 @@
 
 WebAppIconManager::~WebAppIconManager() = default;
 
+void WebAppIconManager::SetSubsystems(WebAppRegistrar* registrar,
+                                      WebAppInstallManager* install_manager) {
+  registrar_ = registrar;
+  install_manager_ = install_manager;
+}
+
 void WebAppIconManager::WriteData(
     AppId app_id,
     IconBitmaps icon_bitmaps,
@@ -737,7 +739,7 @@
 }
 
 void WebAppIconManager::Start() {
-  for (const AppId& app_id : registrar_.GetAppIds()) {
+  for (const AppId& app_id : registrar_->GetAppIds()) {
     ReadFavicon(app_id);
 
     if (base::FeatureList::IsEnabled(
@@ -745,7 +747,7 @@
       ReadMonochromeFavicon(app_id);
     }
   }
-  install_manager_observation_.Observe(&install_manager_);
+  install_manager_observation_.Observe(install_manager_);
 }
 
 void WebAppIconManager::Shutdown() {}
@@ -754,7 +756,7 @@
                                  IconPurpose purpose,
                                  const SortedSizesPx& icon_sizes) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app)
     return false;
 
@@ -767,7 +769,7 @@
                                        const std::vector<IconPurpose>& purposes,
                                        SquareSizePx min_size) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app)
     return absl::nullopt;
 
@@ -810,7 +812,7 @@
 void WebAppIconManager::ReadAllIcons(const AppId& app_id,
                                      ReadIconBitmapsCallback callback) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app) {
     std::move(callback).Run(IconBitmaps());
     return;
@@ -836,7 +838,7 @@
     const AppId& app_id,
     ReadShortcutsMenuIconsCallback callback) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app) {
     std::move(callback).Run(ShortcutsMenuIconBitmaps{});
     return;
@@ -1037,7 +1039,7 @@
     const std::vector<IconPurpose>& purposes,
     SquareSizePx max_size) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app)
     return absl::nullopt;
 
@@ -1080,7 +1082,7 @@
 void WebAppIconManager::OnReadMonochromeFavicon(
     const AppId& app_id,
     gfx::ImageSkia manifest_monochrome_image) {
-  const WebApp* web_app = registrar_.GetAppById(app_id);
+  const WebApp* web_app = registrar_->GetAppById(app_id);
   if (!web_app)
     return;
 
diff --git a/chrome/browser/web_applications/web_app_icon_manager.h b/chrome/browser/web_applications/web_app_icon_manager.h
index b0d8d4b8..459e687c 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.h
+++ b/chrome/browser/web_applications/web_app_icon_manager.h
@@ -41,14 +41,14 @@
   using ReadImageSkiaCallback =
       base::OnceCallback<void(gfx::ImageSkia image_skia)>;
 
-  WebAppIconManager(Profile* profile,
-                    WebAppRegistrar& registrar,
-                    WebAppInstallManager& install_manager,
-                    scoped_refptr<FileUtilsWrapper> utils);
+  WebAppIconManager(Profile* profile, scoped_refptr<FileUtilsWrapper> utils);
   WebAppIconManager(const WebAppIconManager&) = delete;
   WebAppIconManager& operator=(const WebAppIconManager&) = delete;
   ~WebAppIconManager() override;
 
+  void SetSubsystems(WebAppRegistrar* registrar,
+                     WebAppInstallManager* install_manager);
+
   using WriteDataCallback = base::OnceCallback<void(bool success)>;
 
   // Writes all data (icons) for an app.
@@ -205,8 +205,8 @@
   void OnMonochromeIconConverted(const AppId& app_id,
                                  gfx::ImageSkia converted_image);
 
-  WebAppRegistrar& registrar_;
-  WebAppInstallManager& install_manager_;
+  raw_ptr<WebAppRegistrar> registrar_;
+  raw_ptr<WebAppInstallManager> install_manager_;
   base::FilePath web_apps_directory_;
   scoped_refptr<FileUtilsWrapper> utils_;
   scoped_refptr<base::SequencedTaskRunner> icon_task_runner_;
diff --git a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
index 477f49f7..eaeceaa 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
@@ -62,8 +62,8 @@
     install_manager_ = std::make_unique<WebAppInstallManager>(profile());
 
     file_utils_ = base::MakeRefCounted<TestFileUtils>();
-    icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), registrar(), install_manager(), file_utils_);
+    icon_manager_ = std::make_unique<WebAppIconManager>(profile(), file_utils_);
+    icon_manager_->SetSubsystems(&registrar(), &install_manager());
 
     controller().Init();
   }
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 7f36ec57..737edab0 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -44,6 +44,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -499,14 +500,14 @@
     AppId app_id,
     webapps::WebappUninstallSource source,
     UninstallWebAppCallback callback,
-    WebAppUninstallJobResult result) {
+    webapps::UninstallResultCode code) {
   DCHECK(base::Contains(pending_uninstalls_, app_id));
   pending_uninstalls_.erase(app_id);
   if (source == webapps::WebappUninstallSource::kSync) {
     base::UmaHistogramBoolean("Webapp.SyncInitiatedUninstallResult",
-                              result == WebAppUninstallJobResult::kSuccess);
+                              code == webapps::UninstallResultCode::kSuccess);
   }
-  std::move(callback).Run(result == WebAppUninstallJobResult::kSuccess);
+  std::move(callback).Run(code == webapps::UninstallResultCode::kSuccess);
 }
 
 void WebAppInstallFinalizer::UninstallExternalWebAppOrRemoveSource(
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 729b678..93172e9 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -25,6 +25,7 @@
 class Profile;
 
 namespace webapps {
+enum class UninstallResultCode;
 enum class WebappUninstallSource;
 }
 
@@ -39,7 +40,6 @@
 class WebAppRegistrar;
 class WebAppTranslationManager;
 class WebAppUninstallJob;
-enum class WebAppUninstallJobResult;
 
 // An finalizer for the installation process, represents the last step.
 // Takes WebAppInstallInfo as input, writes data to disk (e.g icons, shortcuts)
@@ -176,7 +176,7 @@
   void OnUninstallComplete(AppId app_id,
                            webapps::WebappUninstallSource uninstall_source,
                            UninstallWebAppCallback callback,
-                           WebAppUninstallJobResult result);
+                           webapps::UninstallResultCode code);
   void UninstallExternalWebAppOrRemoveSource(const AppId& app_id,
                                              Source::Type source,
                                              UninstallWebAppCallback callback);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
index e0bc7e1..81a3a56 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
@@ -85,12 +85,12 @@
         std::make_unique<TestInstallManagerObserver>(install_manager_.get());
     fake_registry_controller_->SetUp(profile());
     icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), registrar(), install_manager(),
-        base::MakeRefCounted<FileUtilsWrapper>());
+        profile(), base::MakeRefCounted<FileUtilsWrapper>());
     policy_manager_ = std::make_unique<WebAppPolicyManager>(profile());
     ui_manager_ = std::make_unique<FakeWebAppUiManager>();
     finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile());
 
+    icon_manager_->SetSubsystems(&registrar(), &install_manager());
     finalizer_->SetSubsystems(
         &install_manager(), &registrar(), ui_manager_.get(),
         &fake_registry_controller_->sync_bridge(),
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
index 4fbb77f..ab47c2e 100644
--- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -178,8 +178,7 @@
     fake_registry_controller_->SetUp(profile());
 
     file_utils_ = base::MakeRefCounted<TestFileUtils>();
-    icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), registrar(), install_manager(), file_utils_);
+    icon_manager_ = std::make_unique<WebAppIconManager>(profile(), file_utils_);
 
     policy_manager_ = std::make_unique<WebAppPolicyManager>(profile());
 
@@ -197,6 +196,8 @@
 
     ui_manager_ = std::make_unique<FakeWebAppUiManager>();
 
+    icon_manager_->SetSubsystems(&registrar(), &install_manager());
+
     install_finalizer_->SetSubsystems(
         &install_manager(), &registrar(), ui_manager_.get(),
         &fake_registry_controller_->sync_bridge(),
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc
index 5e2b2b3..62f41f8 100644
--- a/chrome/browser/web_applications/web_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -100,8 +100,7 @@
     install_manager_ = std::make_unique<WebAppInstallManager>(profile());
 
     file_utils_ = base::MakeRefCounted<TestFileUtils>();
-    icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), registrar(), install_manager(), file_utils_);
+    icon_manager_ = std::make_unique<WebAppIconManager>(profile(), file_utils_);
 
     policy_manager_ = std::make_unique<WebAppPolicyManager>(profile());
 
@@ -109,6 +108,8 @@
 
     install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile());
 
+    icon_manager_->SetSubsystems(&registrar(), &install_manager());
+
     install_finalizer_->SetSubsystems(
         &install_manager(), &registrar(), ui_manager_.get(),
         &fake_registry_controller_->sync_bridge(),
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index a81b7a1..08962cb9 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -260,20 +260,16 @@
   {
     auto mutable_registrar = std::make_unique<WebAppRegistrarMutable>(profile);
 
-    sync_bridge = std::make_unique<WebAppSyncBridge>(database_factory_.get(),
-                                                     mutable_registrar.get(),
-                                                     install_manager_.get());
+    sync_bridge = std::make_unique<WebAppSyncBridge>(mutable_registrar.get());
 
     // Upcast to read-only WebAppRegistrar.
     registrar = std::move(mutable_registrar);
   }
 
-  auto icon_manager = std::make_unique<WebAppIconManager>(
-      profile, *registrar, *install_manager_,
-      base::MakeRefCounted<FileUtilsWrapper>());
-  auto translation_manager = std::make_unique<WebAppTranslationManager>(
-      profile, install_manager_.get(),
-      base::MakeRefCounted<FileUtilsWrapper>());
+  icon_manager_ = std::make_unique<WebAppIconManager>(
+      profile, base::MakeRefCounted<FileUtilsWrapper>());
+  translation_manager_ = std::make_unique<WebAppTranslationManager>(
+      profile, base::MakeRefCounted<FileUtilsWrapper>());
   install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile);
 
   if (g_os_integration_manager_factory_for_testing) {
@@ -285,7 +281,7 @@
     auto protocol_handler_manager =
         std::make_unique<WebAppProtocolHandlerManager>(profile);
     auto shortcut_manager = std::make_unique<WebAppShortcutManager>(
-        profile, icon_manager.get(), file_handler_manager.get(),
+        profile, icon_manager_.get(), file_handler_manager.get(),
         protocol_handler_manager.get());
 
     std::unique_ptr<UrlHandlerManager> url_handler_manager;
@@ -301,13 +297,14 @@
 
   registrar_ = std::move(registrar);
   sync_bridge_ = std::move(sync_bridge);
-  icon_manager_ = std::move(icon_manager);
-  translation_manager_ = std::move(translation_manager);
 }
 
 void WebAppProvider::ConnectSubsystems() {
   DCHECK(!started_);
 
+  sync_bridge_->SetSubsystems(database_factory_.get(), install_manager_.get());
+  icon_manager_->SetSubsystems(registrar_.get(), install_manager_.get());
+  translation_manager_->SetSubsystems(install_manager_.get());
   install_finalizer_->SetSubsystems(
       install_manager_.get(), registrar_.get(), ui_manager_.get(),
       sync_bridge_.get(), os_integration_manager_.get(), icon_manager_.get(),
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index 78c48099..29d998f1 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -112,37 +112,35 @@
   app->SetSyncFallbackData(std::move(parsed_sync_fallback_data.value()));
 }
 
-WebAppSyncBridge::WebAppSyncBridge(
-    AbstractWebAppDatabaseFactory* database_factory,
-    WebAppRegistrarMutable* registrar,
-    SyncInstallDelegate* install_delegate)
+WebAppSyncBridge::WebAppSyncBridge(WebAppRegistrarMutable* registrar)
     : WebAppSyncBridge(
-          database_factory,
           registrar,
-          install_delegate,
           std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
               syncer::WEB_APPS,
               base::BindRepeating(&syncer::ReportUnrecoverableError,
                                   chrome::GetChannel()))) {}
 
 WebAppSyncBridge::WebAppSyncBridge(
-    AbstractWebAppDatabaseFactory* database_factory,
     WebAppRegistrarMutable* registrar,
-    SyncInstallDelegate* install_delegate,
     std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
     : syncer::ModelTypeSyncBridge(std::move(change_processor)),
-      registrar_(registrar),
-      install_delegate_(install_delegate) {
-  DCHECK(database_factory);
+      registrar_(registrar) {
   DCHECK(registrar_);
+}
+
+WebAppSyncBridge::~WebAppSyncBridge() = default;
+
+void WebAppSyncBridge::SetSubsystems(
+    AbstractWebAppDatabaseFactory* database_factory,
+    SyncInstallDelegate* install_delegate) {
+  DCHECK(database_factory);
   database_ = std::make_unique<WebAppDatabase>(
       database_factory,
       base::BindRepeating(&WebAppSyncBridge::ReportErrorToChangeProcessor,
                           base::Unretained(this)));
+  install_delegate_ = install_delegate;
 }
 
-WebAppSyncBridge::~WebAppSyncBridge() = default;
-
 std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() {
   DCHECK(database_->is_opened());
 
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.h b/chrome/browser/web_applications/web_app_sync_bridge.h
index e462760..8661024 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.h
+++ b/chrome/browser/web_applications/web_app_sync_bridge.h
@@ -57,19 +57,18 @@
 // ModelTypeChangeProcessor and WebAppDatabase (the storage).
 class WebAppSyncBridge : public syncer::ModelTypeSyncBridge {
  public:
-  WebAppSyncBridge(AbstractWebAppDatabaseFactory* database_factory,
-                   WebAppRegistrarMutable* registrar,
-                   SyncInstallDelegate* install_delegate);
+  explicit WebAppSyncBridge(WebAppRegistrarMutable* registrar);
   // Tests may inject mocks using this ctor.
   WebAppSyncBridge(
-      AbstractWebAppDatabaseFactory* database_factory,
       WebAppRegistrarMutable* registrar,
-      SyncInstallDelegate* install_delegate,
       std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
   WebAppSyncBridge(const WebAppSyncBridge&) = delete;
   WebAppSyncBridge& operator=(const WebAppSyncBridge&) = delete;
   ~WebAppSyncBridge() override;
 
+  void SetSubsystems(AbstractWebAppDatabaseFactory* database_factory,
+                     SyncInstallDelegate* install_delegate);
+
   using CommitCallback = base::OnceCallback<void(bool success)>;
   // This is the writable API for the registry. Any updates will be written to
   // LevelDb and sync service. There can be only 1 update at a time.
@@ -190,7 +189,7 @@
 
   std::unique_ptr<WebAppDatabase> database_;
   const raw_ptr<WebAppRegistrarMutable> registrar_;
-  const raw_ptr<SyncInstallDelegate> install_delegate_;
+  raw_ptr<SyncInstallDelegate> install_delegate_;
 
   bool is_in_update_ = false;
   bool disable_checks_for_testing_ = false;
diff --git a/chrome/browser/web_applications/web_app_translation_manager.cc b/chrome/browser/web_applications/web_app_translation_manager.cc
index 5ef6dcf..574d811 100644
--- a/chrome/browser/web_applications/web_app_translation_manager.cc
+++ b/chrome/browser/web_applications/web_app_translation_manager.cc
@@ -126,14 +126,18 @@
 
 WebAppTranslationManager::WebAppTranslationManager(
     Profile* profile,
-    base::raw_ptr<WebAppInstallManager> install_manager,
     scoped_refptr<FileUtilsWrapper> utils)
-    : install_manager_(install_manager), utils_(std::move(utils)) {
+    : utils_(std::move(utils)) {
   web_apps_directory_ = GetWebAppsRootDirectory(profile);
 }
 
 WebAppTranslationManager::~WebAppTranslationManager() = default;
 
+void WebAppTranslationManager::SetSubsystems(
+    base::raw_ptr<WebAppInstallManager> install_manager) {
+  install_manager_ = install_manager;
+}
+
 void WebAppTranslationManager::Start() {
   if (base::FeatureList::IsEnabled(
           blink::features::kWebAppEnableTranslations)) {
diff --git a/chrome/browser/web_applications/web_app_translation_manager.h b/chrome/browser/web_applications/web_app_translation_manager.h
index 0517c0f..fb41fb8 100644
--- a/chrome/browser/web_applications/web_app_translation_manager.h
+++ b/chrome/browser/web_applications/web_app_translation_manager.h
@@ -28,12 +28,13 @@
   using WriteCallback = base::OnceCallback<void(bool success)>;
 
   WebAppTranslationManager(Profile* profile,
-                           base::raw_ptr<WebAppInstallManager> install_manager,
                            scoped_refptr<FileUtilsWrapper> utils);
   WebAppTranslationManager(const WebAppTranslationManager&) = delete;
   WebAppTranslationManager& operator=(const WebAppTranslationManager&) = delete;
   ~WebAppTranslationManager() override;
 
+  void SetSubsystems(base::raw_ptr<WebAppInstallManager> install_manager);
+
   void Start();
 
   void WriteTranslations(
diff --git a/chrome/browser/web_applications/web_app_translation_manager_unittest.cc b/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
index b5d943e..609abf9 100644
--- a/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
@@ -32,8 +32,9 @@
     controller().Init();
     InitWebAppProvider();
 
-    translation_manager_ = std::make_unique<WebAppTranslationManager>(
-        profile(), &install_manager(), file_utils_);
+    translation_manager_ =
+        std::make_unique<WebAppTranslationManager>(profile(), file_utils_);
+    translation_manager_->SetSubsystems(&install_manager());
   }
 
  protected:
diff --git a/chrome/browser/web_applications/web_app_uninstall_job.cc b/chrome/browser/web_applications/web_app_uninstall_job.cc
index ac43839..873da9ff 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job.cc
+++ b/chrome/browser/web_applications/web_app_uninstall_job.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 
 namespace web_app {
 
@@ -141,8 +142,8 @@
       break;
   }
   install_manager_->NotifyWebAppUninstalled(app_id_);
-  std::move(callback_).Run(errors_ ? WebAppUninstallJobResult::kError
-                                   : WebAppUninstallJobResult::kSuccess);
+  std::move(callback_).Run(errors_ ? webapps::UninstallResultCode::kError
+                                   : webapps::UninstallResultCode::kSuccess);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_uninstall_job.h b/chrome/browser/web_applications/web_app_uninstall_job.h
index e114992d..8f5cbf63 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job.h
+++ b/chrome/browser/web_applications/web_app_uninstall_job.h
@@ -15,6 +15,7 @@
 class PrefService;
 
 namespace webapps {
+enum class UninstallResultCode;
 enum class WebappUninstallSource;
 }
 
@@ -27,11 +28,6 @@
 class WebAppRegistrar;
 class WebAppSyncBridge;
 
-enum class WebAppUninstallJobResult {
-  kSuccess = 0,
-  kError = 1,
-};
-
 // Uninstalls a given web app by:
 // 1) Unregistering OS hooks.
 // 2) Deleting the app from the database.
@@ -42,7 +38,8 @@
 // TODO(https://crbug.com/1162477): Make the database delete happen last.
 class WebAppUninstallJob {
  public:
-  using UninstallCallback = base::OnceCallback<void(WebAppUninstallJobResult)>;
+  using UninstallCallback =
+      base::OnceCallback<void(webapps::UninstallResultCode)>;
 
   WebAppUninstallJob(OsIntegrationManager* os_integration_manager,
                      WebAppSyncBridge* sync_bridge,
diff --git a/chrome/browser/web_applications/web_app_uninstall_job_unittest.cc b/chrome/browser/web_applications/web_app_uninstall_job_unittest.cc
index e63cf53..2d4ecab 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job_unittest.cc
+++ b/chrome/browser/web_applications/web_app_uninstall_job_unittest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
@@ -48,9 +49,9 @@
 
     file_utils_wrapper_ =
         base::MakeRefCounted<testing::StrictMock<MockFileUtilsWrapper>>();
-    icon_manager_ = std::make_unique<WebAppIconManager>(
-        profile(), controller().registrar(), install_manager(),
-        file_utils_wrapper_);
+    icon_manager_ =
+        std::make_unique<WebAppIconManager>(profile(), file_utils_wrapper_);
+    icon_manager_->SetSubsystems(&controller().registrar(), &install_manager());
   }
 
   void TearDown() override {
@@ -103,8 +104,8 @@
   base::RunLoop loop;
   task.Start(id, url::Origin(), webapps::WebappUninstallSource::kAppMenu,
              WebAppUninstallJob::ModifyAppRegistry::kYes,
-             base::BindLambdaForTesting([&](WebAppUninstallJobResult result) {
-               EXPECT_EQ(WebAppUninstallJobResult::kSuccess, result);
+             base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+               EXPECT_EQ(webapps::UninstallResultCode::kSuccess, code);
                loop.Quit();
              }));
   loop.Run();
@@ -139,8 +140,8 @@
   base::RunLoop loop;
   task.Start(id, url::Origin(), webapps::WebappUninstallSource::kAppMenu,
              WebAppUninstallJob::ModifyAppRegistry::kYes,
-             base::BindLambdaForTesting([&](WebAppUninstallJobResult result) {
-               EXPECT_EQ(WebAppUninstallJobResult::kError, result);
+             base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+               EXPECT_EQ(webapps::UninstallResultCode::kError, code);
                loop.Quit();
              }));
   loop.Run();
@@ -176,8 +177,8 @@
   base::RunLoop loop;
   task.Start(id, url::Origin(), webapps::WebappUninstallSource::kAppMenu,
              WebAppUninstallJob::ModifyAppRegistry::kYes,
-             base::BindLambdaForTesting([&](WebAppUninstallJobResult result) {
-               EXPECT_EQ(WebAppUninstallJobResult::kError, result);
+             base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+               EXPECT_EQ(webapps::UninstallResultCode::kError, code);
                loop.Quit();
              }));
   loop.Run();
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 32132e1..a9d20dc 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1646308793-59e2bfd784468c085584e2a95b3529b21c9715c8.profdata
+chrome-linux-main-1646351850-e130df75a1db05108bce9392a7ef7802dcecbcbf.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 4257add..42ac70f4 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1646308793-8139c4ded231ed31952c96484886f7e747dd9477.profdata
+chrome-mac-arm-main-1646351850-162d9ea153d3a2328281d56a551c09dec83c924b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 40337fe4d..7d0fff1 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1646308793-e835e6fbf3a9cf3f815e91bf4af5059675588af1.profdata
+chrome-mac-main-1646351850-8d66e9fd4499fb5b0e45259a4a140c4e769b4f3a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 329a16b..731b8f54 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1646319550-05b31bd11cc9a70a44550b6db415f1587fa0fffe.profdata
+chrome-win32-main-1646340877-f57bfe90c41df81b369842a8d3231b77c3ca4071.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5a25a14..aa05e6e 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1646319550-46fb9a877e4415be1343f88383b0de033cdd5fb5.profdata
+chrome-win64-main-1646351850-f9222b04fae451f4416b18f11de544e1a08062fb.profdata
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index afd3b419..60a58b7 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -11,6 +11,9 @@
 
 namespace chrome {
 
+const char kAccessCodeCastLearnMoreURL[] =
+    "https://support.google.com/chrome/a/?p=cast_to_class_teacher";
+
 const char kAccessibilityLabelsLearnMoreURL[] =
     "https://support.google.com/chrome/?p=image_descriptions";
 
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index dc71009e..a12d7d9 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -27,6 +27,9 @@
 
 namespace chrome {
 
+// "Learn more" URL linked in the dialog to cast using a code.
+extern const char kAccessCodeCastLearnMoreURL[];
+
 // "Learn more" URL for accessibility image labels, linked from the permissions
 // dialog shown when a user enables the feature.
 extern const char kAccessibilityLabelsLearnMoreURL[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 10945ffd..fb26cbf 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6697,6 +6697,7 @@
       "../browser/ui/views/passwords/password_save_unsynced_credentials_locally_view_unittest.cc",
       "../browser/ui/views/passwords/password_save_update_view_unittest.cc",
       "../browser/ui/views/passwords/post_save_compromised_bubble_view_unittest.cc",
+      "../browser/ui/views/side_panel/side_panel_coordinator_unittest.cc",
       "../renderer/media/webrtc_logging_agent_impl_unittest.cc",
     ]
 
diff --git a/chrome/test/chromedriver/net/adb_client_socket.cc b/chrome/test/chromedriver/net/adb_client_socket.cc
index 40b0c7b..c072ac8 100644
--- a/chrome/test/chromedriver/net/adb_client_socket.cc
+++ b/chrome/test/chromedriver/net/adb_client_socket.cc
@@ -431,7 +431,7 @@
   bool CheckNetResultOrDie(int result) {
     if (result >= 0)
       return true;
-    callback_.Run(result, NULL);
+    callback_.Run(result, std::string());
     delete this;
     return false;
   }
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 6dcba2a..cd932c5 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -273,6 +273,7 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/ambient_mode_photos_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/apps_page_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/app_details_item_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/app_detail_view_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/app_item_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/app_management_page_tests.m.js",
diff --git a/chrome/test/data/webui/access_code_cast/access_code_cast_test.js b/chrome/test/data/webui/access_code_cast/access_code_cast_test.js
index b62852c..eecd3e4a 100644
--- a/chrome/test/data/webui/access_code_cast/access_code_cast_test.js
+++ b/chrome/test/data/webui/access_code_cast/access_code_cast_test.js
@@ -253,4 +253,18 @@
     await waitAfterNextRender();
     assertTrue(visited);
   });
+
+  test('submit button disabled during cast attempt', () => {
+    app.setAccessCodeForTest('foobar');
+    assertFalse(app.$.castButton.disabled);
+    let testProxy = createTestProxy(
+      AddSinkResultCode.OK,
+      RouteRequestResultCode.OK,
+      () => {
+        assertTrue(app.$.castButton.disabled);
+      }
+    );
+    BrowserProxy.setInstance(testProxy);
+    app.addSinkAndCast();
+  });
 });
diff --git a/chrome/test/data/webui/app_settings/app_test.ts b/chrome/test/data/webui/app_settings/app_test.ts
index 0b8347bd..de03d14 100644
--- a/chrome/test/data/webui/app_settings/app_test.ts
+++ b/chrome/test/data/webui/app_settings/app_test.ts
@@ -6,7 +6,9 @@
 import 'chrome://app-settings/web_app_settings.js';
 
 import {App, AppManagementPermissionItemElement, AppManagementToggleRowElement, AppType, BrowserProxy, createTriStatePermission, getPermissionValueBool, InstallReason, InstallSource, OptionalBool, PermissionType, PermissionTypeIndex, RunOnOsLoginMode, TriState, WebAppSettingsAppElement, WindowMode} from 'chrome://app-settings/web_app_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
 import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {TestAppManagementBrowserProxy} from './test_app_management_browser_proxy.js';
@@ -14,6 +16,7 @@
 suite('AppSettingsAppTest', () => {
   let appSettingsApp: WebAppSettingsAppElement;
   let app: App;
+  let testProxy: TestAppManagementBrowserProxy;
 
   setup(async () => {
     app = {
@@ -59,7 +62,7 @@
           createTriStatePermission(permissionType, permissionValue, isManaged);
     }
 
-    const testProxy = new TestAppManagementBrowserProxy(app);
+    testProxy = new TestAppManagementBrowserProxy(app);
     BrowserProxy.setInstance(testProxy);
 
     document.body.innerHTML = '';
@@ -116,6 +119,39 @@
     assertEquals(fileHandlingItem.app.fileHandlingState!.enabled, false);
   });
 
+  test('File Handling overflow', async function() {
+    const fileHandlingItem = appSettingsApp.shadowRoot!.querySelector(
+        'app-management-file-handling-item')!;
+    assertTrue(!!fileHandlingItem);
+
+    // No overflow link because it's not in `userVisibleTypes`.
+    const typeList = fileHandlingItem.shadowRoot!.querySelector('#type-list')!;
+    assertTrue(!!typeList);
+    let link = typeList.shadowRoot!.querySelector<HTMLElement>('a');
+    assertTrue(!link);
+
+    // Overflow link present.
+    app.fileHandlingState!.userVisibleTypesLabel =
+        'TXT, CSV, MD, DOC (<a href="#">and 1 more</a>)';
+    testProxy.callbackRouterRemote.onAppChanged(app);
+    await flushTasks();
+    link = typeList.shadowRoot!.querySelector<HTMLElement>('a');
+    assertTrue(!!link);
+
+    // Dialog starts hidden.
+    let dialog =
+        fileHandlingItem.shadowRoot!.querySelector<HTMLElement>('#dialog');
+    assertTrue(!dialog);
+    const originalUrl = location.href;
+    link.click();
+    flush();
+
+    // Clicking the link doesn't change the URL, and does open the dialog.
+    assertEquals(originalUrl, location.href);
+    dialog = fileHandlingItem.shadowRoot!.querySelector<HTMLElement>('#dialog');
+    assertTrue(!!dialog);
+  });
+
   test('Toggle window mode', function() {
     const windowModeItem =
         appSettingsApp.shadowRoot!.querySelector('app-management-window-mode-item')!;
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
index c98d897..ad00aaa 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
@@ -14,6 +14,12 @@
 // clang-format on
 
 suite('CrComponentsActivationCodePageTest', function() {
+  /** @type {string} */
+  const ACTIVATION_CODE_VALID = 'LPA:1$ACTIVATION_CODE';
+
+  /** @type {string} */
+  const ACTIVATION_CODE_INVALID = 'INVALID';
+
   let activationCodePage;
 
   /** @type {?FakeMediaDevices} */
@@ -310,7 +316,7 @@
         assertTrue(!!scanFinishContainer);
         assertFalse(input.invalid);
 
-        input.value = 'ACTIVATION_CODE';
+        input.value = ACTIVATION_CODE_VALID;
         activationCodePage.showError = true;
         assertTrue(input.invalid);
 
@@ -432,4 +438,149 @@
 
         assertFalse(!!activationCodePage.getQrCodeDetectorTimerForTest());
       });
+
+  test('Input entered manually is validated', async function() {
+    await flushAsync();
+    const input = activationCodePage.$$('#activationCode');
+    assertTrue(!!input);
+    assertFalse(input.invalid);
+
+    const setInputAndAssert =
+        async (activationCode, isInputInvalid, shouldEventContainCode) => {
+      const activationCodeUpdatedPromise = test_util.eventToPromise(
+          'activation-code-updated', activationCodePage);
+      input.value = activationCode;
+      const activationCodeUpdatedEvent = await activationCodeUpdatedPromise;
+      assertEquals(
+          activationCodeUpdatedEvent.detail.activationCode,
+          shouldEventContainCode ? activationCode : null);
+      assertEquals(input.invalid, isInputInvalid);
+      assertEquals(activationCodePage.$.inputSubtitle.hidden, isInputInvalid);
+      assertEquals(
+          activationCodePage.$.inputSubtitle.innerText.trim(),
+          loadTimeData.getString('scanQrCodeInputSubtitle'));
+    };
+
+    await setInputAndAssert(
+        /*activationCode=*/ 'U', /*isInputValid=*/ true,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'L', /*isInputInvalid=*/ false,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'Lp', /*isInputInvalid=*/ true,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'LP', /*isInputInvalid=*/ false,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'LPA:1#', /*isInputInvalid=*/ true,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'LPA:1$', /*isInputInvalid=*/ false,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'LPA:1#ACTIVATION_CODE', /*isInputInvalid=*/ true,
+        /*shouldEventContainCode=*/ false);
+    await setInputAndAssert(
+        /*activationCode=*/ 'LPA:1$ACTIVATION_CODE', /*isInputInvalid=*/ false,
+        /*shouldEventContainCode=*/ true);
+
+    // Erase the code so that it's incomplete. The event should no longer
+    // contain the code.
+    await setInputAndAssert(
+        /*activationCode=*/ 'LPA:1$', /*isInputInvalid=*/ false,
+        /*doesEventContainCode=*/ false);
+  });
+
+  test('Scanned code is validated', async function() {
+    await flushAsync();
+    const input = activationCodePage.$$('#activationCode');
+    const startScanningContainer =
+        activationCodePage.$$('#startScanningContainer');
+    const startScanningButton = activationCodePage.$$('#startScanningButton');
+    const scanFinishContainer = activationCodePage.$$('#scanFinishContainer');
+    const scanInstallFailureHeader =
+        activationCodePage.$$('#scanInstallFailureHeader');
+    const scanSucessHeader = activationCodePage.$$('#scanSucessHeader');
+    const getUseCameraAgainButton = () => {
+      return activationCodePage.$$('#useCameraAgainButton');
+    };
+    assertTrue(!!input);
+    assertTrue(!!startScanningContainer);
+    assertTrue(!!startScanningButton);
+    assertTrue(!!scanFinishContainer);
+    assertTrue(!!scanInstallFailureHeader);
+    assertTrue(!!scanSucessHeader);
+    assertFalse(!!getUseCameraAgainButton());
+    assertFalse(input.invalid);
+
+    // Click the start scanning button.
+    startScanningButton.click();
+    mediaDevices.resolveGetUserMedia();
+    await waitAfterNextRender(activationCodePage);
+
+    // Mock camera scanning an invalid code.
+    let activationCodeUpdatedPromise =
+        test_util.eventToPromise('activation-code-updated', activationCodePage);
+    FakeBarcodeDetector.setDetectedBarcode(ACTIVATION_CODE_INVALID);
+    await intervalFunction();
+    await flushAsync();
+
+    // The scan install failure UI should be showing.
+    assertTrue(startScanningContainer.hidden);
+    assertFalse(scanFinishContainer.hidden);
+    assertTrue(scanSucessHeader.hidden);
+    assertFalse(scanInstallFailureHeader.hidden);
+    assertTrue(!!getUseCameraAgainButton());
+    assertTrue(input.invalid);
+    let activationCodeUpdatedEvent = await activationCodeUpdatedPromise;
+    assertFalse(!!activationCodeUpdatedEvent.detail.activationCode);
+
+    // Start scanning again.
+    getUseCameraAgainButton().click();
+    mediaDevices.resolveGetUserMedia();
+    await waitAfterNextRender(activationCodePage);
+
+    // Mock camera scanning a valid, incomplete code.
+    activationCodeUpdatedPromise =
+        test_util.eventToPromise('activation-code-updated', activationCodePage);
+    FakeBarcodeDetector.setDetectedBarcode(/*barcode=*/ 'LPA:');
+    await intervalFunction();
+    await flushAsync();
+
+    // The scan install failure UI should be showing.
+    assertTrue(startScanningContainer.hidden);
+    assertFalse(scanFinishContainer.hidden);
+    assertTrue(scanSucessHeader.hidden);
+    assertFalse(scanInstallFailureHeader.hidden);
+    assertTrue(!!getUseCameraAgainButton());
+    assertFalse(input.invalid);
+    activationCodeUpdatedEvent = await activationCodeUpdatedPromise;
+    assertFalse(!!activationCodeUpdatedEvent.detail.activationCode);
+
+    // Start scanning again.
+    getUseCameraAgainButton().click();
+    mediaDevices.resolveGetUserMedia();
+    await waitAfterNextRender(activationCodePage);
+
+    // Mock camera scanning a valid code.
+    activationCodeUpdatedPromise =
+        test_util.eventToPromise('activation-code-updated', activationCodePage);
+    FakeBarcodeDetector.setDetectedBarcode(ACTIVATION_CODE_VALID);
+    await intervalFunction();
+    await flushAsync();
+
+    // The code detected UI should be showing.
+    assertTrue(startScanningContainer.hidden);
+    assertFalse(scanFinishContainer.hidden);
+    assertFalse(scanSucessHeader.hidden);
+    assertTrue(scanInstallFailureHeader.hidden);
+    assertFalse(!!getUseCameraAgainButton());
+    assertFalse(input.invalid);
+    activationCodeUpdatedEvent = await activationCodeUpdatedPromise;
+    assertEquals(
+        activationCodeUpdatedEvent.detail.activationCode,
+        ACTIVATION_CODE_VALID);
+  });
 });
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
index bff5f56..eed96f8d 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
@@ -21,6 +21,9 @@
 // clang-format on
 
 suite('CrComponentsEsimFlowUiTest', function() {
+  /** @type {string} */
+  const ACTIVATION_CODE_VALID = 'LPA:1$ACTIVATION_CODE';
+
   let eSimPage;
   let eSimManagerRemote;
   let ironPages;
@@ -303,7 +306,7 @@
           /*forwardButtonShouldBeEnabled*/ false,
           /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
       // Insert an activation code.
-      activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
+      activationCodePage.$$('#activationCode').value = ACTIVATION_CODE_VALID;
 
       // Forward button should now be enabled.
       assertActivationCodePage(
@@ -417,7 +420,8 @@
           /*forwardButtonShouldBeEnabled*/ true,
           /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
       assertEquals(
-          activationCodePage.$$('#activationCode').value, 'ACTIVATION_CODE');
+          activationCodePage.$$('#activationCode').value,
+          ACTIVATION_CODE_VALID);
 
       endFlowAndVerifyResult(
           ESimSetupFlowResult.CANCELLED_NEEDS_CONFIRMATION_CODE);
@@ -585,7 +589,7 @@
       assertFocusDefaultButtonEventFired();
 
       // Insert an activation code.
-      activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
+      activationCodePage.$$('#activationCode').value = ACTIVATION_CODE_VALID;
       assertFalse(focusDefaultButtonEventFired);
 
       assertActivationCodePage(
@@ -638,7 +642,7 @@
           assertFocusDefaultButtonEventFired();
           assertEquals(
               activationCodePage.$$('#activationCode').value,
-              'ACTIVATION_CODE');
+              ACTIVATION_CODE_VALID);
 
           eSimPage.navigateBackward();
           await flushAsync();
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
index 23a951ba..df23a8b 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
@@ -10,6 +10,12 @@
 let shouldBarcodeDetectionFail = false;
 
 /**
+ * The barcode returned by successful calls to detect().
+ * @type {string}
+ */
+let detectedBarcode = 'LPA:1$ACTIVATION_CODE';
+
+/**
  * @implements {BarcodeDetector}
  */
 /* #export */ class FakeBarcodeDetector {
@@ -20,7 +26,7 @@
     if (shouldBarcodeDetectionFail) {
       return Promise.reject('Failed to detect code');
     }
-    return Promise.resolve([{rawValue: 'testbarcode'}]);
+    return Promise.resolve([{rawValue: detectedBarcode}]);
   }
 
   /** @override */
@@ -35,6 +41,13 @@
   static setShouldFail(shouldFail) {
     shouldBarcodeDetectionFail = shouldFail;
   }
+
+  /**
+   * @param {string} barcode
+   */
+  static setDetectedBarcode(barcode) {
+    detectedBarcode = barcode;
+  }
 }
 
 /**
diff --git a/chrome/test/data/webui/extensions/BUILD.gn b/chrome/test/data/webui/extensions/BUILD.gn
index cb4ca1da..adafc57 100644
--- a/chrome/test/data/webui/extensions/BUILD.gn
+++ b/chrome/test/data/webui/extensions/BUILD.gn
@@ -44,7 +44,8 @@
   "shortcut_input_test.ts",
   "sidebar_test.ts",
   "site_permissions_by_site_test.ts",
-  "site_permissions_edit_dialog_test.ts",
+  "site_permissions_edit_permissions_dialog_test.ts",
+  "site_permissions_edit_url_dialog_test.ts",
   "site_permissions_list_test.ts",
   "site_permissions_test.ts",
   "test_service.ts",
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 684b820..b19626c5 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -895,17 +895,17 @@
 });
 
 ////////////////////////////////////////////////////////////////////////////////
-// SitePermissionsEditDialog tests
+// SitePermissionsEditUrlDialog tests
 
-var CrExtensionsSitePermissionsEditDialogTest =
+var CrExtensionsSitePermissionsEditUrlDialogTest =
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/site_permissions_edit_dialog_test.js&host=webui-test';
+    return 'chrome://extensions/test_loader.html?module=extensions/site_permissions_edit_url_dialog_test.js&host=webui-test';
   }
 };
 
-TEST_F('CrExtensionsSitePermissionsEditDialogTest', 'All', () => {
+TEST_F('CrExtensionsSitePermissionsEditUrlDialogTest', 'All', () => {
   mocha.run();
 });
 
@@ -937,3 +937,18 @@
 TEST_F('CrUrlUtilTest', 'All', () => {
   mocha.run();
 });
+
+////////////////////////////////////////////////////////////////////////////////
+// SitePermissionsEditPermissionsDialog tests
+
+var CrExtensionsSitePermissionsEditPermissionsDialog =
+    class extends CrExtensionsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://extensions/test_loader.html?module=extensions/site_permissions_edit_permissions_dialog_test.js&host=webui-test';
+  }
+};
+
+TEST_F('CrExtensionsSitePermissionsEditPermissionsDialog', 'All', () => {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts b/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts
new file mode 100644
index 0000000..68eb4ab
--- /dev/null
+++ b/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts
@@ -0,0 +1,61 @@
+// 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.
+
+/**
+ * @fileoverview
+ * Suite of tests for site-permissions-edit-permissions-dialog.
+ * */
+import 'chrome://extensions/extensions.js';
+
+import {SitePermissionsEditPermissionsDialogElement} from 'chrome://extensions/extensions.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {TestService} from './test_service.js';
+
+suite('SitePermissionsEditPermissionsDialog', function() {
+  let element: SitePermissionsEditPermissionsDialogElement;
+  let delegate: TestService;
+  const UserSiteSet = chrome.developerPrivate.UserSiteSet;
+
+  setup(function() {
+    delegate = new TestService();
+
+    document.body.innerHTML = '';
+    element =
+        document.createElement('site-permissions-edit-permissions-dialog');
+    element.delegate = delegate;
+    element.site = 'http://example.com';
+    element.originalSiteSet = UserSiteSet.PERMITTED;
+    document.body.appendChild(element);
+  });
+
+  test('editing current site set', async function() {
+    const siteSetRadioGroup =
+        element.shadowRoot!.querySelector('cr-radio-group');
+    assertTrue(!!siteSetRadioGroup);
+    assertEquals(UserSiteSet.PERMITTED, siteSetRadioGroup.selected);
+
+    const restrictSiteRadioButton =
+        element.shadowRoot!.querySelector<HTMLElement>(
+            `cr-radio-button[name=${UserSiteSet.RESTRICTED}]`);
+    assertTrue(!!restrictSiteRadioButton);
+    restrictSiteRadioButton.click();
+
+    flush();
+    assertEquals(UserSiteSet.RESTRICTED, siteSetRadioGroup.selected);
+
+    const whenClosed = eventToPromise('close', element);
+    const submit = element.$.submit;
+    submit.click();
+
+    const [siteSet, site] = await delegate.whenCalled('addUserSpecifiedSite');
+    assertEquals(UserSiteSet.RESTRICTED, siteSet);
+    assertEquals(element.site, site);
+
+    await whenClosed;
+    assertFalse(element.$.dialog.open);
+  });
+});
diff --git a/chrome/test/data/webui/extensions/site_permissions_edit_dialog_test.ts b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
similarity index 88%
rename from chrome/test/data/webui/extensions/site_permissions_edit_dialog_test.ts
rename to chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
index 4b439f58..700d0f9 100644
--- a/chrome/test/data/webui/extensions/site_permissions_edit_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @fileoverview Suite of tests for site-permissions-edit-dialog. */
+/** @fileoverview Suite of tests for site-permissions-edit-url-dialog. */
 import 'chrome://extensions/extensions.js';
 
-import {getSitePermissionsPatternFromSite, SitePermissionsEditDialogElement} from 'chrome://extensions/extensions.js';
+import {getSitePermissionsPatternFromSite, SitePermissionsEditUrlDialogElement} from 'chrome://extensions/extensions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 
-suite('SitePermissionsEditDialog', function() {
-  let element: SitePermissionsEditDialogElement;
+suite('SitePermissionsEditUrlDialog', function() {
+  let element: SitePermissionsEditUrlDialogElement;
   let delegate: TestService;
 
   setup(function() {
     delegate = new TestService();
 
     document.body.innerHTML = '';
-    element = document.createElement('site-permissions-edit-dialog');
+    element = document.createElement('site-permissions-edit-url-dialog');
     element.delegate = delegate;
     element.siteSet = chrome.developerPrivate.UserSiteSet.PERMITTED;
     document.body.appendChild(element);
@@ -76,6 +76,7 @@
     input.fire('input');
     assertFalse(input.invalid);
 
+    const whenClosed = eventToPromise('close', element);
     const submit = element.$.submit;
     assertFalse(submit.disabled);
     submit.click();
@@ -90,7 +91,7 @@
     assertEquals(chrome.developerPrivate.UserSiteSet.PERMITTED, addedSiteSet);
     assertEquals(newSite, addedSite);
 
-    await eventToPromise('close', element);
+    await whenClosed;
     assertFalse(element.$.dialog.open);
   });
 
diff --git a/chrome/test/data/webui/extensions/site_permissions_list_test.ts b/chrome/test/data/webui/extensions/site_permissions_list_test.ts
index 61ea2ae..9da0ac2 100644
--- a/chrome/test/data/webui/extensions/site_permissions_list_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_list_test.ts
@@ -38,7 +38,7 @@
     flush();
 
     const dialog =
-        element.shadowRoot!.querySelector('site-permissions-edit-dialog');
+        element.shadowRoot!.querySelector('site-permissions-edit-url-dialog');
     assertTrue(!!dialog);
     assertTrue(dialog.$.dialog.open);
   });
@@ -67,30 +67,66 @@
     assertFalse(actionMenu.open);
   });
 
-  test('clicking edit through action menu opens a dialog', async function() {
-    element.sites = ['https://google.com', 'http://www.example.com'];
-    flush();
+  test(
+      'clicking "edit site url" through action menu opens a dialog',
+      async function() {
+        element.sites = ['https://google.com', 'http://www.example.com'];
+        flush();
 
-    const openEditSites =
-        element!.shadowRoot!.querySelectorAll<HTMLElement>('.icon-more-vert');
-    assertEquals(2, openEditSites.length);
-    openEditSites[1]!.click();
+        const openEditSites =
+            element!.shadowRoot!.querySelectorAll<HTMLElement>(
+                '.icon-more-vert');
+        assertEquals(2, openEditSites.length);
+        openEditSites[1]!.click();
 
-    const actionMenu = element.$.siteActionMenu;
-    assertTrue(!!actionMenu);
-    assertTrue(actionMenu.open);
+        const actionMenu = element.$.siteActionMenu;
+        assertTrue(!!actionMenu);
+        assertTrue(actionMenu.open);
 
-    const actionMenuEdit = actionMenu.querySelector<HTMLElement>('#edit-site');
-    assertTrue(!!actionMenuEdit);
+        const actionMenuEditUrl =
+            actionMenu.querySelector<HTMLElement>('#edit-site-url');
+        assertTrue(!!actionMenuEditUrl);
 
-    actionMenuEdit.click();
-    flush();
-    assertFalse(actionMenu.open);
+        actionMenuEditUrl.click();
+        flush();
+        assertFalse(actionMenu.open);
 
-    const dialog =
-        element.shadowRoot!.querySelector('site-permissions-edit-dialog');
-    assertTrue(!!dialog);
-    assertTrue(dialog.$.dialog.open);
-    assertEquals('http://www.example.com', dialog.siteToEdit);
-  });
+        const dialog = element.shadowRoot!.querySelector(
+            'site-permissions-edit-url-dialog');
+        assertTrue(!!dialog);
+        assertTrue(dialog.$.dialog.open);
+        assertEquals('http://www.example.com', dialog.siteToEdit);
+      });
+
+  test(
+      'clicking "edit site permissions" through action menu opens a dialog',
+      async function() {
+        element.sites = ['https://google.com', 'http://www.example.com'];
+        flush();
+
+        const openEditSites =
+            element!.shadowRoot!.querySelectorAll<HTMLElement>(
+                '.icon-more-vert');
+        assertEquals(2, openEditSites.length);
+        openEditSites[1]!.click();
+
+        const actionMenu = element.$.siteActionMenu;
+        assertTrue(!!actionMenu);
+        assertTrue(actionMenu.open);
+
+        const actionMenuEditPermissions =
+            actionMenu.querySelector<HTMLElement>('#edit-site-permissions');
+        assertTrue(!!actionMenuEditPermissions);
+
+        actionMenuEditPermissions.click();
+        flush();
+        assertFalse(actionMenu.open);
+
+        const dialog = element.shadowRoot!.querySelector(
+            'site-permissions-edit-permissions-dialog');
+        assertTrue(!!dialog);
+        assertTrue(dialog.$.dialog.open);
+        assertEquals('http://www.example.com', dialog.site);
+        assertEquals(element.siteSet, dialog.originalSiteSet);
+      });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 126c31a2..fff95334 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -37,6 +37,7 @@
     "ambient_mode_photos_page_test.js",
     "apps_page_test.js",
     "app_notifications_subpage_tests.js",
+    "app_management/app_details_item_test.js",
     "app_management/app_detail_view_test.js",
     "app_management/app_item_test.js",
     "app_management/app_management_page_tests.js",
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/app_details_item_test.js b/chrome/test/data/webui/settings/chromeos/app_management/app_details_item_test.js
new file mode 100644
index 0000000..5b6d416
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/app_management/app_details_item_test.js
@@ -0,0 +1,219 @@
+// 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.
+
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {AppManagementStore, FakePageHandler, updateSelectedAppId, addApp} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {setupFakeHandler, replaceStore, replaceBody, isHidden} from './test_util.m.js';
+// #import {flushTasks} from 'chrome://test/test_util.js';
+// clang-format on
+
+suite('<app-management-app-details-item>', () => {
+  let appDetailsItem;
+  let fakeHandler;
+
+  setup(async function() {
+    fakeHandler = setupFakeHandler();
+    replaceStore();
+
+    appDetailsItem = document.createElement('app-management-app-details-item');
+
+    replaceBody(appDetailsItem);
+    test_util.flushTasks();
+  });
+
+  test('PWA type', async function() {
+    const options = {
+      type: apps.mojom.AppType.kWeb,
+      installSource: apps.mojom.InstallSource.kUnknown,
+    };
+
+    // Add PWA app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText, 'Web App');
+  });
+
+  test('Android type', async function() {
+    const options = {
+      type: apps.mojom.AppType.kArc,
+      installSource: apps.mojom.InstallSource.kUnknown,
+    };
+
+    // Add Android app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'Android App');
+  });
+
+  test('Chrome type', async function() {
+    const options = {
+      type: apps.mojom.AppType.kChromeApp,
+      installSource: apps.mojom.InstallSource.kUnknown,
+    };
+
+    // Add Chrome app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'Chrome App');
+  });
+
+  test('Chrome App from web store', async function() {
+    const options = {
+      type: apps.mojom.AppType.kChromeApp,
+      installSource: apps.mojom.InstallSource.kChromeWebStore,
+    };
+
+    // Add Chrome app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'Chrome App installed from Chrome Web Store');
+  });
+
+  test('Android App from play store', async function() {
+    const options = {
+      type: apps.mojom.AppType.kArc,
+      installSource: apps.mojom.InstallSource.kPlayStore,
+    };
+
+    // Add Chrome app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'Android App installed from Google Play Store');
+  });
+
+  test('System type', async function() {
+    const options = {
+      type: apps.mojom.AppType.kSystemWeb,
+    };
+
+    // Add System app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'System App');
+  });
+
+  test('system install source', async function() {
+    const options = {
+      installSource: apps.mojom.InstallSource.kSystem,
+    };
+
+    // Add System app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app', options);
+
+    app_management.AppManagementStore.getInstance().dispatch(
+        app_management.actions.updateSelectedAppId(app.id));
+
+    await fakeHandler.flushPipesForTesting();
+
+    assertTrue(
+        !!app_management.AppManagementStore.getInstance().data.apps[app.id]);
+
+    appDetailsItem.app = app;
+
+    replaceBody(appDetailsItem);
+    fakeHandler.flushPipesForTesting();
+    test_util.flushTasks();
+
+    expectEquals(
+        appDetailsItem.shadowRoot.querySelector('#type').innerText,
+        'Chrome OS System App');
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 65964665..3f7c6be 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -281,7 +281,23 @@
   }
 };
 
-TEST_F('OSSettingsSearchEngineV3Test', 'AllJsTests', () => {
+var OSSettingsAppManagementAppDetailsV3Test =
+    class extends OSSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/app_details_item_test.m.js';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled: super.featureList.enabled.concat(
+          ['features::kAppManagementAppDetails'])
+    };
+  }
+};
+
+TEST_F('OSSettingsAppManagementAppDetailsV3Test', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -292,6 +308,7 @@
  ['AmbientModePhotosPage', 'ambient_mode_photos_page_test.m.js'],
  ['AppsPage', 'apps_page_test.m.js'],
  ['AppNotificationsSubpage', 'app_notifications_subpage_tests.m.js'],
+ ['AppManagementAppDetailsItem', 'app_details_item_test.m.js'],
  ['AppManagementAppDetailView', 'app_detail_view_test.m.js'],
  ['AppManagementAppItem', 'app_item_test.m.js'],
  ['AppManagementArcDetailView', 'arc_detail_view_test.m.js'],
diff --git a/chrome/test/enterprise/OWNERS b/chrome/test/enterprise/OWNERS
index 1788c1a..ecf5f278 100644
--- a/chrome/test/enterprise/OWNERS
+++ b/chrome/test/enterprise/OWNERS
@@ -1,2 +1,4 @@
+# When making changes, also update TestOwners in the GwsQ config:
+# http://google3/chrome/enterprise/gwsq/enterprise-policy-review.gwsq
 jxiang@google.com
 eswarank@google.com
diff --git a/chrome/test/enterprise/e2e/OWNERS b/chrome/test/enterprise/e2e/OWNERS
index 1788c1a..ecf5f278 100644
--- a/chrome/test/enterprise/e2e/OWNERS
+++ b/chrome/test/enterprise/e2e/OWNERS
@@ -1,2 +1,4 @@
+# When making changes, also update TestOwners in the GwsQ config:
+# http://google3/chrome/enterprise/gwsq/enterprise-policy-review.gwsq
 jxiang@google.com
 eswarank@google.com
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 48fdd6f..3af59403 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -291,6 +291,7 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
+  IgnoreNetworkServiceCrashes();
   network_service_test->CrashOnResolveHost("crash.com");
 
   RunTestViaHTTP(STRIP_PREFIXES(TCPSocketPrivateCrash_Resolve));
@@ -1281,6 +1282,7 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
+  IgnoreNetworkServiceCrashes();
   network_service_test->CrashOnResolveHost("crash.com");
 
   RunTestViaHTTP(STRIP_PREFIXES(HostResolverCrash_Basic));
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
index f2aac74..e18e7771 100644
--- a/chrome/updater/installer.cc
+++ b/chrome/updater/installer.cc
@@ -97,7 +97,7 @@
   component.crx_format_requirement = crx_verifier_format_;
   component.app_id = app_id_;
   component.ap = ap_;
-  component.ap = persisted_data_->GetBrandCode(app_id_);
+  component.brand = persisted_data_->GetBrandCode(app_id_);
   component.name = app_id_;
   component.version = pv_;
   component.fingerprint = fingerprint_;
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 0f5f0f3..1bc53a45 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -163,6 +163,7 @@
   "$common_java_src_dir/org/chromium/chromecast/shell/CastAccessibilityHelper.java",
   "$common_java_src_dir/org/chromium/chromecast/shell/CastApplication.java",
   "$common_java_src_dir/org/chromium/chromecast/shell/CastBrowserHelper.java",
+  "$common_java_src_dir/org/chromium/chromecast/shell/CastBrowserService.java",
   "$common_java_src_dir/org/chromium/chromecast/shell/CastCommandLineHelper.java",
   "$common_java_src_dir/org/chromium/chromecast/shell/CastContentWindowAndroid.java",
   "$common_java_src_dir/org/chromium/chromecast/shell/CastCrashHandler.java",
diff --git a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
index c99215ec..d3ba6cae 100644
--- a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
+++ b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
@@ -6,6 +6,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
           package="org.chromium.chromecast.shell">
 
     <uses-sdk android:minSdkVersion="23"/>
@@ -14,7 +15,8 @@
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
-    <application android:icon="@drawable/ic_settings_cast">
+    <application android:name=".CastApplication"
+                 android:icon="@drawable/ic_settings_cast">
         <activity android:name="org.chromium.chromecast.shell.CastWebContentsActivity"
                   android:theme="@style/CastShellTheme"
                   android:exported="true"
@@ -27,6 +29,11 @@
                   android:excludeFromRecents="false"
                   android:noHistory="false">
         </activity>
+        <service android:name=".CastBrowserService"
+                 android:hardwareAccelerated="true"
+                 android:label="Chromecast Web Browser"
+                 android:exported="true"
+                 tools:ignore="ExportedService"/>
 
         <!-- The following service entries exist in order to allow us to
              start more than one sandboxed process. -->
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
index a71ab6d..2e9aab2 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
@@ -5,13 +5,9 @@
 package org.chromium.chromecast.shell;
 
 import android.content.Context;
-import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
-import android.media.audiopolicy.AudioPolicy;
-import android.os.Build.VERSION_CODES;
 
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Log;
@@ -87,30 +83,8 @@
         return audioFocusLossState;
     }
 
-    public int getStreamMaxVolume(int streamType) {
-        return mInternal.getStreamMaxVolume(streamType);
-    }
-
-    public int registerAudioPolicy(AudioPolicy audioPolicy) {
-        return mInternal.registerAudioPolicy(audioPolicy);
-    }
-
-    public void unregisterAudioPolicyAsync(AudioPolicy audioPolicy) {
-        mInternal.unregisterAudioPolicyAsync(audioPolicy);
-    }
-
-    public void unregisterAudioPolicy(AudioPolicy audioPolicy) {
-        mInternal.unregisterAudioPolicy(audioPolicy);
-    }
-
-    @RequiresApi(VERSION_CODES.M)
-    public AudioDeviceInfo[] getDevices(int flags) {
-        return mInternal.getDevices(flags);
-    }
-
-    // TODO(sanfin): Do not expose this. All needed AudioManager methods can be adapted with
-    // CastAudioManager.
-    public AudioManager getInternal() {
+    @VisibleForTesting
+    AudioManager getInternal() {
         return mInternal;
     }
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserService.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserService.java
new file mode 100644
index 0000000..4bebda7
--- /dev/null
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserService.java
@@ -0,0 +1,26 @@
+// 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.
+
+package org.chromium.chromecast.shell;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * This Service allows launching the Cast browser module through an Intent. When the service is
+ * created, the browser main loop will start. This allows launching the browser module separately
+ * from the base module, so that the memory overhead of the browser is only incurred when needed.
+ */
+public class CastBrowserService extends Service {
+    @Override
+    public void onCreate() {
+        CastBrowserHelper.initializeBrowser(getApplicationContext());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/chromecast/media/audio/mixer_service/BUILD.gn b/chromecast/media/audio/mixer_service/BUILD.gn
index e078da5..97ab59a 100644
--- a/chromecast/media/audio/mixer_service/BUILD.gn
+++ b/chromecast/media/audio/mixer_service/BUILD.gn
@@ -69,6 +69,7 @@
     "//base",
     "//chromecast/media/audio/net:common",
     "//chromecast/media/audio/net:proto",
+    "//chromecast/metrics:metrics_recorder",
     "//chromecast/net:io_buffer_pool",
     "//net",
   ]
diff --git a/chromecast/media/audio/mixer_service/DEPS b/chromecast/media/audio/mixer_service/DEPS
index 4056fc02..aae5cc34 100644
--- a/chromecast/media/audio/mixer_service/DEPS
+++ b/chromecast/media/audio/mixer_service/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chromecast/media/audio/net",
+  "+chromecast/metrics",
   "+chromecast/net",
   "+net",
 ]
diff --git a/chromecast/media/audio/mixer_service/output_stream_connection.cc b/chromecast/media/audio/mixer_service/output_stream_connection.cc
index f1573795..d21cbb1 100644
--- a/chromecast/media/audio/mixer_service/output_stream_connection.cc
+++ b/chromecast/media/audio/mixer_service/output_stream_connection.cc
@@ -12,6 +12,7 @@
 #include "chromecast/media/audio/mixer_service/mixer_service_transport.pb.h"
 #include "chromecast/media/audio/net/common.pb.h"
 #include "chromecast/media/audio/net/conversions.h"
+#include "chromecast/metrics/metrics_recorder.h"
 #include "chromecast/net/io_buffer_pool.h"
 
 namespace chromecast {
@@ -236,6 +237,12 @@
   }
 
   if (message.has_mixer_underrun()) {
+    std::string metric_name =
+        (message.mixer_underrun().type() == MixerUnderrun::INPUT_UNDERRUN
+             ? "Platform.Audio.Mixer.StreamUnderrun"
+             : "Platform.Audio.Mixer.OutputUnderrun");
+    RecordCastEvent(metric_name, CreateCastEvent(metric_name),
+                    /* verbose_log_level = */ 0);
     delegate_->OnMixerUnderrun(static_cast<Delegate::MixerUnderrunType>(
         message.mixer_underrun().type()));
   }
diff --git a/chromeos/components/multidevice/BUILD.gn b/chromeos/components/multidevice/BUILD.gn
index 9970a42..98781b8 100644
--- a/chromeos/components/multidevice/BUILD.gn
+++ b/chromeos/components/multidevice/BUILD.gn
@@ -27,18 +27,18 @@
   ]
 
   deps = [
+    "//ash/services/device_sync/proto:util",
     "//base",
     "//base:i18n",
     "//chromeos/components/multidevice/logging",
     "//chromeos/dbus",
     "//chromeos/dbus/easy_unlock",
-    "//chromeos/services/device_sync/proto:util",
     "//components/prefs",
   ]
 
   public_deps = [
+    "//ash/services/device_sync/proto",
     "//base",
-    "//chromeos/services/device_sync/proto",
     "//third_party/securemessage/proto",
   ]
 }
@@ -52,8 +52,8 @@
   deps = [
     ":multidevice",
     "//ash/constants",
+    "//ash/services/device_sync/proto",
     "//base",
-    "//chromeos/services/device_sync/proto",
   ]
 }
 
@@ -70,8 +70,8 @@
   public_deps = [ ":multidevice" ]
 
   deps = [
+    "//ash/services/device_sync/proto",
     "//base",
-    "//chromeos/services/device_sync/proto",
   ]
 }
 
@@ -88,10 +88,10 @@
   deps = [
     ":multidevice",
     ":test_support",
+    "//ash/services/device_sync/proto",
     "//base/test:test_support",
     "//chromeos/components/multidevice/logging:unit_tests",
     "//chromeos/components/multidevice/mojom:unit_tests",
-    "//chromeos/services/device_sync/proto",
     "//testing/gtest",
   ]
 }
diff --git a/chromeos/components/multidevice/DEPS b/chromeos/components/multidevice/DEPS
index bbcf3a6..6583fd24 100644
--- a/chromeos/components/multidevice/DEPS
+++ b/chromeos/components/multidevice/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/services/device_sync/proto",
   "+device/bluetooth/public/cpp",
   "+mojo/public/cpp",
   "+third_party/securemessage",
diff --git a/chromeos/components/multidevice/beacon_seed.h b/chromeos/components/multidevice/beacon_seed.h
index 53d0a16f..8cd26a49 100644
--- a/chromeos/components/multidevice/beacon_seed.h
+++ b/chromeos/components/multidevice/beacon_seed.h
@@ -10,9 +10,9 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "base/time/time.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/mojom/BUILD.gn b/chromeos/components/multidevice/mojom/BUILD.gn
index b8ea6e2f..02ace131 100644
--- a/chromeos/components/multidevice/mojom/BUILD.gn
+++ b/chromeos/components/multidevice/mojom/BUILD.gn
@@ -30,8 +30,8 @@
         "//chromeos/components/multidevice",
       ]
       traits_deps = [
+        "//ash/services/device_sync/proto",
         "//chromeos/components/multidevice/logging",
-        "//chromeos/services/device_sync/proto",
         "//device/bluetooth/public/cpp",
       ]
     },
diff --git a/chromeos/components/multidevice/software_feature.h b/chromeos/components/multidevice/software_feature.h
index bbb869c..5c33e1a 100644
--- a/chromeos/components/multidevice/software_feature.h
+++ b/chromeos/components/multidevice/software_feature.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.cc b/chromeos/dbus/dlcservice/dlcservice_client.cc
index d3756a5..52699c9 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client.cc
@@ -110,30 +110,31 @@
 
   ~DlcserviceClientImpl() override = default;
 
-  void Install(const std::string& dlc_id,
+  void Install(const dlcservice::InstallRequest& install_request,
                InstallCallback install_callback,
                ProgressCallback progress_callback) override {
     CheckServiceAvailable("Install");
+    const std::string& id = install_request.id();
     // If another installation for the same DLC ID was already called, go ahead
     // and hold the installation fields.
-    if (installation_holder_.find(dlc_id) != installation_holder_.end()) {
-      HoldInstallation(dlc_id, std::move(install_callback),
+    if (installation_holder_.find(id) != installation_holder_.end()) {
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
     if (installing_) {
-      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
-                                 weak_ptr_factory_.GetWeakPtr(),
-                                 std::move(dlc_id), std::move(install_callback),
-                                 std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(
+          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
+          std::move(install_request), std::move(install_callback),
+          std::move(progress_callback)));
       return;
     }
 
     TaskStarted();
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
-                                 dlcservice::kInstallDlcMethod);
+                                 dlcservice::kInstallMethod);
     dbus::MessageWriter writer(&method_call);
-    writer.AppendString(dlc_id);
+    writer.AppendProtoAsArrayOfBytes(install_request);
 
     VLOG(1) << "Requesting to install DLC(s).";
     // TODO(b/166782419): dlcservice hashes preloadable DLC images which can
@@ -142,9 +143,10 @@
     constexpr int timeout_ms = 5 * 60 * 1000;
     dlcservice_proxy_->CallMethodWithErrorResponse(
         &method_call, timeout_ms,
-        base::BindOnce(
-            &DlcserviceClientImpl::OnInstall, weak_ptr_factory_.GetWeakPtr(),
-            dlc_id, std::move(install_callback), std::move(progress_callback)));
+        base::BindOnce(&DlcserviceClientImpl::OnInstall,
+                       weak_ptr_factory_.GetWeakPtr(), install_request,
+                       std::move(install_callback),
+                       std::move(progress_callback)));
   }
 
   void Uninstall(const std::string& dlc_id,
@@ -235,12 +237,12 @@
   // Fields related to an installation allowing for multiple installations to be
   // in flight concurrently and handled by this dlcservice client. The callbacks
   // are used to report progress and the final installation.
-  struct InstallationCallbacks {
+  struct InstallationHolder {
     InstallCallback install_callback;
     ProgressCallback progress_callback;
 
-    InstallationCallbacks(InstallCallback install_callback,
-                          ProgressCallback progress_callback)
+    InstallationHolder(InstallCallback install_callback,
+                       ProgressCallback progress_callback)
         : install_callback(std::move(install_callback)),
           progress_callback(std::move(progress_callback)) {}
   };
@@ -260,11 +262,11 @@
   // Clears any state an installation had setup while being performed.
   void TaskEnded() { installing_ = false; }
 
-  void HoldInstallation(const std::string& id,
+  void HoldInstallation(const dlcservice::InstallRequest& install_request,
                         InstallCallback install_callback,
                         ProgressCallback progress_callback) {
-    installation_holder_[id].emplace_back(std::move(install_callback),
-                                          std::move(progress_callback));
+    installation_holder_[install_request.id()].emplace_back(
+        std::move(install_callback), std::move(progress_callback));
   }
 
   void ReleaseInstallation(const std::string& id) {
@@ -357,27 +359,29 @@
     LOG_IF(ERROR, !success) << "Failed to connect to DlcStateChanged signal.";
   }
 
-  void OnInstall(const std::string& dlc_id,
+  void OnInstall(const dlcservice::InstallRequest& install_request,
                  InstallCallback install_callback,
                  ProgressCallback progress_callback,
                  dbus::Response* response,
                  dbus::ErrorResponse* err_response) {
+    const std::string& id = install_request.id();
     if (response) {
-      HoldInstallation(dlc_id, std::move(install_callback),
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
 
     const auto err = DlcserviceErrorResponseHandler(err_response).get_err();
     if (err == dlcservice::kErrorBusy) {
-      EnqueueTask(base::BindOnce(
-          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
-          dlc_id, std::move(install_callback), std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 install_request, std::move(install_callback),
+                                 std::move(progress_callback)));
     } else {
-      HoldInstallation(dlc_id, std::move(install_callback),
+      HoldInstallation(install_request, std::move(install_callback),
                        std::move(progress_callback));
       dlcservice::DlcState dlc_state;
-      dlc_state.set_id(dlc_id);
+      dlc_state.set_id(id);
       dlc_state.set_last_error_code(err);
       SendCompleted(dlc_state);
     }
@@ -436,9 +440,8 @@
                    << " called when dlcservice is not available.";
   }
 
-  // DLC ID to |InstallationCallbacks| mapping.
-  std::map<std::string, std::vector<InstallationCallbacks>>
-      installation_holder_;
+  // DLC ID to `InstallationHolder` mapping.
+  std::map<std::string, std::vector<InstallationHolder>> installation_holder_;
 
   dbus::ObjectProxy* dlcservice_proxy_;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.h b/chromeos/dbus/dlcservice/dlcservice_client.h
index ed1562a..4be5671 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/dlcservice_client.h
@@ -88,7 +88,7 @@
 
   // Installs the DLC passed in while reporting progress through the progress
   // callback and only calls install callback on install success/failure.
-  virtual void Install(const std::string& dlc_id,
+  virtual void Install(const dlcservice::InstallRequest& install_request,
                        InstallCallback callback,
                        ProgressCallback progress_callback) = 0;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
index 883bf83..c934428 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
@@ -95,6 +95,17 @@
   }
 
  protected:
+  dlcservice::InstallRequest CreateInstallRequest(
+      const std::string& id = {},
+      const std::string& omaha_url = {},
+      bool reserve = false) {
+    dlcservice::InstallRequest install_request;
+    install_request.set_id(id);
+    install_request.set_omaha_url(omaha_url);
+    install_request.set_reserve(reserve);
+    return install_request;
+  }
+
   base::test::SingleThreadTaskEnvironment task_environment_;
   DlcserviceClient* client_;
   scoped_refptr<dbus::MockBus> mock_bus_;
@@ -332,7 +343,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -351,7 +363,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorInternal, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -366,7 +379,7 @@
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
   responses_.push_back(dbus::Response::CreateEmpty());
-  client_->Install({}, std::move(install_callback),
+  client_->Install(CreateInstallRequest(), std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -389,7 +402,7 @@
   DlcserviceClient::ProgressCallback progress_callback = base::BindRepeating(
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
-  client_->Install({"foo"}, std::move(install_callback),
+  client_->Install(CreateInstallRequest("foo"), std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -421,7 +434,8 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
+  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
+                   base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -442,7 +456,8 @@
         },
         &counter);
     responses_.push_back(dbus::Response::CreateEmpty());
-    client_->Install({}, std::move(install_callback), base::DoNothing());
+    client_->Install(CreateInstallRequest(), std::move(install_callback),
+                     base::DoNothing());
   }
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
index a7f888e..f372ddd 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
@@ -14,16 +14,18 @@
 
 FakeDlcserviceClient::~FakeDlcserviceClient() = default;
 
-void FakeDlcserviceClient::Install(const std::string& dlc_id,
-                                   InstallCallback callback,
-                                   ProgressCallback progress_callback) {
+void FakeDlcserviceClient::Install(
+    const dlcservice::InstallRequest& install_request,
+    InstallCallback callback,
+    ProgressCallback progress_callback) {
   VLOG(1) << "Requesting to install DLC(s).";
+  const std::string& id = install_request.id();
   InstallResult install_result{
       .error = install_err_,
-      .dlc_id = dlc_id,
+      .dlc_id = id,
       .root_path = install_root_path_,
   };
-  dlcs_with_content_.add_dlc_infos()->set_id(dlc_id);
+  dlcs_with_content_.add_dlc_infos()->set_id(id);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), std::move(install_result)));
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.h b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
index 336418c..c8deab4 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
@@ -22,7 +22,7 @@
   ~FakeDlcserviceClient() override;
 
   // DlcserviceClient:
-  void Install(const std::string& dlc_id,
+  void Install(const dlcservice::InstallRequest& install_request,
                InstallCallback callback,
                ProgressCallback progress_callback) override;
   // Uninstalling disables the DLC.
diff --git a/chromeos/language/language_packs/language_pack_manager.cc b/chromeos/language/language_packs/language_pack_manager.cc
index f8bbdb11..8dbd320e 100644
--- a/chromeos/language/language_packs/language_pack_manager.cc
+++ b/chromeos/language/language_packs/language_pack_manager.cc
@@ -209,8 +209,11 @@
     return;
   }
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(dlc_id);
   DlcserviceClient::Get()->Install(
-      dlc_id, base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
+      install_request,
+      base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
       base::DoNothing());
 }
 
diff --git a/chromeos/memory/OWNERS b/chromeos/memory/OWNERS
index 8e5f5711..efb8db2 100644
--- a/chromeos/memory/OWNERS
+++ b/chromeos/memory/OWNERS
@@ -1,5 +1,4 @@
 bgeffon@chromium.org
-sonnyrao@chromium.org
 yuzhao@chromium.org
 jsbarnes@chromium.org
 vovoy@chromium.org
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 8cd25ec..6a673e632 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -155,11 +155,11 @@
 
   deps = [
     "//ash/constants",
+    "//ash/services/device_sync/proto:util",
     "//base",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
     "//chromeos/network",
-    "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
     "//components/gcm_driver",
@@ -238,8 +238,8 @@
 
   deps = [
     ":device_sync",
+    "//ash/services/device_sync/proto:test_support",
     "//base",
-    "//chromeos/services/device_sync/proto:test_support",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
     "//testing/gmock",
@@ -304,6 +304,8 @@
     ":feature_status_change",
     ":test_support",
     "//ash/constants",
+    "//ash/services/device_sync/proto:test_support",
+    "//ash/services/device_sync/proto:util",
     "//base",
     "//base/test:test_support",
     "//chromeos/components/multidevice",
@@ -311,8 +313,6 @@
     "//chromeos/dbus:test_support",
     "//chromeos/network",
     "//chromeos/network:test_support",
-    "//chromeos/services/device_sync/proto:test_support",
-    "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp:prefs",
     "//chromeos/services/device_sync/public/cpp:test_support",
     "//chromeos/services/device_sync/public/cpp:unit_tests",
diff --git a/chromeos/services/device_sync/DEPS b/chromeos/services/device_sync/DEPS
index 13d72dd0..2ac9462 100644
--- a/chromeos/services/device_sync/DEPS
+++ b/chromeos/services/device_sync/DEPS
@@ -1,3 +1,6 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //ash/services/device_sync should be updated as well. We need to sync both
+# files until the migration of //chromeos/services/device_sync is done.
 include_rules = [
   "+components/gcm_driver",
   "+components/proximity_auth/logging",
@@ -10,4 +13,7 @@
   "+services/network/test",
   "+components/cryptauth",
   "+third_party/securemessage",
+
+  # TODO(https://crbug.com/1164001): Remove this deps when migration is done.
+  "+ash/services/device_sync",
 ]
diff --git a/chromeos/services/device_sync/DIR_METADATA b/chromeos/services/device_sync/DIR_METADATA
index dd4c190..d0f66148 100644
--- a/chromeos/services/device_sync/DIR_METADATA
+++ b/chromeos/services/device_sync/DIR_METADATA
@@ -1,3 +1,6 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //ash/services/device_sync should be updated as well. We need to sync both
+# files until the migration of //chromeos/services/device_sync is done.
 buganizer {
   component_id: 1108889
 }
diff --git a/chromeos/services/device_sync/OWNERS b/chromeos/services/device_sync/OWNERS
index 4b6c4d12..9c9a9233 100644
--- a/chromeos/services/device_sync/OWNERS
+++ b/chromeos/services/device_sync/OWNERS
@@ -1,3 +1,6 @@
+# TODO(https://crbug.com/1164001): When this file is edited, same file in
+# //ash/services/device_sync should be updated as well. We need to sync both
+# files until the migration of //chromeos/services/device_sync is done.
 file://chromeos/components/multidevice/OWNERS
 
 per-file *_type_converter*.*=set noparent
diff --git a/chromeos/services/device_sync/cryptauth_client.h b/chromeos/services/device_sync/cryptauth_client.h
index 35cfe0a..6aa7e5f 100644
--- a/chromeos/services/device_sync/cryptauth_client.h
+++ b/chromeos/services/device_sync/cryptauth_client.h
@@ -57,9 +57,9 @@
 // Implmentations shall only processes a single request, so create a new
 // instance for each request you make. DO NOT REUSE.
 // For documentation on each API call, see
-// chromeos/services/device_sync/proto/cryptauth_api.proto,
-// chromeos/services/device_sync/proto/cryptauth_enrollment.proto, and
-// chromeos/services/device_sync/proto/cryptauth_devicesync.proto.
+// ash/services/device_sync/proto/cryptauth_api.proto,
+// ash/services/device_sync/proto/cryptauth_enrollment.proto, and
+// ash/services/device_sync/proto/cryptauth_devicesync.proto.
 class CryptAuthClient {
  public:
   typedef base::OnceCallback<void(NetworkRequestError)> ErrorCallback;
diff --git a/chromeos/services/device_sync/cryptauth_client_impl.cc b/chromeos/services/device_sync/cryptauth_client_impl.cc
index a3ddc3a..d5f8ac77 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_client_impl.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
 #include "chromeos/services/device_sync/switches.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
diff --git a/chromeos/services/device_sync/cryptauth_client_impl.h b/chromeos/services/device_sync/cryptauth_client_impl.h
index a8cb3226..d8f0cfe 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl.h
+++ b/chromeos/services/device_sync/cryptauth_client_impl.h
@@ -9,10 +9,10 @@
 #include <utility>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/device_sync/cryptauth_api_call_flow.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
index 47dd62c..943ebcc 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
@@ -9,6 +9,11 @@
 #include <utility>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
@@ -18,11 +23,6 @@
 #include "base/test/null_task_runner.h"
 #include "base/test/task_environment.h"
 #include "chromeos/services/device_sync/cryptauth_api_call_flow.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "chromeos/services/device_sync/switches.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/chromeos/services/device_sync/cryptauth_device.cc b/chromeos/services/device_sync/cryptauth_device.cc
index 0588d0a..518f569 100644
--- a/chromeos/services/device_sync/cryptauth_device.cc
+++ b/chromeos/services/device_sync/cryptauth_device.cc
@@ -6,11 +6,11 @@
 
 #include <sstream>
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/i18n/time_formatting.h"
 #include "base/json/values_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_device.h b/chromeos/services/device_sync/cryptauth_device.h
index 5dd77ea..ce44e49 100644
--- a/chromeos/services/device_sync/cryptauth_device.h
+++ b/chromeos/services/device_sync/cryptauth_device.h
@@ -9,11 +9,11 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc
index 2cc229f0..db304be8 100644
--- a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc
@@ -7,6 +7,7 @@
 #include <array>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -19,7 +20,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
 #include "chromeos/services/device_sync/device_sync_type_converters.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h
index cca7aa47a..bd7a4a8e 100644
--- a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h
@@ -8,11 +8,11 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_activity_getter.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "chromeos/services/device_sync/public/cpp/client_app_metadata_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc
index dd76552..b69b16619 100644
--- a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc
@@ -9,6 +9,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
 #include "base/timer/mock_timer.h"
@@ -21,9 +24,6 @@
 #include "chromeos/services/device_sync/device_sync_type_converters.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_manager.h b/chromeos/services/device_sync/cryptauth_device_manager.h
index 0b1108d..62887d1 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager.h
+++ b/chromeos/services/device_sync/cryptauth_device_manager.h
@@ -7,8 +7,8 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/observer_list.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 class PrefRegistrySimple;
 
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
index e29241b..d75bbbf 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
@@ -10,6 +10,7 @@
 #include <stdexcept>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
@@ -21,7 +22,6 @@
 #include "chromeos/components/multidevice/software_feature_state.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "chromeos/services/device_sync/sync_scheduler_impl.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.h b/chromeos/services/device_sync/cryptauth_device_manager_impl.h
index 8f64c3cfa..1e1cedf 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
@@ -14,7 +15,6 @@
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/sync_scheduler.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index 7337d412..c5b3450 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/base64url.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
@@ -24,7 +25,6 @@
 #include "chromeos/services/device_sync/mock_sync_scheduler.h"
 #include "chromeos/services/device_sync/network_request_error.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_notifier.h b/chromeos/services/device_sync/cryptauth_device_notifier.h
index 5a2f485..49c1229 100644
--- a/chromeos/services/device_sync/cryptauth_device_notifier.h
+++ b/chromeos/services/device_sync/cryptauth_device_notifier.h
@@ -7,11 +7,11 @@
 
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_device_notifier_impl.h b/chromeos/services/device_sync/cryptauth_device_notifier_impl.h
index 6d17c445..bbaf2d9 100644
--- a/chromeos/services/device_sync/cryptauth_device_notifier_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_notifier_impl.h
@@ -9,6 +9,8 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/queue.h"
@@ -16,8 +18,6 @@
 #include "chromeos/services/device_sync/cryptauth_device_notifier.h"
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_device_notifier_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_notifier_impl_unittest.cc
index 43a9a5b..4ae5439 100644
--- a/chromeos/services/device_sync/cryptauth_device_notifier_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_notifier_impl_unittest.cc
@@ -8,6 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/queue.h"
@@ -19,9 +22,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_device_registry_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_registry_impl_unittest.cc
index 7aaf4d7..08d9b90 100644
--- a/chromeos/services/device_sync/cryptauth_device_registry_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_registry_impl_unittest.cc
@@ -9,6 +9,8 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -16,8 +18,6 @@
 #include "chromeos/components/multidevice/software_feature_state.h"
 #include "chromeos/services/device_sync/cryptauth_device_registry.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_sync_result.h b/chromeos/services/device_sync/cryptauth_device_sync_result.h
index a1c492c4..e596b8c 100644
--- a/chromeos/services/device_sync/cryptauth_device_sync_result.h
+++ b/chromeos/services/device_sync/cryptauth_device_sync_result.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
index a763235..3cfa88a 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
@@ -20,8 +22,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_registry.h"
 #include "chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h"
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/synced_bluetooth_address_tracker.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
index a8020b7..1c6e2de 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
@@ -10,6 +10,9 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -23,9 +26,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/cryptauth_metadata_syncer.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
index a3887ef7..7722a96c 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
@@ -8,6 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/contains.h"
 #include "base/no_destructor.h"
 #include "base/timer/mock_timer.h"
@@ -37,9 +40,6 @@
 #include "chromeos/services/device_sync/fake_synced_bluetooth_address_tracker.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_unittest.cc b/chromeos/services/device_sync/cryptauth_device_unittest.cc
index d4f7638..82162dae 100644
--- a/chromeos/services/device_sync/cryptauth_device_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <map>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/time/time.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_enroller.h b/chromeos/services/device_sync/cryptauth_enroller.h
index 533a236..d1bf7be 100644
--- a/chromeos/services/device_sync/cryptauth_enroller.h
+++ b/chromeos/services/device_sync/cryptauth_enroller.h
@@ -8,8 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/callback_forward.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_enroller_impl.h b/chromeos/services/device_sync/cryptauth_enroller_impl.h
index 1e46bf6d..f6d1e3a 100644
--- a/chromeos/services/device_sync/cryptauth_enroller_impl.h
+++ b/chromeos/services/device_sync/cryptauth_enroller_impl.h
@@ -7,11 +7,11 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/device_sync/cryptauth_enroller.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_manager.h b/chromeos/services/device_sync/cryptauth_enrollment_manager.h
index a703ff0..ff520f3 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_manager.h
+++ b/chromeos/services/device_sync/cryptauth_enrollment_manager.h
@@ -8,9 +8,9 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.cc b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.cc
index c97bde0..c012687 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/clock.h"
@@ -17,7 +18,6 @@
 #include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "chromeos/services/device_sync/cryptauth_enroller.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "chromeos/services/device_sync/sync_scheduler_impl.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h
index b8c962d..03baef8 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h
@@ -8,12 +8,12 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/sync_scheduler.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_result.h b/chromeos/services/device_sync/cryptauth_enrollment_result.h
index 5ad9a66..ad98ee1d 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_result.h
+++ b/chromeos/services/device_sync/cryptauth_enrollment_result.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
index b5a1f9b..6308664 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
@@ -8,13 +8,13 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/containers/flat_set.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_feature_status_getter.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
index 32342f61..99afefd 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
@@ -8,6 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
@@ -22,9 +25,6 @@
 #include "chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.cc b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.cc
index 6c2ba4a..6590b0c 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -15,7 +16,6 @@
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h
index ff539bcf2..d5bd16a 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h
+++ b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h
@@ -9,6 +9,7 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/callback.h"
 #include "base/containers/queue.h"
 #include "base/time/time.h"
@@ -17,7 +18,6 @@
 #include "chromeos/services/device_sync/cryptauth_feature_status_setter.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl_unittest.cc
index 58f2958..991a28f 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_setter_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_setter_impl_unittest.cc
@@ -8,6 +8,10 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/queue.h"
 #include "base/no_destructor.h"
 #include "base/timer/mock_timer.h"
@@ -17,10 +21,6 @@
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chromeos/services/device_sync/cryptauth_gcm_manager_impl.cc b/chromeos/services/device_sync/cryptauth_gcm_manager_impl.cc
index 8f0a497..ed67e1fd 100644
--- a/chromeos/services/device_sync/cryptauth_gcm_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_gcm_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/services/device_sync/cryptauth_gcm_manager_impl.h"
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -13,7 +14,6 @@
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_constants.h"
 #include "components/gcm_driver/gcm_driver.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_gcm_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_gcm_manager_impl_unittest.cc
index 5ffbe27..0bdd422 100644
--- a/chromeos/services/device_sync/cryptauth_gcm_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_gcm_manager_impl_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "chromeos/services/device_sync/cryptauth_gcm_manager_impl.h"
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/strings/string_number_conversions.h"
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/gcm_driver/gcm_client.h"
 #include "components/prefs/testing_pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h
index 0a0c1006..85773453 100644
--- a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h
+++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h
@@ -9,13 +9,13 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_ecies_encryptor.h"
 #include "chromeos/services/device_sync/cryptauth_group_private_key_sharer.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
index b5b70a2..6b37921 100644
--- a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
@@ -8,6 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
@@ -23,9 +26,6 @@
 #include "chromeos/services/device_sync/fake_ecies_encryption.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/cryptauth_key.h b/chromeos/services/device_sync/cryptauth_key.h
index 57c91b0..ab5f43f 100644
--- a/chromeos/services/device_sync/cryptauth_key.h
+++ b/chromeos/services/device_sync/cryptauth_key.h
@@ -5,8 +5,8 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_H_
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/values.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_key_bundle.h b/chromeos/services/device_sync/cryptauth_key_bundle.h
index da4226c..5f41b43 100644
--- a/chromeos/services/device_sync/cryptauth_key_bundle.h
+++ b/chromeos/services/device_sync/cryptauth_key_bundle.h
@@ -5,11 +5,11 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_
 
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/values.h"
 #include "chromeos/services/device_sync/cryptauth_key.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_key_creator.h b/chromeos/services/device_sync/cryptauth_key_creator.h
index 9e80ee2..d79667a 100644
--- a/chromeos/services/device_sync/cryptauth_key_creator.h
+++ b/chromeos/services/device_sync/cryptauth_key_creator.h
@@ -7,11 +7,11 @@
 
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "chromeos/services/device_sync/cryptauth_key.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_key_creator_impl.cc b/chromeos/services/device_sync/cryptauth_key_creator_impl.cc
index 2c459dc..73c899c 100644
--- a/chromeos/services/device_sync/cryptauth_key_creator_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_key_creator_impl.cc
@@ -4,12 +4,12 @@
 
 #include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_constants.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "crypto/hkdf.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_key_creator_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_key_creator_impl_unittest.cc
index f687d56..0d0ce7a6 100644
--- a/chromeos/services/device_sync/cryptauth_key_creator_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_key_creator_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/containers/flat_map.h"
 #include "chromeos/components/multidevice/fake_secure_message_delegate.h"
@@ -16,7 +17,6 @@
 #include "chromeos/services/device_sync/cryptauth_key.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/cryptauth_key_creator.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "crypto/hkdf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/cryptauth_key_proof_computer_impl.cc b/chromeos/services/device_sync/cryptauth_key_proof_computer_impl.cc
index b1c9fc3..13572051 100644
--- a/chromeos/services/device_sync/cryptauth_key_proof_computer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_key_proof_computer_impl.cc
@@ -6,12 +6,12 @@
 
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/containers/span.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/device_sync/cryptauth_key.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/ec_signature_creator.h"
 #include "crypto/hkdf.h"
diff --git a/chromeos/services/device_sync/cryptauth_key_proof_computer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_key_proof_computer_impl_unittest.cc
index d0ba42d..6ce362e 100644
--- a/chromeos/services/device_sync/cryptauth_key_proof_computer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_key_proof_computer_impl_unittest.cc
@@ -6,10 +6,10 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/cryptauth_key.h"
 #include "chromeos/services/device_sync/cryptauth_key_proof_computer.h"
 #include "chromeos/services/device_sync/cryptauth_key_proof_computer_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/ec_signature_creator.h"
 #include "crypto/hmac.h"
diff --git a/chromeos/services/device_sync/cryptauth_key_registry.h b/chromeos/services/device_sync/cryptauth_key_registry.h
index c27898d..3ffc797 100644
--- a/chromeos/services/device_sync/cryptauth_key_registry.h
+++ b/chromeos/services/device_sync/cryptauth_key_registry.h
@@ -5,9 +5,9 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_REGISTRY_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_REGISTRY_H_
 
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "base/containers/flat_map.h"
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_metadata_syncer.h b/chromeos/services/device_sync/cryptauth_metadata_syncer.h
index 3a61a3f..af6e999 100644
--- a/chromeos/services/device_sync/cryptauth_metadata_syncer.h
+++ b/chromeos/services/device_sync/cryptauth_metadata_syncer.h
@@ -7,12 +7,12 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_key.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cryptauthv2 {
diff --git a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.cc
index bbbf00c1..68422a7 100644
--- a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
@@ -17,7 +18,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_creator_impl.h"
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h
index daa2c089..413ba26e 100644
--- a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h
+++ b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h
@@ -9,6 +9,9 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/containers/flat_map.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -17,9 +20,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_bundle.h"
 #include "chromeos/services/device_sync/cryptauth_metadata_syncer.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefRegistrySimple;
diff --git a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl_unittest.cc
index e31e110..3d1df47e 100644
--- a/chromeos/services/device_sync/cryptauth_metadata_syncer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_metadata_syncer_impl_unittest.cc
@@ -8,6 +8,11 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
 #include "base/stl_util.h"
@@ -26,11 +31,6 @@
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/cryptauth_scheduler.h b/chromeos/services/device_sync/cryptauth_scheduler.h
index 3582f417..b9d610e 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler.h
+++ b/chromeos/services/device_sync/cryptauth_scheduler.h
@@ -7,11 +7,11 @@
 
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_result.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
index 108f1c0..fe9a385 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
@@ -7,13 +7,13 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/base64.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl.h b/chromeos/services/device_sync/cryptauth_scheduler_impl.h
index 3cf7137..8c359e3e 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl.h
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/default_clock.h"
@@ -17,8 +19,6 @@
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_result.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefRegistrySimple;
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
index 438dcd9..249dcc2 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/base64.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/simple_test_clock.h"
@@ -17,7 +18,6 @@
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/services/device_sync/fake_cryptauth_scheduler.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager.h b/chromeos/services/device_sync/cryptauth_v2_device_manager.h
index 2dc494f..0bb6a22 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager.h
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager.h
@@ -7,13 +7,13 @@
 
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_device.h"
 #include "chromeos/services/device_sync/cryptauth_device_registry.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
index f1765eeb..dc0f417 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
@@ -6,13 +6,13 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
 #include "chromeos/services/device_sync/cryptauth_device_syncer_impl.h"
 #include "chromeos/services/device_sync/cryptauth_key_registry.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 #include "chromeos/services/device_sync/synced_bluetooth_address_tracker_impl.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h
index 04a13ee..f577359 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h
@@ -9,6 +9,8 @@
 #include <ostream>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_device_registry.h"
@@ -16,8 +18,6 @@
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler.h"
 #include "chromeos/services/device_sync/cryptauth_v2_device_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl_unittest.cc
index 7a894d1..aabad28 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h"
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "chromeos/services/device_sync/cryptauth_device_registry_impl.h"
@@ -14,8 +16,6 @@
 #include "chromeos/services/device_sync/fake_cryptauth_scheduler.h"
 #include "chromeos/services/device_sync/fake_synced_bluetooth_address_tracker.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
index ad993f9..28eb1b6 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
@@ -4,14 +4,14 @@
 
 #include "chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.h"
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/check_op.h"
 #include "base/no_destructor.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
 #include "chromeos/services/device_sync/cryptauth_device.h"
 #include "chromeos/services/device_sync/fake_ecies_encryption.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
index a2b8629..d0bfb59 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
@@ -17,8 +19,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_proof_computer_impl.h"
 #include "chromeos/services/device_sync/cryptauth_key_registry.h"
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_constants.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
index fec0ee7..ecfce7c 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.h
@@ -10,6 +10,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "base/containers/flat_map.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -19,8 +21,6 @@
 #include "chromeos/services/device_sync/cryptauth_key_creator.h"
 #include "chromeos/services/device_sync/cryptauth_v2_enroller.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cryptauthv2 {
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_v2_enroller_impl_unittest.cc
index aeed24f2..8685c46 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl_unittest.cc
@@ -9,6 +9,12 @@
 #include <utility>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
@@ -23,12 +29,6 @@
 #include "chromeos/services/device_sync/fake_cryptauth_key_proof_computer.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_constants.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
index f789ba3..be37b45 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h"
 
+#include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "base/bind.h"
 #include "base/hash/hash.h"
 #include "base/memory/ptr_util.h"
@@ -17,7 +18,6 @@
 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
 #include "chromeos/services/device_sync/cryptauth_v2_enroller_impl.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_logging.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
index 43a613c0..a6dfb00 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/default_clock.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
@@ -15,8 +17,6 @@
 #include "chromeos/services/device_sync/cryptauth_feature_type.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl_unittest.cc
index 25b2a0c..0706971 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl_unittest.cc
@@ -8,6 +8,12 @@
 #include <unordered_map>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/no_destructor.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
@@ -25,12 +31,6 @@
 #include "chromeos/services/device_sync/fake_cryptauth_v2_enroller.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_constants.h"
 #include "chromeos/services/device_sync/value_string_encoding.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index 9c186c4..f809471 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -5,6 +5,8 @@
 #include "chromeos/services/device_sync/device_sync_impl.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/device_classifier_util.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
@@ -33,8 +35,6 @@
 #include "chromeos/services/device_sync/cryptauth_v2_device_manager_impl.h"
 #include "chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.h"
 #include "chromeos/services/device_sync/device_sync_type_converters.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/device_classifier_util.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h"
 #include "chromeos/services/device_sync/remote_device_provider_impl.h"
 #include "chromeos/services/device_sync/software_feature_manager_impl.h"
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index 48e1bbb..a6c5976 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
@@ -18,8 +20,6 @@
 #include "chromeos/services/device_sync/device_sync_base.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "chromeos/services/device_sync/remote_device_provider.h"
 #include "components/signin/public/identity_manager/account_info.h"
diff --git a/chromeos/services/device_sync/device_sync_service_unittest.cc b/chromeos/services/device_sync/device_sync_service_unittest.cc
index 9c1e551..7288fe2 100644
--- a/chromeos/services/device_sync/device_sync_service_unittest.cc
+++ b/chromeos/services/device_sync/device_sync_service_unittest.cc
@@ -8,6 +8,9 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
@@ -45,9 +48,6 @@
 #include "chromeos/services/device_sync/fake_device_sync_observer.h"
 #include "chromeos/services/device_sync/fake_remote_device_provider.h"
 #include "chromeos/services/device_sync/fake_software_feature_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_prefs.h"
 #include "chromeos/services/device_sync/public/cpp/fake_client_app_metadata_provider.h"
 #include "chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h"
diff --git a/chromeos/services/device_sync/fake_cryptauth_device_manager.h b/chromeos/services/device_sync/fake_cryptauth_device_manager.h
index e4e0d9a..1f857be 100644
--- a/chromeos/services/device_sync/fake_cryptauth_device_manager.h
+++ b/chromeos/services/device_sync/fake_cryptauth_device_manager.h
@@ -7,9 +7,9 @@
 
 #include <memory>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_device_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/fake_cryptauth_device_notifier.h b/chromeos/services/device_sync/fake_cryptauth_device_notifier.h
index 77b2317..38faa1b 100644
--- a/chromeos/services/device_sync/fake_cryptauth_device_notifier.h
+++ b/chromeos/services/device_sync/fake_cryptauth_device_notifier.h
@@ -9,13 +9,13 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_notifier.h"
 #include "chromeos/services/device_sync/cryptauth_device_notifier_impl.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/fake_cryptauth_device_syncer.h b/chromeos/services/device_sync/fake_cryptauth_device_syncer.h
index 5349663..9398e57d 100644
--- a/chromeos/services/device_sync/fake_cryptauth_device_syncer.h
+++ b/chromeos/services/device_sync/fake_cryptauth_device_syncer.h
@@ -7,10 +7,10 @@
 
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/cryptauth_device_syncer.h"
 #include "chromeos/services/device_sync/cryptauth_device_syncer_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h b/chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h
index f9fec7b..158210b 100644
--- a/chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h
+++ b/chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h
@@ -8,9 +8,9 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
index 16cd31a..402e3ee 100644
--- a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
+++ b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
@@ -9,12 +9,12 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/containers/flat_set.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_feature_status_getter.h"
 #include "chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/fake_cryptauth_group_private_key_sharer.h b/chromeos/services/device_sync/fake_cryptauth_group_private_key_sharer.h
index de95c42..eb24d1d 100644
--- a/chromeos/services/device_sync/fake_cryptauth_group_private_key_sharer.h
+++ b/chromeos/services/device_sync/fake_cryptauth_group_private_key_sharer.h
@@ -8,11 +8,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_group_private_key_sharer.h"
 #include "chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/fake_cryptauth_metadata_syncer.h b/chromeos/services/device_sync/fake_cryptauth_metadata_syncer.h
index b313e92..88316cc 100644
--- a/chromeos/services/device_sync/fake_cryptauth_metadata_syncer.h
+++ b/chromeos/services/device_sync/fake_cryptauth_metadata_syncer.h
@@ -8,13 +8,13 @@
 #include <memory>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "base/timer/timer.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_metadata_syncer.h"
 #include "chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefService;
diff --git a/chromeos/services/device_sync/fake_cryptauth_scheduler.h b/chromeos/services/device_sync/fake_cryptauth_scheduler.h
index 80dc28b..66ebd75 100644
--- a/chromeos/services/device_sync/fake_cryptauth_scheduler.h
+++ b/chromeos/services/device_sync/fake_cryptauth_scheduler.h
@@ -8,11 +8,11 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_result.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h
index 6e2b00f..68fcffc 100644
--- a/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h
+++ b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h
@@ -7,13 +7,13 @@
 
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/containers/queue.h"
 #include "base/time/time.h"
 #include "chromeos/services/device_sync/cryptauth_device.h"
 #include "chromeos/services/device_sync/cryptauth_device_registry.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
 #include "chromeos/services/device_sync/cryptauth_v2_device_manager.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/fake_cryptauth_v2_enroller.h b/chromeos/services/device_sync/fake_cryptauth_v2_enroller.h
index 5f8ca6a..74f480d2 100644
--- a/chromeos/services/device_sync/fake_cryptauth_v2_enroller.h
+++ b/chromeos/services/device_sync/fake_cryptauth_v2_enroller.h
@@ -5,11 +5,11 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_V2_ENROLLER_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_V2_ENROLLER_H_
 
+#include "ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_result.h"
 #include "chromeos/services/device_sync/cryptauth_v2_enroller.h"
-#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/mock_cryptauth_client.h b/chromeos/services/device_sync/mock_cryptauth_client.h
index ac02947..945edd1d 100644
--- a/chromeos/services/device_sync/mock_cryptauth_client.h
+++ b/chromeos/services/device_sync/mock_cryptauth_client.h
@@ -5,12 +5,12 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_MOCK_CRYPTAUTH_CLIENT_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_MOCK_CRYPTAUTH_CLIENT_H_
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/pref_names.cc b/chromeos/services/device_sync/pref_names.cc
index 27a3cc0a..1d5ac7e 100644
--- a/chromeos/services/device_sync/pref_names.cc
+++ b/chromeos/services/device_sync/pref_names.cc
@@ -22,7 +22,7 @@
 
 // (CryptAuth v1) The reason that the next device_sync is performed. This should
 // be one of the enum values of cryptauth::InvocationReason in
-// chromeos/services/device_sync/proto/cryptauth_api.proto.
+// ash/services/device_sync/proto/cryptauth_api.proto.
 const char kCryptAuthDeviceSyncReason[] = "cryptauth.device_sync.reason";
 
 // (CryptAuth v1) A list of unlock keys (stored as dictionaries) synced from
@@ -43,7 +43,7 @@
 
 // (CryptAuth v1) The reason that the next enrollment is performed. This should
 // be one of the enum values of cryptauth::InvocationReason in
-// chromeos/services/device_sync/proto/cryptauth_api.proto.
+// ash/services/device_sync/proto/cryptauth_api.proto.
 const char kCryptAuthEnrollmentReason[] = "cryptauth.enrollment.reason";
 
 // (CryptAuth v1 and during migration to v2) The public key of the user and
diff --git a/chromeos/services/device_sync/proto/device_classifier_util.h b/chromeos/services/device_sync/proto/device_classifier_util.h
deleted file mode 100644
index 06ec1307..0000000
--- a/chromeos/services/device_sync/proto/device_classifier_util.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 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 CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
-
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-
-namespace chromeos {
-
-namespace device_sync {
-
-namespace device_classifier_util {
-
-const cryptauth::DeviceClassifier& GetDeviceClassifier();
-
-}  // namespace device_classifier_util
-
-}  // namespace device_sync
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PROTO_DEVICE_CLASSIFIER_UTIL_H_
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.h b/chromeos/services/device_sync/public/cpp/device_sync_client.h
index feda1f9..d95bdc2 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.h
@@ -9,13 +9,13 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/observer_list.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
index 7174adc..0305f7f5 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -9,13 +9,13 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
index a3145c77..e141a4d 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
@@ -23,7 +24,6 @@
 #include "chromeos/services/device_sync/device_sync_impl.h"
 #include "chromeos/services/device_sync/fake_device_sync.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_prefs.h"
 #include "chromeos/services/device_sync/public/cpp/fake_client_app_metadata_provider.h"
 #include "chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h"
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
index d113b7d2..087e7fa 100644
--- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -9,12 +9,12 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/callback.h"
 #include "base/containers/circular_deque.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h b/chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h
index d5c8afb4..12f083e 100644
--- a/chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h
+++ b/chromeos/services/device_sync/public/cpp/fake_gcm_device_info_provider.h
@@ -5,7 +5,7 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_FAKE_GCM_DEVICE_INFO_PROVIDER_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_FAKE_GCM_DEVICE_INFO_PROVIDER_H_
 
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h b/chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h
index bc8d62f..72ebb645 100644
--- a/chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h
+++ b/chromeos/services/device_sync/public/cpp/gcm_device_info_provider.h
@@ -5,7 +5,7 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_GCM_DEVICE_INFO_PROVIDER_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_GCM_DEVICE_INFO_PROVIDER_H_
 
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/public/mojom/BUILD.gn b/chromeos/services/device_sync/public/mojom/BUILD.gn
index 466f77f..bd2b4f4 100644
--- a/chromeos/services/device_sync/public/mojom/BUILD.gn
+++ b/chromeos/services/device_sync/public/mojom/BUILD.gn
@@ -31,8 +31,8 @@
       traits_headers = [ "device_sync_mojom_traits.h" ]
       traits_sources = [ "device_sync_mojom_traits.cc" ]
       traits_public_deps = [
+        "//ash/services/device_sync/proto",
         "//chromeos/services/device_sync:feature_status_change",
-        "//chromeos/services/device_sync/proto",
       ]
     },
   ]
@@ -45,10 +45,10 @@
 
   deps = [
     ":mojom",
+    "//ash/services/device_sync/proto",
     "//base",
     "//base/test:test_support",
     "//chromeos/services/device_sync:feature_status_change",
-    "//chromeos/services/device_sync/proto",
     "//mojo/public/cpp/test_support:test_utils",
     "//testing/gtest",
   ]
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
index 5e9f3f1a..53268bc 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
@@ -5,9 +5,9 @@
 #ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_MOJOM_DEVICE_SYNC_MOJOM_TRAITS_H_
 #define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_MOJOM_DEVICE_SYNC_MOJOM_TRAITS_H_
 
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom-shared.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
index 35f202f..996fe8d4 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h"
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/remote_device_loader.cc b/chromeos/services/device_sync/remote_device_loader.cc
index fb4c5ad4..2878034 100644
--- a/chromeos/services/device_sync/remote_device_loader.cc
+++ b/chromeos/services/device_sync/remote_device_loader.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
@@ -14,7 +15,6 @@
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/remote_device_loader.h b/chromeos/services/device_sync/remote_device_loader.h
index 2a974690..a3690502 100644
--- a/chromeos/services/device_sync/remote_device_loader.h
+++ b/chromeos/services/device_sync/remote_device_loader.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/remote_device_loader_unittest.cc b/chromeos/services/device_sync/remote_device_loader_unittest.cc
index f711b4e..71eb5ff 100644
--- a/chromeos/services/device_sync/remote_device_loader_unittest.cc
+++ b/chromeos/services/device_sync/remote_device_loader_unittest.cc
@@ -9,9 +9,9 @@
 #include <memory>
 #include <utility>
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc b/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
index 34e4c561..d01f4d76 100644
--- a/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
+++ b/chromeos/services/device_sync/remote_device_provider_impl_unittest.cc
@@ -8,6 +8,8 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
@@ -22,8 +24,6 @@
 #include "chromeos/services/device_sync/fake_cryptauth_device_manager.h"
 #include "chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h"
 #include "chromeos/services/device_sync/fake_remote_device_v2_loader.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "chromeos/services/device_sync/remote_device_loader.h"
 #include "chromeos/services/device_sync/remote_device_v2_loader_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/services/device_sync/remote_device_v2_loader_impl_unittest.cc b/chromeos/services/device_sync/remote_device_v2_loader_impl_unittest.cc
index b878e71e..fbc9ab9 100644
--- a/chromeos/services/device_sync/remote_device_v2_loader_impl_unittest.cc
+++ b/chromeos/services/device_sync/remote_device_v2_loader_impl_unittest.cc
@@ -7,12 +7,12 @@
 #include <string>
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/bind.h"
 #include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "chromeos/components/multidevice/remote_device.h"
 #include "chromeos/services/device_sync/cryptauth_device.h"
-#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
-#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "chromeos/services/device_sync/remote_device_v2_loader_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chromeos/services/device_sync/software_feature_manager_impl.cc b/chromeos/services/device_sync/software_feature_manager_impl.cc
index 7486a83..165d7337 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl.cc
+++ b/chromeos/services/device_sync/software_feature_manager_impl.cc
@@ -6,12 +6,12 @@
 
 #include <utility>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/check.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
 #include "chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 
 namespace chromeos {
 
diff --git a/chromeos/services/device_sync/software_feature_manager_impl.h b/chromeos/services/device_sync/software_feature_manager_impl.h
index c290f7c..cdf37979 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl.h
+++ b/chromeos/services/device_sync/software_feature_manager_impl.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/containers/queue.h"
@@ -16,7 +17,6 @@
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/network_request_error.h"
-#include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
 #include "chromeos/services/device_sync/software_feature_manager.h"
 
 namespace chromeos {
diff --git a/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc b/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
index 2a378c5..74304bb0 100644
--- a/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/software_feature_manager_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/services/device_sync/software_feature_manager_impl.h"
 
+#include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
@@ -11,7 +12,6 @@
 #include "chromeos/services/device_sync/fake_cryptauth_feature_status_setter.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/device_sync/mock_cryptauth_client.h"
-#include "chromeos/services/device_sync/proto/enum_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chromeos/services/device_sync/synced_bluetooth_address_tracker_impl.cc b/chromeos/services/device_sync/synced_bluetooth_address_tracker_impl.cc
index 5184b80..98bbfd5 100644
--- a/chromeos/services/device_sync/synced_bluetooth_address_tracker_impl.cc
+++ b/chromeos/services/device_sync/synced_bluetooth_address_tracker_impl.cc
@@ -7,12 +7,12 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
+#include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/device_sync/cryptauth_scheduler.h"
 #include "chromeos/services/device_sync/pref_names.h"
-#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
index 7d0e581..b59d2094 100644
--- a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
+++ b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
@@ -93,8 +93,10 @@
   // the handwriting dlc if it is already on device.
   for (const auto& dlc_info : dlcs_with_content.dlc_infos()) {
     if (dlc_info.id() == kLibHandwritingDlcId) {
+      dlcservice::InstallRequest install_request;
+      install_request.set_id(kLibHandwritingDlcId);
       dlc_client->Install(
-          kLibHandwritingDlcId,
+          install_request,
           base::BindOnce(&OnInstallDlcComplete, std::move(spec),
                          std::move(receiver), std::move(callback)),
           base::DoNothing());
diff --git a/components/component_updater/README.md b/components/component_updater/README.md
index 962af7f..e3947c05 100644
--- a/components/component_updater/README.md
+++ b/components/component_updater/README.md
@@ -67,7 +67,7 @@
 Components need to be registered with the component updater. This is done in
 [RegisterComponentsForUpdate](https://cs.chromium.org/chromium/src/chrome/browser/chrome_browser_main.cc).
 
-### Bundle with the Chrome Installer (Optional)
+### Bundle with the Chrome Installer (Optional, not recommended)
 If you need the guarantee that some implementation of your component is always
 available, you must bundle a component implementation with the browser itself.
 If you are using `ComponentInstaller`, you simply need to make sure that
@@ -78,5 +78,14 @@
 platform, as the system will attempt to apply differential updates over these
 files.
 
+This option is not recommended, because:
+* the browser should generally not depend on any particular component's
+existence
+* bundling increases the installer's size
+* the actual gap between install and receipt of updates should be < 5 minutes
+* bundling increases the complexity of the solution
+
+Note that you can always start simple and bundle later, if it becomes required.
+
 ### Implement On-Demand or Just-In-Time Updates (Optional)
 Contact the component\_updater OWNERS.
diff --git a/components/custom_handlers/protocol_handler.cc b/components/custom_handlers/protocol_handler.cc
index 5ccf8a5..625f4529 100644
--- a/components/custom_handlers/protocol_handler.cc
+++ b/components/custom_handlers/protocol_handler.cc
@@ -66,10 +66,10 @@
 
 ProtocolHandler::ProtocolHandler() = default;
 
-bool ProtocolHandler::IsValidDict(const base::DictionaryValue* value) {
+bool ProtocolHandler::IsValidDict(const base::Value::Dict& value) {
   // Note that "title" parameter is ignored.
   // The |last_modified| field is optional as it was introduced in M68.
-  return value->FindKey("protocol") && value->FindKey("url");
+  return value.FindString("protocol") && value.FindString("url");
 }
 
 bool ProtocolHandler::IsValid() const {
@@ -106,7 +106,7 @@
 }
 
 ProtocolHandler ProtocolHandler::CreateProtocolHandler(
-    const base::DictionaryValue* value) {
+    const base::Value::Dict& value) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!IsValidDict(value)) {
     return EmptyProtocolHandler();
@@ -116,23 +116,22 @@
   base::Time time;
   blink::ProtocolHandlerSecurityLevel security_level =
       blink::ProtocolHandlerSecurityLevel::kStrict;
-  if (const std::string* protocol_in = value->FindStringKey("protocol"))
+  if (const std::string* protocol_in = value.FindString("protocol"))
     protocol = *protocol_in;
-  if (const std::string* url_in = value->FindStringKey("url"))
+  if (const std::string* url_in = value.FindString("url"))
     url = *url_in;
   absl::optional<base::Time> time_value =
-      base::ValueToTime(value->FindKey("last_modified"));
+      base::ValueToTime(value.Find("last_modified"));
   // Treat invalid times as the default value.
   if (time_value)
     time = *time_value;
-  absl::optional<int> security_level_value =
-      value->FindIntKey("security_level");
+  absl::optional<int> security_level_value = value.FindInt("security_level");
   if (security_level_value) {
     security_level =
         blink::ProtocolHandlerSecurityLevelFrom(*security_level_value);
   }
 
-  if (const base::Value* app_id_val = value->FindKey("app_id")) {
+  if (const base::Value* app_id_val = value.Find("app_id")) {
     std::string app_id;
     if (app_id_val->is_string())
       app_id = app_id_val->GetString();
@@ -151,16 +150,16 @@
   return GURL(translatedUrlSpec);
 }
 
-std::unique_ptr<base::DictionaryValue> ProtocolHandler::Encode() const {
+base::Value::Dict ProtocolHandler::Encode() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  auto d = std::make_unique<base::DictionaryValue>();
-  d->SetString("protocol", protocol_);
-  d->SetString("url", url_.spec());
-  d->SetKey("last_modified", base::TimeToValue(last_modified_));
-  d->SetIntPath("security_level", static_cast<int>(security_level_));
+  base::Value::Dict d;
+  d.Set("protocol", protocol_);
+  d.Set("url", url_.spec());
+  d.Set("last_modified", base::TimeToValue(last_modified_));
+  d.Set("security_level", static_cast<int>(security_level_));
 
   if (web_app_id_.has_value())
-    d->SetString("app_id", web_app_id_.value());
+    d.Set("app_id", web_app_id_.value());
 
   return d;
 }
diff --git a/components/custom_handlers/protocol_handler.h b/components/custom_handlers/protocol_handler.h
index c31ba3629..3df097d 100644
--- a/components/custom_handlers/protocol_handler.h
+++ b/components/custom_handlers/protocol_handler.h
@@ -49,12 +49,11 @@
 
   // Creates a ProtocolHandler with fields from the dictionary. Returns an
   // empty ProtocolHandler if the input is invalid.
-  static ProtocolHandler CreateProtocolHandler(
-      const base::DictionaryValue* value);
+  static ProtocolHandler CreateProtocolHandler(const base::Value::Dict& value);
 
   // Returns true if the dictionary value has all the necessary fields to
   // define a ProtocolHandler.
-  static bool IsValidDict(const base::DictionaryValue* value);
+  static bool IsValidDict(const base::Value::Dict& value);
 
   // Return true if the protocol handler meets security constraints.
   bool IsValid() const;
@@ -74,7 +73,7 @@
   bool IsEquivalent(const ProtocolHandler& other) const;
 
   // Encodes this protocol handler as a DictionaryValue.
-  std::unique_ptr<base::DictionaryValue> Encode() const;
+  base::Value::Dict Encode() const;
 
   // Returns a friendly name for |protocol| if one is available, otherwise
   // this function returns |protocol|.
diff --git a/components/custom_handlers/protocol_handler_registry.cc b/components/custom_handlers/protocol_handler_registry.cc
index 5dd85e1e..f27e90f 100644
--- a/components/custom_handlers/protocol_handler_registry.cc
+++ b/components/custom_handlers/protocol_handler_registry.cc
@@ -118,8 +118,8 @@
   ProtocolHandlerList to_replace(GetReplacedHandlers(handler));
   if (to_replace.empty())
     return false;
-  for (auto p = to_replace.begin(); p != to_replace.end(); ++p) {
-    RemoveHandler(*p);
+  for (const auto& replaced_handler : to_replace) {
+    RemoveHandler(replaced_handler);
   }
   if (make_new_handler_default) {
     OnAcceptRegisterProtocolHandler(handler);
@@ -137,9 +137,9 @@
   const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
   if (!handlers)
     return replaced_handlers;
-  for (auto p = handlers->begin(); p != handlers->end(); ++p) {
-    if (handler.IsSameOrigin(*p)) {
-      replaced_handlers.push_back(*p);
+  for (const auto& old_handler : *handlers) {
+    if (handler.IsSameOrigin(old_handler)) {
+      replaced_handlers.push_back(old_handler);
     }
   }
   return replaced_handlers;
@@ -201,9 +201,7 @@
   // For each default protocol handler, check that we are still registered
   // with the OS as the default application.
   if (delegate_->ShouldRemoveHandlersNotInOS()) {
-    for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
-         p != default_handlers_.end(); ++p) {
-      const std::string& protocol = p->second.protocol();
+    for (const auto& [protocol, handler] : default_handlers_) {
       delegate_->CheckDefaultClientWithOS(
           protocol, GetDefaultWebClientCallback(protocol));
     }
@@ -212,18 +210,18 @@
 
 int ProtocolHandlerRegistry::GetHandlerIndex(base::StringPiece scheme) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const ProtocolHandler& handler = GetHandlerFor(scheme);
-  if (handler.IsEmpty())
+  const ProtocolHandler& candidate = GetHandlerFor(scheme);
+  if (candidate.IsEmpty())
     return -1;
   const ProtocolHandlerList* handlers = GetHandlerList(scheme);
   if (!handlers)
     return -1;
 
-  ProtocolHandlerList::const_iterator p;
-  int i;
-  for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
-    if (*p == handler)
+  int i = 0;
+  for (const auto& handler : *handlers) {
+    if (handler == candidate)
       return i;
+    i++;
   }
   return -1;
 }
@@ -242,8 +240,8 @@
 ProtocolHandlerRegistry::GetUserDefinedHandlers(base::Time begin,
                                                 base::Time end) const {
   ProtocolHandlerRegistry::ProtocolHandlerList result;
-  for (const auto& entry : user_protocol_handlers_) {
-    for (const ProtocolHandler& handler : entry.second) {
+  for (const auto& [protocol, handlers_list] : user_protocol_handlers_) {
+    for (const ProtocolHandler& handler : handlers_list) {
       if (base::Contains(predefined_protocol_handlers_, handler))
         continue;
       if (begin <= handler.last_modified() && handler.last_modified() < end)
@@ -281,10 +279,9 @@
 void ProtocolHandlerRegistry::GetRegisteredProtocols(
     std::vector<std::string>* output) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ProtocolHandlerMultiMap::const_iterator p;
-  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
-    if (!p->second.empty())
-      output->push_back(p->first);
+  for (const auto& [protocol, handlers_list] : protocol_handlers_) {
+    if (!handlers_list.empty())
+      output->push_back(protocol);
   }
 }
 
@@ -323,10 +320,8 @@
 
 bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ProtocolHandlerList::const_iterator i;
-  for (i = ignored_protocol_handlers_.begin();
-       i != ignored_protocol_handlers_.end(); ++i) {
-    if (*i == handler) {
+  for (const auto& ignored_handler : ignored_protocol_handlers_) {
+    if (ignored_handler == handler) {
       return true;
     }
   }
@@ -340,9 +335,8 @@
   if (!handlers) {
     return false;
   }
-  ProtocolHandlerList::const_iterator i;
-  for (i = handlers->begin(); i != handlers->end(); ++i) {
-    if (handler.IsEquivalent(*i)) {
+  for (const auto& registered_handler : *handlers) {
+    if (handler.IsEquivalent(registered_handler)) {
       return true;
     }
   }
@@ -352,10 +346,8 @@
 bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
     const ProtocolHandler& handler) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ProtocolHandlerList::const_iterator i;
-  for (i = ignored_protocol_handlers_.begin();
-       i != ignored_protocol_handlers_.end(); ++i) {
-    if (handler.IsEquivalent(*i)) {
+  for (const auto& ignored_handler : ignored_protocol_handlers_) {
+    if (handler.IsEquivalent(ignored_handler)) {
       return true;
     }
   }
@@ -444,9 +436,8 @@
     return;
   }
   enabled_ = true;
-  ProtocolHandlerMap::const_iterator p;
-  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
-    delegate_->RegisterExternalHandler(p->first);
+  for (const auto& [protocol, handler] : default_handlers_) {
+    delegate_->RegisterExternalHandler(protocol);
   }
   Save();
   NotifyChanged();
@@ -459,9 +450,8 @@
   }
   enabled_ = false;
 
-  ProtocolHandlerMap::const_iterator p;
-  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
-    delegate_->DeregisterExternalHandler(p->first);
+  for (const auto& [protocol, handler] : default_handlers_) {
+    delegate_->DeregisterExternalHandler(protocol);
   }
   Save();
   NotifyChanged();
@@ -510,14 +500,12 @@
   if (is_loading_) {
     return;
   }
-  std::unique_ptr<base::Value> registered_protocol_handlers(
-      EncodeRegisteredHandlers());
-  std::unique_ptr<base::Value> ignored_protocol_handlers(
-      EncodeIgnoredHandlers());
+  base::Value registered_protocol_handlers(EncodeRegisteredHandlers());
+  base::Value ignored_protocol_handlers(EncodeIgnoredHandlers());
   PrefService* prefs = user_prefs::UserPrefs::Get(context_);
 
-  prefs->Set(prefs::kRegisteredProtocolHandlers, *registered_protocol_handlers);
-  prefs->Set(prefs::kIgnoredProtocolHandlers, *ignored_protocol_handlers);
+  prefs->Set(prefs::kRegisteredProtocolHandlers, registered_protocol_handlers);
+  prefs->Set(prefs::kIgnoredProtocolHandlers, ignored_protocol_handlers);
   prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_);
 }
 
@@ -561,30 +549,28 @@
   protocol_handlers_[handler.protocol()] = new_list;
 }
 
-base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
+base::Value::List ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::ListValue* protocol_handlers = new base::ListValue();
-  for (auto i = user_protocol_handlers_.begin();
-       i != user_protocol_handlers_.end(); ++i) {
-    for (auto j = i->second.begin(); j != i->second.end(); ++j) {
-      std::unique_ptr<base::DictionaryValue> encoded = j->Encode();
-      if (IsDefault(*j)) {
-        encoded->Set("default", std::make_unique<base::Value>(true));
+  auto encoded_handlers = base::Value::List();
+  for (const auto& [protocol, handlers_list] : user_protocol_handlers_) {
+    for (const auto& handler : handlers_list) {
+      base::Value::Dict encoded = handler.Encode();
+      if (IsDefault(handler)) {
+        encoded.Set("default", true);
       }
-      protocol_handlers->Append(std::move(encoded));
+      encoded_handlers.Append(std::move(encoded));
     }
   }
-  return protocol_handlers;
+  return encoded_handlers;
 }
 
-base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
+base::Value::List ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::ListValue* handlers = new base::ListValue();
-  for (auto i = user_ignored_protocol_handlers_.begin();
-       i != user_ignored_protocol_handlers_.end(); ++i) {
-    handlers->Append(i->Encode());
+  base::Value::List encoded_handlers = base::Value::List();
+  for (const auto& handler : user_ignored_protocol_handlers_) {
+    encoded_handlers.Append(handler.Encode());
   }
-  return handlers;
+  return encoded_handlers;
 }
 
 void ProtocolHandlerRegistry::NotifyChanged() {
@@ -617,10 +603,10 @@
   return true;
 }
 
-std::vector<const base::DictionaryValue*>
+std::vector<const base::Value::Dict*>
 ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::vector<const base::DictionaryValue*> result;
+  std::vector<const base::Value::Dict*> result;
   PrefService* prefs = user_prefs::UserPrefs::Get(context_);
   if (!prefs->HasPrefPath(pref_name)) {
     return result;
@@ -628,13 +614,11 @@
 
   const base::Value* handlers = prefs->GetList(pref_name);
   if (handlers) {
-    for (const auto& dict : handlers->GetListDeprecated()) {
-      if (!dict.is_dict())
-        continue;
-      const base::DictionaryValue* dict_value =
-          static_cast<const base::DictionaryValue*>(&dict);
-      if (ProtocolHandler::IsValidDict(dict_value)) {
-        result.push_back(dict_value);
+    for (const auto& list_item : handlers->GetList()) {
+      if (const base::Value::Dict* encoded_handler = list_item.GetIfDict()) {
+        if (ProtocolHandler::IsValidDict(*encoded_handler)) {
+          result.push_back(encoded_handler);
+        }
       }
     }
   }
@@ -644,15 +628,14 @@
 void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref(
     const char* pref_name,
     const HandlerSource source) {
-  std::vector<const base::DictionaryValue*> registered_handlers =
+  std::vector<const base::Value::Dict*> registered_handlers =
       GetHandlersFromPref(pref_name);
-  for (std::vector<const base::DictionaryValue*>::const_iterator p =
-           registered_handlers.begin();
-       p != registered_handlers.end(); ++p) {
-    ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
+  for (const auto* encoded_handler : registered_handlers) {
+    ProtocolHandler handler =
+        ProtocolHandler::CreateProtocolHandler(*encoded_handler);
     if (!RegisterProtocolHandler(handler, source))
       continue;
-    if ((*p)->FindBoolKey("default").value_or(false)) {
+    if (encoded_handler->FindBool("default").value_or(false)) {
       SetDefault(handler);
     }
   }
@@ -675,13 +658,11 @@
 void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref(
     const char* pref_name,
     const HandlerSource source) {
-  std::vector<const base::DictionaryValue*> ignored_handlers =
+  std::vector<const base::Value::Dict*> ignored_handlers =
       GetHandlersFromPref(pref_name);
-  for (std::vector<const base::DictionaryValue*>::const_iterator p =
-           ignored_handlers.begin();
-       p != ignored_handlers.end(); ++p) {
-    IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source);
-  }
+  for (const auto* encoded_handler : ignored_handlers)
+    IgnoreProtocolHandler(
+        ProtocolHandler::CreateProtocolHandler(*encoded_handler), source);
 }
 
 bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
diff --git a/components/custom_handlers/protocol_handler_registry.h b/components/custom_handlers/protocol_handler_registry.h
index eba3719..1d99bdb 100644
--- a/components/custom_handlers/protocol_handler_registry.h
+++ b/components/custom_handlers/protocol_handler_registry.h
@@ -247,11 +247,11 @@
 
   // Returns a JSON list of protocol handlers. The caller is responsible for
   // deleting this Value.
-  base::Value* EncodeRegisteredHandlers();
+  base::Value::List EncodeRegisteredHandlers();
 
   // Returns a JSON list of ignored protocol handlers. The caller is
   // responsible for deleting this Value.
-  base::Value* EncodeIgnoredHandlers();
+  base::Value::List EncodeIgnoredHandlers();
 
   // Notifies observers of a change to the registry.
   void NotifyChanged();
@@ -274,9 +274,12 @@
   ProtocolHandlerList GetUserIgnoredHandlers(base::Time begin,
                                              base::Time end) const;
 
-  // Get the DictionaryValues stored under the given pref name that are valid
+  // Get the Dict values stored under the given pref name that are valid
   // ProtocolHandler values.
-  std::vector<const base::DictionaryValue*> GetHandlersFromPref(
+  // These pointers may be invalidated by other changes in the preferences
+  // storage, hence they must not be stored in a way that outlives the current
+  // stack frame.
+  std::vector<const base::Value::Dict*> GetHandlersFromPref(
       const char* pref_name) const;
 
   // Ignores future requests to register the given protocol handler.
diff --git a/components/custom_handlers/protocol_handler_registry_unittest.cc b/components/custom_handlers/protocol_handler_registry_unittest.cc
index c2aa177..8fe4584 100644
--- a/components/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/components/custom_handlers/protocol_handler_registry_unittest.cc
@@ -300,8 +300,7 @@
   ProtocolHandler handler("news", GURL("https://example.com"), "app_id", now,
                           blink::ProtocolHandlerSecurityLevel::kStrict);
   auto value = handler.Encode();
-  ProtocolHandler recreated =
-      ProtocolHandler::CreateProtocolHandler(value.get());
+  ProtocolHandler recreated = ProtocolHandler::CreateProtocolHandler(value);
   EXPECT_EQ("news", recreated.protocol());
   EXPECT_EQ(GURL("https://example.com"), recreated.url());
   EXPECT_EQ(now, recreated.last_modified());
diff --git a/components/metrics/clean_exit_beacon.cc b/components/metrics/clean_exit_beacon.cc
index a46138ba..32581d0 100644
--- a/components/metrics/clean_exit_beacon.cc
+++ b/components/metrics/clean_exit_beacon.cc
@@ -167,14 +167,17 @@
 // 3. The file is successfully read.
 // 4. The file contents are in the expected format with the expected info.
 //
-// The file is not expected to exist for clients that do not belong to the
-// kSignalAndWriteViaFileUtilGroup, but even among clients in that group, there
-// are some edge cases. MaybeGetFileContents() is called before clients are
-// assigned to an Extended Variations Safe Mode experiment group, so a client
-// that is later assigned to the kSignalAndWriteViaFileUtilGroup will not have
-// the file in the first session after updating. It is also possible for a user
-// to delete the file or to reset their variations state with
-// kResetVariationState.
+// The file is not expected to exist for clients that have never been in the
+// Extended Variations Safe Mode experiment group,
+// kSignalAndWriteViaFileUtilGroup. The file may not exist for all experiment
+// group clients because there are some are some edge cases. First,
+// MaybeGetFileContents() is called before clients are assigned to an Extended
+// Variations Safe Mode group, so a client that is later assigned to the
+// experiment group will not have the file in the first session after updating
+// to or installing a Chrome version with the experiment. Second, Android Chrome
+// experiment group clients with repeated background sessions may never write a
+// beacon file. Finally, it is possible for a user to delete the file or to
+// reset their variations state with kResetVariationState.
 //
 // Note that not all beacon files are expected to have a monitoring stage as
 // this info was added in M100.
@@ -295,14 +298,6 @@
   did_previous_session_exit_cleanly_ =
       DidPreviousSessionExitCleanly(beacon_file_contents.get());
 
-#if BUILDFLAG(IS_ANDROID)
-  // TODO(crbug/1248239): Use the beacon file, if any, to maybe increment the
-  // crash streak when the Extended Variations Safe Mode experiment is fully
-  // enabled on Android Chrome stable.
-  if (channel_ == version_info::Channel::STABLE)
-    beacon_file_contents.reset();
-#endif  // BUILDFLAG(IS_ANDROID)
-
   MaybeIncrementCrashStreak(did_previous_session_exit_cleanly_,
                             beacon_file_contents.get(), local_state_);
   initialized_ = true;
@@ -336,21 +331,12 @@
                             beacon_file_beacon_value, local_state_beacon_value);
   }
 
-  // Emit this metric here so that it is meaningful for Android Chrome stable,
-  // on which the experiment is only partially enabled.
   bool did_previous_session_exit_cleanly =
       use_beacon_file ? beacon_file_beacon_value.value_or(true)
                       : local_state_beacon_value.value_or(true);
   if (!did_previous_session_exit_cleanly)
     RecordMonitoringStage(use_beacon_file ? beacon_file_contents : nullptr);
 
-#if BUILDFLAG(IS_ANDROID)
-  // TODO(crbug/1248239): Fully enable the Extended Variations Safe Mode
-  // experiment on Android Chrome by using the beacon file's beacon value for
-  // clients in the SignalAndWriteViaFileUtil group on stable.
-  if (channel_ == version_info::Channel::STABLE)
-    return local_state_beacon_value.value_or(true);
-#endif  // BUILDFLAG(IS_ANDROID)
 #if BUILDFLAG(IS_IOS)
   // For the time being, this is a no-op to avoid interference with the Extended
   // Variations Safe Mode experiment; i.e., ShouldUseUserDefaultsBeacon() always
diff --git a/components/metrics/clean_exit_beacon_unittest.cc b/components/metrics/clean_exit_beacon_unittest.cc
index 486292d..ceba28d7f6 100644
--- a/components/metrics/clean_exit_beacon_unittest.cc
+++ b/components/metrics/clean_exit_beacon_unittest.cc
@@ -617,46 +617,6 @@
   }
 }
 
-#if BUILDFLAG(IS_ANDROID)
-// TODO(crbug/1248239): Remove this test once the Extended Variations Safe Mode
-// experiment is enabled on Android Chrome stable.
-//
-// Verify that the beacon file, if any, is ignored on Android.
-TEST_F(CleanExitBeaconTest, FileIgnoredOnAndroid) {
-  SetUpExtendedSafeModeExperiment(variations::kSignalAndWriteViaFileUtilGroup);
-
-  // Set up the beacon file such that the previous session did not exit cleanly
-  // and the running crash streak is 2. The file (and thus these values) are
-  // expected to be ignored.
-  const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
-  const base::FilePath temp_beacon_file_path =
-      user_data_dir_path.Append(variations::kVariationsFilename);
-  const int last_session_num_crashes = 2;
-  ASSERT_LT(0, base::WriteFile(temp_beacon_file_path,
-                               CreateWellFormedBeaconFileContents(
-                                   /*exited_cleanly=*/false,
-                                   /*crash_streak=*/last_session_num_crashes)
-                                   .data()));
-
-  // Set up the PrefService such that the previous session exited cleanly and
-  // the running crash streak is 0. The PrefService (and thus these values) are
-  // expected to be used.
-  CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_,
-                                                       /*exited_cleanly=*/true);
-  const int expected_num_crashes = 0;
-  prefs_.SetInteger(variations::prefs::kVariationsCrashStreak,
-                    expected_num_crashes);
-
-  TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path,
-                                        version_info::Channel::STABLE);
-
-  // Verify that the Local State beacon was used (not the beacon file beacon).
-  EXPECT_TRUE(clean_exit_beacon.exited_cleanly());
-  histogram_tester_.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes",
-                                       expected_num_crashes, 1);
-}
-#endif  // BUILDFLAG(IS_ANDROID)
-
 // Verify that attempting to write synchronously DCHECKs for clients that do not
 // belong to the SignalAndWriteViaFileUtil experiment group.
 TEST_F(CleanExitBeaconTest,
diff --git a/components/no_state_prefetch/OWNERS b/components/no_state_prefetch/OWNERS
index 482e65d..641a4d8 100644
--- a/components/no_state_prefetch/OWNERS
+++ b/components/no_state_prefetch/OWNERS
@@ -1,3 +1,2 @@
 nhiroki@chromium.org
-robertogden@chromium.org
 ryansturm@chromium.org
diff --git a/components/no_state_prefetch/browser/no_state_prefetch_contents.cc b/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
index dca291e..bd8589f6 100644
--- a/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
+++ b/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
@@ -505,11 +505,11 @@
   if (!no_state_prefetch_contents_)
     return nullptr;
   auto dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString("url", prerender_url_.spec());
+  dict_value->SetStringKey("url", prerender_url_.spec());
   base::TimeTicks current_time = base::TimeTicks::Now();
   base::TimeDelta duration = current_time - load_start_time_;
-  dict_value->SetInteger("duration", duration.InSeconds());
-  dict_value->SetBoolean(
+  dict_value->SetIntKey("duration", duration.InSeconds());
+  dict_value->SetBoolKey(
       "is_loaded",
       no_state_prefetch_contents_ && !no_state_prefetch_contents_->IsLoading());
   return dict_value;
diff --git a/components/no_state_prefetch/browser/no_state_prefetch_manager.cc b/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
index cd4e60e..7233ea08 100644
--- a/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
+++ b/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
@@ -378,13 +378,13 @@
                                     prerender_history_->CopyEntriesAsValue()));
   dict_value->SetKey(
       "active", base::Value::FromUniquePtrValue(GetActivePrerendersAsValue()));
-  dict_value->SetBoolean("enabled",
+  dict_value->SetBoolKey("enabled",
                          delegate_->IsNetworkPredictionPreferenceEnabled());
-  dict_value->SetString("disabled_note",
-                        delegate_->GetReasonForDisablingPrediction());
+  dict_value->SetStringKey("disabled_note",
+                           delegate_->GetReasonForDisablingPrediction());
   // If prerender is disabled via a flag this method is not even called.
   std::string enabled_note;
-  dict_value->SetString("enabled_note", enabled_note);
+  dict_value->SetStringKey("enabled_note", enabled_note);
   return dict_value;
 }
 
diff --git a/components/performance_manager/graph/graph_impl_unittest.cc b/components/performance_manager/graph/graph_impl_unittest.cc
index 80caab7..7d2d137 100644
--- a/components/performance_manager/graph/graph_impl_unittest.cc
+++ b/components/performance_manager/graph/graph_impl_unittest.cc
@@ -121,7 +121,7 @@
 }  // namespace
 
 TEST_F(GraphImplTest, ObserverWorks) {
-  std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
+  std::unique_ptr<GraphImpl> graph = std::make_unique<GraphImpl>();
   Graph* raw_graph = graph.get();
 
   MockObserver obs;
@@ -164,13 +164,13 @@
 TEST_F(GraphImplTest, GraphOwned) {
   int destructor_count = 0;
 
-  std::unique_ptr<Foo> foo1 = base::WrapUnique(new Foo(&destructor_count));
-  std::unique_ptr<Foo> foo2 = base::WrapUnique(new Foo(&destructor_count));
+  std::unique_ptr<Foo> foo1 = std::make_unique<Foo>(&destructor_count);
+  std::unique_ptr<Foo> foo2 = std::make_unique<Foo>(&destructor_count);
   auto* raw1 = foo1.get();
   auto* raw2 = foo2.get();
 
   // Pass both objects to the graph.
-  std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
+  std::unique_ptr<GraphImpl> graph = std::make_unique<GraphImpl>();
   EXPECT_EQ(0u, graph->GraphOwnedCountForTesting());
   EXPECT_FALSE(raw1->passed_to_called());
   graph->PassToGraph(std::move(foo1));
diff --git a/components/performance_manager/metrics/metrics_collector_unittest.cc b/components/performance_manager/metrics/metrics_collector_unittest.cc
index f6d99ac0..6d55a702 100644
--- a/components/performance_manager/metrics/metrics_collector_unittest.cc
+++ b/components/performance_manager/metrics/metrics_collector_unittest.cc
@@ -39,8 +39,9 @@
 
   void SetUp() override {
     Super::SetUp();
-    metrics_collector_ = new MetricsCollector();
-    graph()->PassToGraph(base::WrapUnique(metrics_collector_.get()));
+    auto metrics_collector = std::make_unique<MetricsCollector>();
+    metrics_collector_ = metrics_collector.get();
+    graph()->PassToGraph(std::move(metrics_collector));
   }
 
   void TearDown() override {
diff --git a/components/performance_manager/owned_objects_unittest.cc b/components/performance_manager/owned_objects_unittest.cc
index 9939a93..743bb19 100644
--- a/components/performance_manager/owned_objects_unittest.cc
+++ b/components/performance_manager/owned_objects_unittest.cc
@@ -31,10 +31,10 @@
 TEST(OwnedObjectsTest, ContainerWorksAsAdvertised) {
   using Owner =
       OwnedObjects<Owned, void*, &Owned::OnPassedTo, &Owned::OnTakenFrom>;
-  std::unique_ptr<Owner> owner = base::WrapUnique(new Owner());
+  std::unique_ptr<Owner> owner = std::make_unique<Owner>();
 
-  std::unique_ptr<Owned> owned1 = base::WrapUnique(new Owned());
-  std::unique_ptr<Owned> owned2 = base::WrapUnique(new Owned());
+  std::unique_ptr<Owned> owned1 = std::make_unique<Owned>();
+  std::unique_ptr<Owned> owned2 = std::make_unique<Owned>();
   auto* raw1 = owned1.get();
   auto* raw2 = owned2.get();
 
diff --git a/components/performance_manager/performance_manager_registry_impl_unittest.cc b/components/performance_manager/performance_manager_registry_impl_unittest.cc
index 2e31e2a..8c2bec2 100644
--- a/components/performance_manager/performance_manager_registry_impl_unittest.cc
+++ b/components/performance_manager/performance_manager_registry_impl_unittest.cc
@@ -100,13 +100,13 @@
   std::unique_ptr<content::WebContents> contents =
       content::RenderViewHostTestHarness::CreateTestWebContents();
   std::unique_ptr<content::NavigationHandle> handle =
-      base::WrapUnique(new content::MockNavigationHandle(contents.get()));
+      std::make_unique<content::MockNavigationHandle>(contents.get());
   std::unique_ptr<content::NavigationThrottle> throttle1 =
-      base::WrapUnique(new content::TestNavigationThrottle(handle.get()));
+      std::make_unique<content::TestNavigationThrottle>(handle.get());
   std::unique_ptr<content::NavigationThrottle> throttle2 =
-      base::WrapUnique(new content::TestNavigationThrottle(handle.get()));
+      std::make_unique<content::TestNavigationThrottle>(handle.get());
   std::unique_ptr<content::NavigationThrottle> throttle3 =
-      base::WrapUnique(new content::TestNavigationThrottle(handle.get()));
+      std::make_unique<content::TestNavigationThrottle>(handle.get());
   auto* raw_throttle1 = throttle1.get();
   auto* raw_throttle2 = throttle2.get();
   auto* raw_throttle3 = throttle3.get();
@@ -158,8 +158,8 @@
   PerformanceManagerRegistryImpl* registry =
       PerformanceManagerRegistryImpl::GetInstance();
 
-  std::unique_ptr<Owned> owned1 = base::WrapUnique(new Owned());
-  std::unique_ptr<Owned> owned2 = base::WrapUnique(new Owned());
+  std::unique_ptr<Owned> owned1 = std::make_unique<Owned>();
+  std::unique_ptr<Owned> owned2 = std::make_unique<Owned>();
   auto* raw1 = owned1.get();
   auto* raw2 = owned2.get();
 
diff --git a/components/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc b/components/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
index 4cd8845..51421200 100644
--- a/components/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
+++ b/components/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
@@ -31,8 +31,8 @@
   ~NonRecordingSiteDataCacheTest() override = default;
 
   void SetUp() override {
-    recording_data_cache_ = base::WrapUnique(new SiteDataCacheImpl(
-        parent_browser_context_.UniqueId(), parent_browser_context_.GetPath()));
+    recording_data_cache_ = std::make_unique<SiteDataCacheImpl>(
+        parent_browser_context_.UniqueId(), parent_browser_context_.GetPath());
 
     // Wait for the database to be initialized.
     base::RunLoop run_loop;
diff --git a/components/performance_manager/v8_memory/v8_detailed_memory_any_seq.cc b/components/performance_manager/v8_memory/v8_detailed_memory_any_seq.cc
index 0aecf34..20ebc4d 100644
--- a/components/performance_manager/v8_memory/v8_detailed_memory_any_seq.cc
+++ b/components/performance_manager/v8_memory/v8_detailed_memory_any_seq.cc
@@ -98,12 +98,11 @@
     MeasurementMode mode,
     absl::optional<base::WeakPtr<ProcessNode>> process_to_measure) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Can't use make_unique since this calls the private any-sequence
-  // constructor. After construction the V8DetailedMemoryRequest must only be
-  // accessed on the graph sequence.
-  request_ = base::WrapUnique(new V8DetailedMemoryRequest(
+  // After construction the V8DetailedMemoryRequest must only be accessed on
+  // the graph sequence.
+  request_ = std::make_unique<V8DetailedMemoryRequest>(
       base::PassKey<V8DetailedMemoryRequestAnySeq>(), min_time_between_requests,
-      mode, std::move(process_to_measure), weak_factory_.GetWeakPtr()));
+      mode, std::move(process_to_measure), weak_factory_.GetWeakPtr());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -168,12 +167,11 @@
       base::SequenceBound<MeasurementCallback>(
           base::SequencedTaskRunnerHandle::Get(), std::move(callback)));
 
-  // Can't use make_unique since this calls the private any-sequence
-  // constructor. After construction the V8DetailedMemoryRequestOneShot must
-  // only be accessed on the graph sequence.
-  request_ = base::WrapUnique(new V8DetailedMemoryRequestOneShot(
+  // After construction the V8DetailedMemoryRequest must only be accessed on
+  // the graph sequence.
+  request_ = std::make_unique<V8DetailedMemoryRequestOneShot>(
       base::PassKey<V8DetailedMemoryRequestOneShotAnySeq>(),
-      std::move(process_node), std::move(wrapped_callback), mode));
+      std::move(process_node), std::move(wrapped_callback), mode);
 }
 
 // static
diff --git a/components/policy/resources/webui/policy.css b/components/policy/resources/webui/policy.css
index c941438..06f8205 100644
--- a/components/policy/resources/webui/policy.css
+++ b/components/policy/resources/webui/policy.css
@@ -155,8 +155,8 @@
 }
 
 .show-unset-checkbox > label {
-  display: flex;
   align-items: center;
+  display: flex;
   justify-content: space-between;
 }
 
@@ -245,7 +245,8 @@
 }
 
 .messages.row .value,
-.value.row .value {
+.value.row .value,
+.precedence.row .value {
   display: block;
   flex: 1;
 }
diff --git a/components/safe_browsing/core/browser/password_protection/OWNERS b/components/safe_browsing/core/browser/password_protection/OWNERS
index ac5b456..e69de29 100644
--- a/components/safe_browsing/core/browser/password_protection/OWNERS
+++ b/components/safe_browsing/core/browser/password_protection/OWNERS
@@ -1 +0,0 @@
-bdea@chromium.org
diff --git a/components/security_interstitials/content/https_only_mode_blocking_page.cc b/components/security_interstitials/content/https_only_mode_blocking_page.cc
index 09fa24d8..3328297c 100644
--- a/components/security_interstitials/content/https_only_mode_blocking_page.cc
+++ b/components/security_interstitials/content/https_only_mode_blocking_page.cc
@@ -12,6 +12,7 @@
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/core/common_string_util.h"
 #include "components/security_interstitials/core/controller_client.h"
+#include "components/security_interstitials/core/https_only_mode_ui_util.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/security_interstitials/core/pref_names.h"
 #include "components/strings/grit/components_strings.h"
@@ -107,41 +108,8 @@
 
 void HttpsOnlyModeBlockingPage::PopulateInterstitialStrings(
     base::Value* load_time_data) {
-  PopulateValuesForSharedHTML(load_time_data);
-
-  load_time_data->SetStringKey(
-      "tabTitle", l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_TITLE));
-  load_time_data->SetStringKey(
-      "heading", l10n_util::GetStringFUTF16(
-                     IDS_HTTPS_ONLY_MODE_HEADING,
-                     common_string_util::GetFormattedHostName(request_url())));
-  load_time_data->SetStringKey(
-      "primaryParagraph",
-      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH));
-  load_time_data->SetStringKey(
-      "proceedButtonText",
-      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON));
-  load_time_data->SetStringKey(
-      "primaryButtonText",
-      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_BACK_BUTTON));
-  load_time_data->SetStringKey(
-      "optInLink",
-      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE));
-  load_time_data->SetStringKey(
-      "enhancedProtectionMessage",
-      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_ENHANCED_PROTECTION_MESSAGE));
-}
-
-void HttpsOnlyModeBlockingPage::PopulateValuesForSharedHTML(
-    base::Value* load_time_data) {
-  load_time_data->SetStringKey("type", "HTTPS_ONLY");
-  load_time_data->SetBoolKey("overridable", false);
-  load_time_data->SetBoolKey("hide_primary_button", false);
-  load_time_data->SetBoolKey("show_recurrent_error_paragraph", false);
-  load_time_data->SetStringKey("recurrentErrorParagraph", "");
-  load_time_data->SetStringKey("openDetails", "");
-  load_time_data->SetStringKey("explanationParagraph", "");
-  load_time_data->SetStringKey("finalParagraph", "");
+  PopulateHttpsOnlyModeStringsForSharedHTML(load_time_data);
+  PopulateHttpsOnlyModeStringsForBlockingPage(load_time_data, request_url());
 }
 
 }  // namespace security_interstitials
diff --git a/components/security_interstitials/content/https_only_mode_blocking_page.h b/components/security_interstitials/content/https_only_mode_blocking_page.h
index 7f2b497..9d6daa1 100644
--- a/components/security_interstitials/content/https_only_mode_blocking_page.h
+++ b/components/security_interstitials/content/https_only_mode_blocking_page.h
@@ -31,9 +31,6 @@
   void PopulateInterstitialStrings(base::Value* load_time_data) override;
 
  private:
-  // Adds values required for shared interstitial HTML to |load_time_data|.
-  void PopulateValuesForSharedHTML(base::Value* load_time_data);
-
   bool user_made_decision_ = false;
 };
 
diff --git a/components/security_interstitials/core/BUILD.gn b/components/security_interstitials/core/BUILD.gn
index 346f5b5..33ff61067 100644
--- a/components/security_interstitials/core/BUILD.gn
+++ b/components/security_interstitials/core/BUILD.gn
@@ -14,6 +14,8 @@
     "common_string_util.h",
     "controller_client.cc",
     "controller_client.h",
+    "https_only_mode_ui_util.cc",
+    "https_only_mode_ui_util.h",
     "metrics_helper.cc",
     "metrics_helper.h",
     "mitm_software_ui.cc",
diff --git a/components/security_interstitials/core/https_only_mode_ui_util.cc b/components/security_interstitials/core/https_only_mode_ui_util.cc
new file mode 100644
index 0000000..a3ffc0e
--- /dev/null
+++ b/components/security_interstitials/core/https_only_mode_ui_util.cc
@@ -0,0 +1,50 @@
+// 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/security_interstitials/core/https_only_mode_ui_util.h"
+
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+void PopulateHttpsOnlyModeStringsForBlockingPage(base::Value* load_time_data,
+                                                 const GURL& url) {
+  PopulateHttpsOnlyModeStringsForSharedHTML(load_time_data);
+
+  load_time_data->SetStringKey(
+      "tabTitle", l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_TITLE));
+  load_time_data->SetStringKey(
+      "heading",
+      l10n_util::GetStringFUTF16(
+          IDS_HTTPS_ONLY_MODE_HEADING,
+          security_interstitials::common_string_util::GetFormattedHostName(
+              url)));
+  load_time_data->SetStringKey(
+      "primaryParagraph",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_PRIMARY_PARAGRAPH));
+  load_time_data->SetStringKey(
+      "proceedButtonText",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_SUBMIT_BUTTON));
+  load_time_data->SetStringKey(
+      "primaryButtonText",
+      l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_BACK_BUTTON));
+  load_time_data->SetStringKey(
+      "optInLink",
+      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE));
+  load_time_data->SetStringKey(
+      "enhancedProtectionMessage",
+      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_ENHANCED_PROTECTION_MESSAGE));
+}
+
+void PopulateHttpsOnlyModeStringsForSharedHTML(base::Value* load_time_data) {
+  load_time_data->SetStringKey("type", "HTTPS_ONLY");
+  load_time_data->SetBoolKey("overridable", false);
+  load_time_data->SetBoolKey("hide_primary_button", false);
+  load_time_data->SetBoolKey("show_recurrent_error_paragraph", false);
+  load_time_data->SetStringKey("recurrentErrorParagraph", "");
+  load_time_data->SetStringKey("openDetails", "");
+  load_time_data->SetStringKey("explanationParagraph", "");
+  load_time_data->SetStringKey("finalParagraph", "");
+}
diff --git a/components/security_interstitials/core/https_only_mode_ui_util.h b/components/security_interstitials/core/https_only_mode_ui_util.h
new file mode 100644
index 0000000..c4eb62b
--- /dev/null
+++ b/components/security_interstitials/core/https_only_mode_ui_util.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_UI_UTIL_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_UI_UTIL_H_
+
+namespace base {
+class Value;
+}  // namespace base
+
+class GURL;
+
+// Populates |load_time_data| for interstitial HTML.
+void PopulateHttpsOnlyModeStringsForBlockingPage(base::Value* load_time_data,
+                                                 const GURL& url);
+
+// Values added to get shared interstitial HTML to play nice.
+void PopulateHttpsOnlyModeStringsForSharedHTML(base::Value* load_time_data);
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_UI_UTIL_H_
diff --git a/components/segmentation_platform/DEPS b/components/segmentation_platform/DEPS
index de9d9bd..b264cd0a 100644
--- a/components/segmentation_platform/DEPS
+++ b/components/segmentation_platform/DEPS
@@ -6,6 +6,7 @@
   "-components/optimization_guide/content",
   "+components/prefs",
   "+services/metrics/public",
+  "+sql",
   "+third_party/perfetto",
   "+third_party/tflite",
   "+third_party/tflite_support",
diff --git a/components/segmentation_platform/components_unittests.filter b/components/segmentation_platform/components_unittests.filter
index 36bd497..e5b34d2 100644
--- a/components/segmentation_platform/components_unittests.filter
+++ b/components/segmentation_platform/components_unittests.filter
@@ -29,4 +29,5 @@
 TrainingDataCollectorTest.*
 UkmConfigTest.*
 UrlSignalHandlerTest.*
+UkmMetricsTableTest.*
 UserActionSignalHandlerTest.*
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index 02ca6180..dfd447d8 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -37,6 +37,8 @@
     "database/signal_storage_config.h",
     "database/ukm_database.cc",
     "database/ukm_database.h",
+    "database/ukm_metrics_table.cc",
+    "database/ukm_metrics_table.h",
     "database/ukm_types.h",
     "dummy_segmentation_platform_service.cc",
     "dummy_segmentation_platform_service.h",
@@ -112,6 +114,7 @@
     "//components/segmentation_platform/public",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/metrics/public/cpp:ukm_builders",
+    "//sql:sql",
   ]
 
   public_deps = [
@@ -167,6 +170,7 @@
     "database/signal_storage_config_unittest.cc",
     "database/test_segment_info_database.cc",
     "database/test_segment_info_database.h",
+    "database/ukm_metrics_table_unittest.cc",
     "dummy_segmentation_platform_service_unittest.cc",
     "execution/dummy_model_execution_manager_unittest.cc",
     "execution/feature_aggregator_impl_unittest.cc",
@@ -209,6 +213,7 @@
     "//components/segmentation_platform/public",
     "//components/ukm:test_support",
     "//services/metrics/public/cpp:ukm_builders",
+    "//sql:sql",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/segmentation_platform/internal/database/ukm_metrics_table.cc b/components/segmentation_platform/internal/database/ukm_metrics_table.cc
new file mode 100644
index 0000000..52b47c6
--- /dev/null
+++ b/components/segmentation_platform/internal/database/ukm_metrics_table.cc
@@ -0,0 +1,182 @@
+// 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/segmentation_platform/internal/database/ukm_metrics_table.h"
+
+#include <inttypes.h>
+#include <cstdint>
+
+#include "base/check_op.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "sql/database.h"
+#include "sql/statement.h"
+
+namespace segmentation_platform {
+
+namespace {}  // namespace
+
+UkmMetricsTable::UkmMetricsTable(sql::Database* db) : db_(db) {
+  DCHECK(db_);
+}
+
+UkmMetricsTable::~UkmMetricsTable() = default;
+
+bool UkmMetricsTable::InitTable() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  static constexpr char kCreateTableQuery[] =
+      // clang-format off
+      "CREATE TABLE IF NOT EXISTS metrics("
+          "id INTEGER PRIMARY KEY NOT NULL,"
+          "event_timestamp INTEGER NOT NULL,"
+          "ukm_source_id INTEGER NOT NULL,"
+          "url_id INTEGER NOT NULL,"
+          "event_id INTEGER NOT NULL,"
+          "event_hash TEXT NOT NULL,"
+          "metric_hash TEXT NOT NULL,"
+          "metric_value INTEGER NOT NULL)";
+  // clang-format on
+  if (!db_->Execute(kCreateTableQuery)) {
+    return false;
+  }
+
+  // Accelerates get entries for a range of event_timestamps.
+  if (!db_->Execute("CREATE INDEX IF NOT EXISTS event_timestamp_index ON "
+                    "metrics(event_timestamp)")) {
+    return false;
+  }
+  // Accelerates update entries for a given ukm_source_id.
+  if (!db_->Execute("CREATE INDEX IF NOT EXISTS ukm_source_id_index ON "
+                    "metrics(ukm_source_id)")) {
+    return false;
+  }
+  // Accelerates find metrics for a given url_id, join with URL table using
+  // url_id.
+  if (!db_->Execute("CREATE INDEX IF NOT EXISTS url_id_index ON "
+                    "metrics(url_id)")) {
+    return false;
+  }
+  // Accelerates find value for a given event_hash and metric_hash.
+  if (!db_->Execute("CREATE INDEX IF NOT EXISTS event_hash_index ON "
+                    "metrics(event_hash)")) {
+    return false;
+  }
+
+  // Other common queries using the metrics table:
+  // * Join metrics table with itself using event_id, to find metrics of the
+  //   same event.
+  //
+  // Metric hashes should not be used to query on the database unless event hash
+  // is also used as filter. So, just having an index for event_hash is good
+  // enough. Event ID is also used only after filtering the table by the
+  // event_hash. So, event_id does not need an index.
+
+  return true;
+}
+
+bool UkmMetricsTable::AddUkmEvent(const UkmMetricsTable::MetricsRow& row) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!row.id);
+  // Verify visit row.
+  if (row.metric_hash.is_null() || row.event_hash.is_null()) {
+    return false;
+  }
+  if (row.event_timestamp.is_null() ||
+      row.event_timestamp < base::Time::Now() - kNumDaysToKeepUkm) {
+    return false;
+  }
+
+  static constexpr char kInsertQuery[] =
+      // clang-format off
+      "INSERT INTO metrics(event_timestamp,ukm_source_id,url_id,event_id,"
+          "event_hash,metric_hash,metric_value) "
+          "VALUES (?,?,?,?,?,?,?)";
+  // clang-format on
+
+  sql::Statement statement(
+      db_->GetCachedStatement(SQL_FROM_HERE, kInsertQuery));
+  statement.BindTime(0, row.event_timestamp);
+  statement.BindInt64(1, row.source_id);
+  statement.BindInt64(2, row.url_id.GetUnsafeValue());
+  statement.BindInt64(3, row.event_id.GetUnsafeValue());
+  uint64_t event_hash = row.event_hash.GetUnsafeValue();
+  statement.BindString(4, base::StringPrintf("%" PRIX64, event_hash));
+  uint64_t metric_hash = row.metric_hash.GetUnsafeValue();
+  statement.BindString(5, base::StringPrintf("%" PRIX64, metric_hash));
+  statement.BindInt64(6, row.metric_value);
+  return statement.Run();
+}
+
+std::string HashToHexString(uint64_t hash) {
+  return base::StringPrintf("%" PRIX64, hash);
+}
+
+bool UkmMetricsTable::UpdateUrlIdForSource(ukm::SourceId source_id,
+                                           UrlId url_id) {
+  DCHECK_NE(source_id, ukm::kInvalidSourceId);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  static constexpr char kUpdateUrlIdQuery[] =
+      "UPDATE metrics SET url_id = ? WHERE ukm_source_id = ?";
+  sql::Statement statement(
+      db_->GetCachedStatement(SQL_FROM_HERE, kUpdateUrlIdQuery));
+  statement.BindInt64(0, url_id.GetUnsafeValue());
+  statement.BindInt64(1, source_id);
+
+  return statement.Run();
+}
+
+bool UkmMetricsTable::DeleteEventsForUrls(const std::vector<UrlId>& urls) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  static constexpr char kDeleteForUrl[] =
+      "DELETE FROM metrics WHERE url_id = ?";
+  bool success = true;
+  sql::Statement statement(
+      db_->GetCachedStatement(SQL_FROM_HERE, kDeleteForUrl));
+  for (const UrlId& url_id : urls) {
+    statement.Reset(/*clear_bound_vars=*/true);
+    statement.BindInt64(0, url_id.GetUnsafeValue());
+    if (!statement.Run()) {
+      success = false;
+    }
+  }
+  return success;
+}
+
+bool UkmMetricsTable::DeleteEventsBeforeTimestamp(base::Time time) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  static constexpr char kDeleteoldEntries[] =
+      "DELETE FROM metrics WHERE event_timestamp <= ?";
+  sql::Statement statement(
+      db_->GetCachedStatement(SQL_FROM_HERE, kDeleteoldEntries));
+  statement.BindTime(0, time);
+  return statement.Run();
+}
+
+// static
+UkmMetricsTable::MetricsRow UkmMetricsTable::FillRowFromStatementForTesting(
+    sql::Statement& statement) {
+  DCHECK(statement.is_valid());
+  DCHECK_EQ(statement.ColumnCount(), 8);
+
+  UkmMetricsTable::MetricsRow row;
+  row.id = MetricsRowId::FromUnsafeValue(statement.ColumnInt(0));
+  row.event_timestamp = statement.ColumnTime(1);
+  row.source_id = statement.ColumnInt64(2);
+  row.url_id = UrlId::FromUnsafeValue(statement.ColumnInt64(3));
+  row.event_id = MetricsRowEventId::FromUnsafeValue(statement.ColumnInt64(4));
+  uint64_t event_hash = 0;
+  if (base::HexStringToUInt64(statement.ColumnString(5), &event_hash))
+    row.event_hash = UkmEventHash::FromUnsafeValue(event_hash);
+  uint64_t metric_hash = 0;
+  if (base::HexStringToUInt64(statement.ColumnString(6), &metric_hash))
+    row.metric_hash = UkmMetricHash::FromUnsafeValue(metric_hash);
+  row.metric_value = statement.ColumnInt64(7);
+  return row;
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/database/ukm_metrics_table.h b/components/segmentation_platform/internal/database/ukm_metrics_table.h
new file mode 100644
index 0000000..903150a
--- /dev/null
+++ b/components/segmentation_platform/internal/database/ukm_metrics_table.h
@@ -0,0 +1,97 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_METRICS_TABLE_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_METRICS_TABLE_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "base/types/id_type.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+
+namespace sql {
+class Database;
+class Statement;
+}  // namespace sql
+
+namespace segmentation_platform {
+
+using MetricsRowId = base::IdType64<class MetricsRowTag>;
+using MetricsRowEventId = base::IdType64<class MetricsRowEventIdTag>;
+
+// Handles database queries for the UKM metrics table in UKM database.
+class UkmMetricsTable {
+ public:
+  static constexpr char kTableName[] = "metrics";
+
+  // Represents a row in the metrics table.
+  struct MetricsRow {
+    MetricsRow() = default;
+    ~MetricsRow() = default;
+
+    // Timestamp of the event, all the metrics in the event will have the same
+    // timestamp. The timestamp is approximate and generated by the database
+    // when getting notifications about UKM. Timestamps are required since its
+    // used for deleting old entries.
+    base::Time event_timestamp;
+
+    // ID is not required to be filled in when inserting the row, and will not
+    // be used. The ID will be generated by sql as primary key.
+    MetricsRowId id;
+
+    // ID of the URL, used to join with the URL table row.
+    UrlId url_id;
+    // UKM source ID for the entry.
+    ukm::SourceId source_id = ukm::kInvalidSourceId;
+    // Unique event ID associated with the UKM event. All metrics recorded with
+    // in the event will have the same event ID.
+    MetricsRowEventId event_id;
+
+    UkmEventHash event_hash;
+    UkmMetricHash metric_hash;
+    int64_t metric_value = 0;
+  };
+
+  explicit UkmMetricsTable(sql::Database* db);
+  ~UkmMetricsTable();
+
+  UkmMetricsTable(const UkmMetricsTable&) = delete;
+  UkmMetricsTable& operator=(const UkmMetricsTable&) = delete;
+
+  // Creates the metrics table if it doesn't exist.
+  bool InitTable();
+
+  // Adds the given row to the metrics table, does not check for duplicate
+  // entries.
+  bool AddUkmEvent(const MetricsRow& row);
+
+  // Updates URL ID of all the rows with |url_id| when the |source_id| matches.
+  bool UpdateUrlIdForSource(ukm::SourceId source_id, UrlId url_id);
+
+  // Deletes all rows associated with any of the ID from |urls|.
+  bool DeleteEventsForUrls(const std::vector<UrlId>& urls);
+
+  // Deletes all entries that have an event timestamp earlier to |time|.
+  bool DeleteEventsBeforeTimestamp(base::Time time);
+
+ private:
+  friend class UkmMetricsTableTest;
+
+  // Gets values from all the columns from the |statement|, assuming the
+  // statement is already executed (Step()), and the current row is valid. This
+  // function only works for "SELECT * FROM metrics" queries, that select all
+  // columns in order.
+  static MetricsRow FillRowFromStatementForTesting(sql::Statement& statement);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  const raw_ptr<sql::Database> db_ GUARDED_BY_CONTEXT(sequence_checker_);
+};
+
+}  // namespace segmentation_platform
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_METRICS_TABLE_H_
diff --git a/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc b/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc
new file mode 100644
index 0000000..f5829656
--- /dev/null
+++ b/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc
@@ -0,0 +1,281 @@
+// 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/segmentation_platform/internal/database/ukm_metrics_table.h"
+
+#include <memory>
+
+#include "base/rand_util.h"
+#include "sql/database.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform {
+
+namespace {
+
+void ExpectRowIsEqual(const UkmMetricsTable::MetricsRow& row1,
+                      const UkmMetricsTable::MetricsRow& row2) {
+  // Skip checking ID.
+  EXPECT_EQ(row1.url_id, row2.url_id);
+  EXPECT_EQ(row1.event_timestamp, row2.event_timestamp);
+  EXPECT_EQ(row1.source_id, row2.source_id);
+  EXPECT_EQ(row1.event_id, row2.event_id);
+  EXPECT_EQ(row1.event_hash, row2.event_hash);
+  EXPECT_EQ(row1.metric_hash, row2.metric_hash);
+  EXPECT_EQ(row1.metric_value, row2.metric_value);
+}
+
+UkmMetricsTable::MetricsRow GetSampleRow() {
+  static auto event_id_generator = MetricsRowEventId::Generator();
+  static auto event_hash_generator = UkmEventHash::Generator();
+  static auto metric_hash_generator = UkmMetricHash::Generator();
+  return UkmMetricsTable::MetricsRow{
+      .event_timestamp = base::Time::Now(),
+      .source_id = base::RandInt(0, 1000),
+      .event_id = event_id_generator.GenerateNextId(),
+      .event_hash = event_hash_generator.GenerateNextId(),
+      .metric_hash = metric_hash_generator.GenerateNextId(),
+      .metric_value = base::RandInt(-1000, 1000)};
+}
+
+}  // namespace
+
+class UkmMetricsTableTest : public testing::Test {
+ public:
+  UkmMetricsTableTest() = default;
+  ~UkmMetricsTableTest() override = default;
+
+  void SetUp() override {
+    sql::DatabaseOptions options;
+    db_ = std::make_unique<sql::Database>(options);
+    bool opened = db_->OpenInMemory();
+    ASSERT_TRUE(opened);
+    metrics_table_ = std::make_unique<UkmMetricsTable>(db_.get());
+  }
+
+  void TearDown() override {
+    metrics_table_.reset();
+    db_.reset();
+  }
+
+  UkmMetricsTable& metrics_table() { return *metrics_table_; }
+
+  sql::Database& db() { return *db_; }
+
+  absl::optional<UkmMetricsTable::MetricsRow> GetSingleRow(const char* query) {
+    sql::Statement statement(db_->GetUniqueStatement(query));
+    if (statement.Step()) {
+      return UkmMetricsTable::FillRowFromStatementForTesting(statement);
+    }
+    return absl::nullopt;
+  }
+
+  void AssertRowsInTable(const std::vector<UkmMetricsTable::MetricsRow>& rows) {
+    sql::Statement statement(db_->GetCachedStatement(
+        SQL_FROM_HERE, "SELECT * FROM metrics ORDER BY id"));
+    std::vector<UkmMetricsTable::MetricsRow> actual_rows;
+    while (statement.Step()) {
+      actual_rows.emplace_back(
+          UkmMetricsTable::FillRowFromStatementForTesting(statement));
+    }
+    ASSERT_EQ(actual_rows.size(), rows.size());
+    auto it1 = actual_rows.begin();
+    auto it2 = rows.begin();
+    for (; it1 != actual_rows.end(); ++it1, ++it2) {
+      ExpectRowIsEqual(*it1, *it2);
+    }
+  }
+
+ private:
+  std::unique_ptr<sql::Database> db_;
+  std::unique_ptr<UkmMetricsTable> metrics_table_;
+};
+
+TEST_F(UkmMetricsTableTest, CreateTable) {
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  EXPECT_TRUE(db().DoesTableExist(UkmMetricsTable::kTableName));
+  EXPECT_TRUE(db().DoesIndexExist("event_timestamp_index"));
+  EXPECT_TRUE(db().DoesIndexExist("url_id_index"));
+  EXPECT_TRUE(db().DoesIndexExist("ukm_source_id_index"));
+  EXPECT_TRUE(db().DoesIndexExist("event_hash_index"));
+
+  // Creating table again should be noop.
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  EXPECT_TRUE(db().DoesTableExist(UkmMetricsTable::kTableName));
+}
+
+TEST_F(UkmMetricsTableTest, InsertRow) {
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  EXPECT_TRUE(db().DoesTableExist(UkmMetricsTable::kTableName));
+
+  // Invalid rows should not inserted.
+  UkmMetricsTable::MetricsRow row;
+  EXPECT_FALSE(metrics_table().AddUkmEvent(row));
+  auto row1 = GetSampleRow();
+  row1.event_timestamp = base::Time();
+  EXPECT_FALSE(metrics_table().AddUkmEvent(row1));
+
+  AssertRowsInTable({});
+
+  auto row2 = GetSampleRow();
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row2));
+  AssertRowsInTable({row2});
+
+  auto row3 = GetSampleRow();
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row3));
+  AssertRowsInTable({row2, row3});
+}
+
+TEST_F(UkmMetricsTableTest, DeleteForUrl) {
+  auto url_id_generator = UrlId::Generator();
+  const UrlId url_id1 = url_id_generator.GenerateNextId();
+  const UrlId url_id2 = url_id_generator.GenerateNextId();
+  const UrlId url_id3 = url_id_generator.GenerateNextId();
+
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  // Delete on empty table does nothing.
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({}));
+  AssertRowsInTable({});
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({url_id1}));
+  AssertRowsInTable({});
+
+  auto row1 = GetSampleRow();
+  row1.url_id = url_id1;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
+  auto row2 = GetSampleRow();
+  row2.url_id = url_id1;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row2));
+  auto row3 = GetSampleRow();
+  row3.url_id = url_id2;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row3));
+  auto row4 = GetSampleRow();
+  row4.url_id = url_id2;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row4));
+
+  AssertRowsInTable({row1, row2, row3, row4});
+
+  // Remove empty URL list.
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({}));
+  AssertRowsInTable({row1, row2, row3, row4});
+
+  // Remove matching URL ID, should remove 2 rows.
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({url_id1}));
+  AssertRowsInTable({row3, row4});
+
+  // Remove non-existent URL IDs, no change to db.
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({url_id1, url_id3}));
+  AssertRowsInTable({row3, row4});
+
+  auto row5 = GetSampleRow();
+  row5.url_id = url_id3;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row5));
+  auto row6 = GetSampleRow();
+  row6.url_id = url_id3;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row6));
+
+  AssertRowsInTable({row3, row4, row5, row6});
+
+  // Remove all URL IDs, should clear the table.
+  EXPECT_TRUE(metrics_table().DeleteEventsForUrls({url_id1, url_id2, url_id3}));
+  AssertRowsInTable({});
+}
+
+TEST_F(UkmMetricsTableTest, DeleteBeforeTimestamp) {
+  const base::Time kTimestamp1 = base::Time::Now();
+  const base::Time kTimestamp2 = kTimestamp1 + base::Seconds(1);
+  const base::Time kTimestamp3 = kTimestamp1 + base::Seconds(2);
+  const base::Time kTimestamp4 = kTimestamp1 + base::Seconds(3);
+  const base::Time kTimestamp5 = kTimestamp1 + base::Seconds(4);
+
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  // Delete on empty table does nothing.
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
+  AssertRowsInTable({});
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
+  AssertRowsInTable({});
+
+  auto row1 = GetSampleRow();
+  row1.event_timestamp = kTimestamp1;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
+  auto row2 = GetSampleRow();
+  row2.event_timestamp = kTimestamp2;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row2));
+  auto row3 = GetSampleRow();
+  row3.event_timestamp = kTimestamp3;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row3));
+  auto row4 = GetSampleRow();
+  row4.event_timestamp = kTimestamp4;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row4));
+
+  AssertRowsInTable({row1, row2, row3, row4});
+
+  // Delete with time before all rows does nothing.
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
+  AssertRowsInTable({row1, row2, row3, row4});
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1 -
+                                                          base::Seconds(1)));
+  AssertRowsInTable({row1, row2, row3, row4});
+
+  // Remove single row.
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
+  AssertRowsInTable({row2, row3, row4});
+
+  auto row5 = GetSampleRow();
+  row5.event_timestamp = kTimestamp5;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row5));
+  AssertRowsInTable({row2, row3, row4, row5});
+
+  // Remove bunch of rows.
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp3));
+  AssertRowsInTable({row4, row5});
+
+  // Insert entry with an older timestamp out of order and remove old entries
+  // should still work.
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
+  AssertRowsInTable({row4, row5, row1});
+  EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp3));
+  AssertRowsInTable({row4, row5});
+}
+
+TEST_F(UkmMetricsTableTest, MatchHashesTest) {
+  const UkmEventHash event_hash1 = UkmEventHash::FromUnsafeValue(1);
+  const UkmEventHash event_hash2 =
+      UkmEventHash::FromUnsafeValue(0x9CEA8CBC362AB242);
+  const UkmEventHash event_hash3 =
+      UkmEventHash::FromUnsafeValue(std::numeric_limits<uint64_t>::max());
+  ASSERT_TRUE(metrics_table().InitTable());
+
+  UkmMetricsTable::MetricsRow row1 = GetSampleRow();
+  row1.event_hash = event_hash1;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
+  UkmMetricsTable::MetricsRow row2 = GetSampleRow();
+  row2.event_hash = event_hash2;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row2));
+  UkmMetricsTable::MetricsRow row3 = GetSampleRow();
+  row3.event_hash = event_hash3;
+  EXPECT_TRUE(metrics_table().AddUkmEvent(row3));
+
+  auto result1 = GetSingleRow("SELECT * FROM metrics WHERE event_hash = '1'");
+  ASSERT_TRUE(result1);
+  ExpectRowIsEqual(row1, *result1);
+
+  auto result2 = GetSingleRow(
+      "SELECT * FROM metrics WHERE event_hash = '9CEA8CBC362AB242'");
+  ASSERT_TRUE(result2);
+  ExpectRowIsEqual(row2, *result2);
+
+  auto result3 = GetSingleRow(
+      "SELECT * FROM metrics WHERE event_hash = 'FFFFFFFFFFFFFFFF'");
+  ASSERT_TRUE(result3);
+  ExpectRowIsEqual(row3, *result3);
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/database/ukm_types.h b/components/segmentation_platform/internal/database/ukm_types.h
index ef2c633..46552d6 100644
--- a/components/segmentation_platform/internal/database/ukm_types.h
+++ b/components/segmentation_platform/internal/database/ukm_types.h
@@ -19,6 +19,28 @@
 using UkmMetricHash = base::IdTypeU64<class UkmMetricHashTag>;
 using UrlId = base::IdType64<class UrlIdTag>;
 
+// A struct that can accommodate multiple output types needed for Segmentation
+// metadata's feature processing. It can only hold one value at a time with the
+// corresponding type.
+struct ProcessedValue {
+  enum Type {
+    UNKNOWN = 0,
+    BOOL = 1,
+    INT = 2,
+    FLOAT = 3,
+    DOUBLE = 4,
+    STRING = 5,
+    TIME = 6,
+  };
+  Type type{UNKNOWN};
+  bool bool_val{false};
+  int int_val{0};
+  float float_val{0};
+  double double_val{0};
+  std::string str_val;
+  base::Time time_val;
+};
+
 }  // namespace segmentation_platform
 
 #endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_TYPES_H_
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
index 90f4bc1..dcd3815b 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Promise;
+import org.chromium.components.signin.base.AccountCapabilities;
 
 import java.util.List;
 
@@ -103,6 +104,13 @@
     void checkChildAccountStatus(Account account, ChildAccountStatusListener listener);
 
     /**
+     * @param account The account used to look up capabilities.
+     * @return account capabilities for the given account.
+     */
+    @MainThread
+    Promise<AccountCapabilities> getAccountCapabilities(Account account);
+
+    /**
      * Gets the boolean for whether the account can offer extended sync promos.
      * If the result is not yet fetched, the optional will be empty.
      */
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
index 71e7d69..2de2b595 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
@@ -28,6 +28,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.components.signin.AccountManagerDelegate.CapabilityResponse;
+import org.chromium.components.signin.base.AccountCapabilities;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -51,6 +52,10 @@
     @VisibleForTesting
     static final String CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS = "gi2tklldmfya";
 
+    // Prefix used to define the capability name for querying Identity services. This
+    // prefix is not required for Android queries to GmsCore.
+    private static final String ACCOUNT_CAPABILITY_NAME_PREFIX = "accountcapabilities/";
+
     private final AccountManagerDelegate mDelegate;
 
     private final ObserverList<AccountsChangeObserver> mObservers = new ObserverList<>();
@@ -209,6 +214,36 @@
         return mDelegate.getAccountGaiaId(accountEmail);
     }
 
+    /**
+     * @param account The account used to look up capabilities.
+     * @return Set of supported account capability values.
+     */
+    @Override
+    public Promise<AccountCapabilities> getAccountCapabilities(Account account) {
+        Promise<AccountCapabilities> accountCapabilitiesPromise = new Promise<>();
+        ThreadUtils.assertOnUiThread();
+        new AsyncTask<AccountCapabilities>() {
+            @Override
+            public AccountCapabilities doInBackground() {
+                Map<String, Integer> capabilitiesResponse = new HashMap<>();
+                for (String capabilityName :
+                        AccountCapabilities.SUPPORTED_ACCOUNT_CAPABILITY_NAMES) {
+                    @CapabilityResponse
+                    int capability = mDelegate.hasCapability(
+                            account, getAndroidCapabilityName(capabilityName));
+                    capabilitiesResponse.put(capabilityName, capability);
+                }
+                return AccountCapabilities.parseFromCapabilitiesResponse(capabilitiesResponse);
+            }
+
+            @Override
+            protected void onPostExecute(AccountCapabilities result) {
+                accountCapabilitiesPromise.fulfill(result);
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        return accountCapabilitiesPromise;
+    }
+
     private void updateCanOfferExtendedSyncPromos(List<Account> accounts) {
         PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
             final Map<String, Boolean> canOfferExtendedSyncPromos = new HashMap<>();
@@ -274,4 +309,15 @@
         }
         return Collections.unmodifiableList(filteredAccounts);
     }
+
+    /**
+     * @param capabilityName the name of the capability used to query Identity services.
+     * @return the name of the capability used to query GmsCore.
+     */
+    static String getAndroidCapabilityName(@NonNull String capabilityName) {
+        if (capabilityName.startsWith(ACCOUNT_CAPABILITY_NAME_PREFIX)) {
+            return capabilityName.substring(ACCOUNT_CAPABILITY_NAME_PREFIX.length());
+        }
+        return capabilityName;
+    }
 }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
index ffad7dec..4165030 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
@@ -31,6 +31,22 @@
     public AccountCapabilities() {}
 
     /**
+     * @param account the given account to retrieve capabilities from.
+     * @param managerDelegate the manager used to query capability responses.
+     * @return the supported account capabilities values.
+     */
+    public static AccountCapabilities parseFromCapabilitiesResponse(
+            Map<String, Integer> capabilityResponses) {
+        AccountCapabilities capabilities = new AccountCapabilities();
+        for (String capabilityName : SUPPORTED_ACCOUNT_CAPABILITY_NAMES) {
+            @AccountManagerDelegate.CapabilityResponse
+            int hasCapability = capabilityResponses.get(capabilityName);
+            capabilities.setAccountCapability(capabilityName, hasCapability);
+        }
+        return capabilities;
+    }
+
+    /**
      * Stores the Capability Value for the given capability name.
      * @param capabilityName One of the supported capability names {@link
      *         #SUPPORTED_ACCOUNT_CAPABILITY_NAMES}.
@@ -65,6 +81,10 @@
                 AccountCapabilitiesConstants.IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME);
     }
 
+    /**
+     * @param capabilityName the name of the capability to lookup.
+     * @return the capability value associated to the name.
+     */
     private @Tribool int getCapabilityByName(@NonNull String capabilityName) {
         if (!mAccountCapabilities.containsKey(capabilityName)) {
             return Tribool.UNKNOWN;
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
index 8f832ef..4a50ae7 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
@@ -21,6 +21,7 @@
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.AuthException;
+import org.chromium.components.signin.base.AccountCapabilities;
 
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
@@ -111,6 +112,11 @@
     }
 
     @Override
+    public Promise<AccountCapabilities> getAccountCapabilities(Account account) {
+        return Promise.fulfilled(new AccountCapabilities());
+    }
+
+    @Override
     public Optional<Boolean> canOfferExtendedSyncPromos(Account account) {
         assert account != null;
         return Optional.absent();
diff --git a/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java b/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
index 715c1f75..033b6f8 100644
--- a/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
+++ b/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
@@ -49,6 +49,7 @@
 import org.chromium.components.externalauth.ExternalAuthUtils;
 import org.chromium.components.signin.AccountManagerDelegate.CapabilityResponse;
 import org.chromium.components.signin.AccountManagerFacade.ChildAccountStatusListener;
+import org.chromium.components.signin.base.AccountCapabilities;
 import org.chromium.components.signin.test.util.AccountHolder;
 import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
 
@@ -347,6 +348,47 @@
         AccountManagerFacadeProvider.getInstance();
     }
 
+    @Test
+    public void testGetAccountCapabilitiesResponseSuccess() {
+        AccountManagerFacade facade = new AccountManagerFacadeImpl(mDelegate);
+        final AccountHolder accountHolder = AccountHolder.createFromEmail("test1@gmail.com");
+        mDelegate.addAccount(accountHolder);
+
+        doReturn(CapabilityResponse.YES)
+                .when(mDelegate)
+                .hasCapability(eq(accountHolder.getAccount()),
+                        eq(AccountManagerFacadeImpl.getAndroidCapabilityName(
+                                org.chromium.components.signin.AccountCapabilitiesConstants
+                                        .IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME)));
+        doReturn(CapabilityResponse.NO)
+                .when(mDelegate)
+                .hasCapability(eq(accountHolder.getAccount()),
+                        eq(AccountManagerFacadeImpl.getAndroidCapabilityName(
+                                org.chromium.components.signin.AccountCapabilitiesConstants
+                                        .CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS_CAPABILITY_NAME)));
+
+        AccountCapabilities capabilities =
+                facade.getAccountCapabilities(accountHolder.getAccount()).getResult();
+        Assert.assertEquals(capabilities.canOfferExtendedSyncPromos(), Tribool.FALSE);
+        Assert.assertEquals(capabilities.isSubjectToParentalControls(), Tribool.TRUE);
+    }
+
+    @Test
+    public void testGetAccountCapabilitiesResponseException() {
+        AccountManagerFacade facade = new AccountManagerFacadeImpl(mDelegate);
+        final AccountHolder accountHolder = AccountHolder.createFromEmail("test1@gmail.com");
+        mDelegate.addAccount(accountHolder);
+
+        doReturn(CapabilityResponse.EXCEPTION)
+                .when(mDelegate)
+                .hasCapability(eq(accountHolder.getAccount()), any());
+
+        AccountCapabilities capabilities =
+                facade.getAccountCapabilities(accountHolder.getAccount()).getResult();
+        Assert.assertEquals(capabilities.canOfferExtendedSyncPromos(), Tribool.UNKNOWN);
+        Assert.assertEquals(capabilities.isSubjectToParentalControls(), Tribool.UNKNOWN);
+    }
+
     private Account setFeaturesForAccount(String email, String... features) {
         final Account account = AccountUtils.createAccountFromName(email);
         mShadowAccountManager.setFeatures(account, features);
diff --git a/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java b/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
index 6e438fff..3e35533 100644
--- a/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
+++ b/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
@@ -4,7 +4,10 @@
 
 package org.chromium.components.signin.base;
 
+import static org.mockito.Mockito.spy;
+
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
@@ -13,6 +16,9 @@
 import org.chromium.components.signin.AccountCapabilitiesConstants;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.Tribool;
+import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
+
+import java.util.HashMap;
 
 /**
  * Test class for {@link AccountCapabilities}.
@@ -20,6 +26,13 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public final class AccountCapabilitiesTest {
+    private FakeAccountManagerDelegate mDelegate;
+
+    @Before
+    public void setUp() {
+        mDelegate = spy(new FakeAccountManagerDelegate());
+    }
+
     @Test
     public void testCanOfferExtendedSyncPromosException() {
         AccountCapabilities capabilities = new AccountCapabilities();
@@ -101,4 +114,38 @@
                 AccountManagerDelegate.CapabilityResponse.EXCEPTION);
         Assert.assertEquals(capabilities.isSubjectToParentalControls(), Tribool.FALSE);
     }
+
+    @Test
+    public void testParseFromCapabilitiesResponseWithSuccessResponse() {
+        AccountCapabilities capabilities =
+                AccountCapabilities.parseFromCapabilitiesResponse(new HashMap<String, Integer>() {
+                    {
+                        put(AccountCapabilitiesConstants
+                                        .IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME,
+                                AccountManagerDelegate.CapabilityResponse.YES);
+                        put(AccountCapabilitiesConstants
+                                        .CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS_CAPABILITY_NAME,
+                                AccountManagerDelegate.CapabilityResponse.NO);
+                    }
+                });
+        Assert.assertEquals(capabilities.canOfferExtendedSyncPromos(), Tribool.FALSE);
+        Assert.assertEquals(capabilities.isSubjectToParentalControls(), Tribool.TRUE);
+    }
+
+    @Test
+    public void testParseFromCapabilitiesResponseWithExceptionResponse() {
+        AccountCapabilities capabilities =
+                AccountCapabilities.parseFromCapabilitiesResponse(new HashMap<String, Integer>() {
+                    {
+                        put(AccountCapabilitiesConstants
+                                        .IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME,
+                                AccountManagerDelegate.CapabilityResponse.EXCEPTION);
+                        put(AccountCapabilitiesConstants
+                                        .CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS_CAPABILITY_NAME,
+                                AccountManagerDelegate.CapabilityResponse.EXCEPTION);
+                    }
+                });
+        Assert.assertEquals(capabilities.canOfferExtendedSyncPromos(), Tribool.UNKNOWN);
+        Assert.assertEquals(capabilities.isSubjectToParentalControls(), Tribool.UNKNOWN);
+    }
 }
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index bc859e7..1912b23 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -38,7 +38,8 @@
 
     deps += [
       "//ash/constants",
-      "//chromeos/dbus/dlcservice",
+      "//chromeos/dbus/dlcservice:dlcservice",
+      "//chromeos/dbus/dlcservice:dlcservice_proto",
       "//ui/base",
     ]
   }
diff --git a/components/soda/soda_installer_impl_chromeos.cc b/components/soda/soda_installer_impl_chromeos.cc
index 8927ca9..156f99d 100644
--- a/components/soda/soda_installer_impl_chromeos.cc
+++ b/components/soda/soda_installer_impl_chromeos.cc
@@ -9,6 +9,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -53,8 +54,10 @@
   soda_progress_ = 0.0;
 
   // Install SODA DLC.
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kSodaDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kSodaDlcName,
+      install_request,
       base::BindOnce(&SodaInstallerImplChromeOS::OnSodaInstalled,
                      base::Unretained(this), base::Time::Now()),
       base::BindRepeating(&SodaInstallerImplChromeOS::OnSodaProgress,
@@ -79,8 +82,10 @@
 
   language_pack_progress_.insert({LanguageCode::kEnUs, 0.0});
 
+  dlcservice::InstallRequest install_request;
+  install_request.set_id(kSodaEnglishUsDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      kSodaEnglishUsDlcName,
+      install_request,
       base::BindOnce(&SodaInstallerImplChromeOS::OnLanguageInstalled,
                      base::Unretained(this), LanguageCode::kEnUs,
                      base::Time::Now()),
diff --git a/components/web_package/OWNERS b/components/web_package/OWNERS
index 0addbbd1..db572530 100644
--- a/components/web_package/OWNERS
+++ b/components/web_package/OWNERS
@@ -1,3 +1,2 @@
 horo@chromium.org
-kinuko@chromium.org
 ksakamoto@chromium.org
diff --git a/components/webapps/browser/BUILD.gn b/components/webapps/browser/BUILD.gn
index 76733d4..594604a 100644
--- a/components/webapps/browser/BUILD.gn
+++ b/components/webapps/browser/BUILD.gn
@@ -10,6 +10,7 @@
   sources = [
     "install_result_code.cc",
     "install_result_code.h",
+    "uninstall_result_code.h",
   ]
 }
 
diff --git a/components/webapps/browser/uninstall_result_code.h b/components/webapps/browser/uninstall_result_code.h
new file mode 100644
index 0000000..5b201c21
--- /dev/null
+++ b/components/webapps/browser/uninstall_result_code.h
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_WEBAPPS_BROWSER_UNINSTALL_RESULT_CODE_H_
+#define COMPONENTS_WEBAPPS_BROWSER_UNINSTALL_RESULT_CODE_H_
+
+namespace webapps {
+
+enum class UninstallResultCode {
+  kSuccess = 0,
+  kError = 1,
+};
+
+}
+
+#endif  // COMPONENTS_WEBAPPS_BROWSER_UNINSTALL_RESULT_CODE_H_
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index da0d4a6..157a224 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -600,7 +600,7 @@
     int32_t unique_id) {
   WebContentsAccessibilityAndroid* wcax = GetWebContentsAXFromRootManager();
   if (!wcax)
-    return nullptr;
+    return {};
 
   return wcax->GenerateAccessibilityNodeInfoString(unique_id);
 }
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 95dc993..4e93ba3 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -621,7 +621,7 @@
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
   if (obj.is_null())
-    return nullptr;
+    return {};
 
   return base::android::ConvertJavaStringToUTF16(
       Java_WebContentsAccessibilityImpl_generateAccessibilityNodeInfoString(
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_sources.cc b/content/browser/attribution_reporting/attribution_aggregatable_sources.cc
index d0ceb71..fcc3930 100644
--- a/content/browser/attribution_reporting/attribution_aggregatable_sources.cc
+++ b/content/browser/attribution_reporting/attribution_aggregatable_sources.cc
@@ -18,7 +18,7 @@
     proto::AttributionAggregatableSources proto) {
   bool is_valid =
       proto.sources().size() <=
-          blink::kMaxAttributionAggregatableKeysPerSource &&
+          blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger &&
       base::ranges::all_of(proto.sources(), [](const auto& source) {
         return source.first.size() <=
                    blink::kMaxBytesPerAttributionAggregatableKeyId &&
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index ec34d37..3bc8102 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -291,9 +291,10 @@
 
   const AggregatableSourcesSizeTestCase kTestCases[] = {
       {"empty", true, 0, 0},
-      {"max_keys", true, blink::kMaxAttributionAggregatableKeysPerSource, 1},
+      {"max_keys", true,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
       {"too_many_keys", false,
-       blink::kMaxAttributionAggregatableKeysPerSource + 1, 1},
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
       {"max_key_size", true, 1,
        blink::kMaxBytesPerAttributionAggregatableKeyId},
       {"excessive_key_size", false, 1,
diff --git a/content/browser/attribution_reporting/attribution_internals.mojom b/content/browser/attribution_reporting/attribution_internals.mojom
index a96d71d8..06d21ba 100644
--- a/content/browser/attribution_reporting/attribution_internals.mojom
+++ b/content/browser/attribution_reporting/attribution_internals.mojom
@@ -43,6 +43,7 @@
     kProhibitedByBrowserPolicy,
     kSent,
     kNetworkError,
+    kNoMatchingEventTriggers,
   };
 
   Status status;
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index ab4c39e4..78c61f5 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -314,6 +314,9 @@
       status = mojom::WebUIAttributionReport::Status::
           kNoReportCapacityForDestinationSite;
       break;
+    case AttributionTrigger::Result::kNoMatchingEventTriggers:
+      status = mojom::WebUIAttributionReport::Status::kNoMatchingEventTriggers;
+      break;
     case AttributionTrigger::Result::kInternalError:
       // `kInternalError` doesn't always have a dropped report.
       if (!result.dropped_report().has_value())
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index b1177ea..2d42c86d 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -385,14 +385,14 @@
   builder.SetExpiry(kImpressionExpiry).SetSourceEventId(100);
   attribution_manager_->HandleSource(builder.Build());
 
-  auto conversion = DefaultTrigger();
+  auto conversion = TriggerBuilder().SetTriggerData(5).Build();
   attribution_manager_->HandleTrigger(conversion);
 
   AttributionReport expected_report =
       ReportBuilder(AttributionInfoBuilder(builder.BuildStored())
                         .SetTime(base::Time::Now())
                         .Build())
-          .SetTriggerData(conversion.trigger_data())
+          .SetTriggerData(5)
           .SetReportTime(base::Time::Now() + kFirstReportingWindow)
           .Build();
 
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc
index d6b55be8..b34eb0c 100644
--- a/content/browser/attribution_reporting/attribution_src_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -29,10 +29,12 @@
 
 namespace {
 
+using ::testing::AllOf;
 using ::testing::ElementsAre;
 using ::testing::Field;
 using ::testing::IsEmpty;
 using ::testing::Pair;
+using ::testing::Pointee;
 using ::testing::SizeIs;
 
 std::unique_ptr<MockDataHost> GetRegisteredDataHost(
@@ -40,6 +42,34 @@
   return std::make_unique<MockDataHost>(std::move(data_host));
 }
 
+MATCHER_P(AggregatableKeyIs, matcher, "") {
+  return ExplainMatchResult(matcher, arg.key, result_listener);
+}
+
+MATCHER_P(AggregatableKeyHighBitsIs, matcher, "") {
+  return ExplainMatchResult(matcher, arg.high_bits, result_listener);
+}
+
+MATCHER_P(AggregatableKeyLowBitsIs, matcher, "") {
+  return ExplainMatchResult(matcher, arg.low_bits, result_listener);
+}
+
+MATCHER_P(SourceKeysAre, matcher, "") {
+  return ExplainMatchResult(matcher, arg.source_keys, result_listener);
+}
+
+MATCHER_P(FiltersAre, matcher, "") {
+  return ExplainMatchResult(matcher, arg.filters, result_listener);
+}
+
+MATCHER_P(NotFiltersAre, matcher, "") {
+  return ExplainMatchResult(matcher, arg.not_filters, result_listener);
+}
+
+MATCHER_P(FilterValuesAre, matcher, "") {
+  return ExplainMatchResult(matcher, arg.filter_values, result_listener);
+}
+
 }  // namespace
 
 class AttributionSrcBrowserTest : public ContentBrowserTest {
@@ -371,8 +401,18 @@
   EXPECT_EQ(trigger_data.size(), 1u);
   EXPECT_EQ(trigger_data.front()->reporting_origin,
             url::Origin::Create(register_url));
+  EXPECT_THAT(trigger_data.front()->filters->filter_values, IsEmpty());
   EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u);
   EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u);
+  EXPECT_THAT(
+      trigger_data.front()->event_triggers.front()->filters->filter_values,
+      IsEmpty());
+  EXPECT_THAT(
+      trigger_data.front()->event_triggers.front()->not_filters->filter_values,
+      IsEmpty());
+  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->trigger_data,
+              IsEmpty());
+  EXPECT_THAT(trigger_data.front()->aggregatable_values->values, IsEmpty());
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
@@ -404,6 +444,9 @@
   EXPECT_EQ(trigger_data.size(), 1u);
   EXPECT_EQ(trigger_data.front()->reporting_origin,
             url::Origin::Create(register_url));
+  EXPECT_THAT(
+      trigger_data.front()->filters->filter_values,
+      ElementsAre(Pair("w", IsEmpty()), Pair("x", ElementsAre("y", "z"))));
   EXPECT_EQ(trigger_data.front()->event_triggers.size(), 2u);
 
   // Verify first trigger.
@@ -411,10 +454,85 @@
   EXPECT_EQ(event_trigger_datas.front()->data, 1u);
   EXPECT_EQ(event_trigger_datas.front()->priority, 5);
   EXPECT_EQ(event_trigger_datas.front()->dedup_key->value, 1024u);
+  EXPECT_THAT(event_trigger_datas.front()->filters->filter_values,
+              ElementsAre(Pair("a", ElementsAre("b"))));
+  EXPECT_THAT(event_trigger_datas.front()->not_filters->filter_values,
+              ElementsAre(Pair("c", IsEmpty())));
 
+  // Verify second trigger.
   EXPECT_EQ(event_trigger_datas.back()->data, 2u);
   EXPECT_EQ(event_trigger_datas.back()->priority, 10);
   EXPECT_FALSE(event_trigger_datas.back()->dedup_key);
+  EXPECT_THAT(event_trigger_datas.back()->filters->filter_values, IsEmpty());
+  EXPECT_THAT(
+      event_trigger_datas.back()->not_filters->filter_values,
+      ElementsAre(Pair("d", ElementsAre("e", "f")), Pair("g", IsEmpty())));
+
+  EXPECT_THAT(
+      trigger_data.front()->aggregatable_trigger->trigger_data,
+      ElementsAre(Pointee(
+          AllOf(AggregatableKeyIs(Pointee(AllOf(AggregatableKeyHighBitsIs(0),
+                                                AggregatableKeyLowBitsIs(1)))),
+                SourceKeysAre(ElementsAre("key"))))));
+
+  EXPECT_THAT(trigger_data.front()->aggregatable_values->values,
+              ElementsAre(Pair("key", 123)));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    AttributionSrcBrowserTest,
+    AttributionSrcImg_TriggerRegisteredWithAggregatableTrigger) {
+  GURL page_url =
+      https_server()->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server()->GetURL(
+      "c.test", "/register_aggregatable_trigger_data_headers.html");
+
+  EXPECT_TRUE(ExecJs(web_contents(),
+                     JsReplace("createAttributionSrcImg($1);", register_url)));
+  if (!data_host)
+    loop.Run();
+  data_host->WaitForTriggerData(/*num_trigger_data=*/1);
+  const auto& trigger_data = data_host->trigger_data();
+
+  EXPECT_EQ(trigger_data.size(), 1u);
+  EXPECT_EQ(trigger_data.front()->reporting_origin,
+            url::Origin::Create(register_url));
+  EXPECT_THAT(trigger_data.front()->event_triggers, IsEmpty());
+
+  EXPECT_THAT(
+      trigger_data.front()->aggregatable_trigger->trigger_data,
+      ElementsAre(
+          Pointee(AllOf(
+              AggregatableKeyIs(Pointee(AllOf(AggregatableKeyHighBitsIs(0),
+                                              AggregatableKeyLowBitsIs(1)))),
+              SourceKeysAre(ElementsAre("key1")),
+              FiltersAre(Pointee(
+                  FilterValuesAre(ElementsAre(Pair("a", ElementsAre("b")))))),
+              NotFiltersAre(Pointee(
+                  FilterValuesAre(ElementsAre(Pair("c", IsEmpty()))))))),
+          Pointee(AllOf(
+              AggregatableKeyIs(Pointee(AllOf(AggregatableKeyHighBitsIs(0),
+                                              AggregatableKeyLowBitsIs(0)))),
+              SourceKeysAre(IsEmpty()),
+              FiltersAre(Pointee(FilterValuesAre(IsEmpty()))),
+              NotFiltersAre(Pointee(
+                  FilterValuesAre(ElementsAre(Pair("d", ElementsAre("e", "f")),
+                                              Pair("g", IsEmpty())))))))));
+
+  EXPECT_THAT(trigger_data.front()->aggregatable_values->values,
+              ElementsAre(Pair("key1", 123), Pair("key2", 456)));
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
@@ -451,6 +569,66 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
+                       AttributionSrcImg_InvalidAggregatableTriggerDropped) {
+  // Create a separate server as we cannot register a `ControllableHttpResponse`
+  // after the server starts.
+  auto https_server = std::make_unique<net::EmbeddedTestServer>(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  https_server->ServeFilesFromSourceDirectory(
+      "content/test/data/attribution_reporting");
+  https_server->ServeFilesFromSourceDirectory("content/test/data");
+  SetupCrossSiteRedirector(https_server.get());
+
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server.get(), "/register_trigger");
+  ASSERT_TRUE(https_server->Start());
+
+  GURL page_url =
+      https_server->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server->GetURL("d.test", "/register_trigger");
+  EXPECT_TRUE(ExecJs(web_contents(),
+                     JsReplace("createAttributionSrcImg($1);", register_url)));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader(
+      "Attribution-Reporting-Register-Aggregatable-Trigger-Data", "");
+  http_response->AddCustomHeader(
+      "Attribution-Reporting-Register-Aggregatable-Values", "");
+  http_response->AddCustomHeader(
+      "Location", "/register_aggregatable_trigger_data_headers.html");
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  if (!data_host)
+    loop.Run();
+  data_host->WaitForTriggerData(/*num_trigger_data=*/1);
+  const auto& trigger_data = data_host->trigger_data();
+
+  // Only the second trigger is registered.
+  EXPECT_EQ(trigger_data.size(), 1u);
+  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->trigger_data,
+              SizeIs(2));
+  EXPECT_THAT(trigger_data.front()->aggregatable_values->values, SizeIs(2));
+}
+
+IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
                        AttributionSrcImgTriggerThenSource_SourceIgnored) {
   GURL page_url =
       https_server()->GetURL("b.test", "/page_with_impression_creator.html");
@@ -614,6 +792,63 @@
             url::Origin::Create(GURL("https://d.test")));
 }
 
+IN_PROC_BROWSER_TEST_P(AttributionSrcInvalidFiltersBrowserTest,
+                       AttributionSrcImgFiltersInvalid_TriggerDropped) {
+  // Create a separate server as we cannot register a `ControllableHttpResponse`
+  // after the server starts.
+  auto https_server = std::make_unique<net::EmbeddedTestServer>(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  https_server->ServeFilesFromSourceDirectory(
+      "content/test/data/attribution_reporting");
+  https_server->ServeFilesFromSourceDirectory("content/test/data");
+  SetupCrossSiteRedirector(https_server.get());
+
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server.get(), "/register_trigger");
+  ASSERT_TRUE(https_server->Start());
+
+  GURL page_url =
+      https_server->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server->GetURL("d.test", "/register_trigger");
+  EXPECT_TRUE(ExecJs(web_contents(),
+                     JsReplace("createAttributionSrcImg($1);", register_url)));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader(
+      "Attribution-Reporting-Register-Event-Trigger",
+      base::StrCat({R"([{"trigger_data":"9","filters":)", GetParam(), "}]"}));
+  http_response->AddCustomHeader("Location", "/register_trigger_headers.html");
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  if (!data_host)
+    loop.Run();
+  data_host->WaitForTriggerData(/*num_trigger_data=*/1);
+  const auto& trigger_data = data_host->trigger_data();
+
+  // Only the second trigger is registered.
+  EXPECT_EQ(trigger_data.size(), 1u);
+  EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u);
+  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     AttributionSrcInvalidFilters,
     AttributionSrcInvalidFiltersBrowserTest,
@@ -704,6 +939,85 @@
             url::Origin::Create(GURL("https://d.test")));
 }
 
+IN_PROC_BROWSER_TEST_P(AttributionSrcFilterSizeBrowserTest,
+                       AttributionSrcImgExcessiveFilterSize_TriggerDropped) {
+  const AttributionFilterSizeTestCase& test_case = GetParam();
+
+  // Create a separate server as we cannot register a `ControllableHttpResponse`
+  // after the server starts.
+  auto https_server = std::make_unique<net::EmbeddedTestServer>(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  https_server->ServeFilesFromSourceDirectory(
+      "content/test/data/attribution_reporting");
+  https_server->ServeFilesFromSourceDirectory("content/test/data");
+  SetupCrossSiteRedirector(https_server.get());
+
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server.get(), "/register_trigger");
+  ASSERT_TRUE(https_server->Start());
+
+  GURL page_url =
+      https_server->GetURL("b.test", "/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));
+
+  MockAttributionHost host(web_contents());
+  std::unique_ptr<MockDataHost> data_host;
+  base::RunLoop loop;
+  EXPECT_CALL(host, RegisterDataHost)
+      .WillOnce(
+          [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) {
+            data_host = GetRegisteredDataHost(std::move(host));
+            loop.Quit();
+          });
+
+  GURL register_url = https_server->GetURL("d.test", "/register_trigger");
+  EXPECT_TRUE(ExecJs(web_contents(),
+                     JsReplace("createAttributionSrcImg($1);", register_url)));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("trigger_data", "9");
+
+  base::Value filter_data(base::Value::Type::DICTIONARY);
+  for (auto [filter, values] : test_case.AsMap()) {
+    base::Value list(base::Value::Type::LIST);
+    for (auto value : values) {
+      list.Append(std::move(value));
+    }
+    filter_data.SetKey(std::move(filter), std::move(list));
+  }
+  dict.SetKey("filters", std::move(filter_data));
+
+  base::Value list(base::Value::Type::LIST);
+  list.Append(std::move(dict));
+
+  std::string json;
+  EXPECT_TRUE(base::JSONWriter::Write(list, &json));
+
+  http_response->AddCustomHeader("Attribution-Reporting-Register-Event-Trigger",
+                                 std::move(json));
+  http_response->AddCustomHeader("Location", "/register_trigger_headers.html");
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  if (!data_host)
+    loop.Run();
+
+  const size_t expected_triggers = test_case.valid ? 2 : 1;
+  data_host->WaitForTriggerData(/*num_trigger_data=*/expected_triggers);
+  const auto& trigger_data = data_host->trigger_data();
+
+  EXPECT_EQ(trigger_data.size(), expected_triggers);
+  EXPECT_EQ(trigger_data.back()->event_triggers.size(), 1u);
+  EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 10u);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     AttributionSrcFilterSizes,
     AttributionSrcFilterSizeBrowserTest,
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 0ff7c869..c940be9 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -669,18 +669,22 @@
   const CommonSourceInfo::SourceType source_type =
       source_to_attribute->source.common_info().source_type();
 
-  uint64_t trigger_data;
-  switch (source_type) {
-    case CommonSourceInfo::SourceType::kNavigation:
-      trigger_data = trigger.trigger_data();
-      break;
-    case CommonSourceInfo::SourceType::kEvent:
-      trigger_data = trigger.event_source_trigger_data();
-      break;
+  uint64_t trigger_data = 0;
+  int64_t priority = 0;
+  absl::optional<uint64_t> dedup_key;
+
+  auto event_trigger =
+      base::ranges::find(trigger.event_triggers(), source_type,
+                         &AttributionTrigger::EventTriggerData::source_type);
+
+  if (event_trigger != trigger.event_triggers().end()) {
+    // TODO(apaseltiner): Consider informing the manager if the trigger
+    // data was out of range for DevTools issue reporting.
+    trigger_data =
+        delegate_->SanitizeTriggerData(event_trigger->data, source_type);
+    priority = event_trigger->priority;
+    dedup_key = event_trigger->dedup_key;
   }
-  // TODO(apaseltiner): Consider informing the manager if the trigger
-  // data was out of range for DevTools issue reporting.
-  trigger_data = delegate_->SanitizeTriggerData(trigger_data, source_type);
 
   const base::Time report_time =
       delegate_->GetReportTime(source_to_attribute->source.common_info(),
@@ -704,11 +708,22 @@
                       /*time=*/current_time, trigger.debug_key()),
       /*report_time=*/report_time,
       /*external_report_id=*/delegate_->NewReportID(),
-      AttributionReport::EventLevelData(trigger_data, trigger.priority(),
+      AttributionReport::EventLevelData(trigger_data, priority,
                                         randomized_response_rate,
                                         /*id=*/absl::nullopt));
 
-  switch (ReportAlreadyStored(source_id_to_attribute, trigger.dedup_key())) {
+  // Note that this cannot currently occur outside of tests, because all
+  // triggers have two event triggers, one for each source type, one of which
+  // must match. In the future, when we have general filtering based on strings,
+  // this code path will be reachable and we return without performing
+  // attribution.
+  if (event_trigger == trigger.event_triggers().end()) {
+    return CreateReportResult(
+        AttributionTrigger::Result::kNoMatchingEventTriggers,
+        std::move(report));
+  }
+
+  switch (ReportAlreadyStored(source_id_to_attribute, dedup_key)) {
     case ReportAlreadyStoredStatus::kNotStored:
       break;
     case ReportAlreadyStoredStatus::kStored:
@@ -768,7 +783,7 @@
   absl::optional<AttributionReport> replaced_report;
   const auto maybe_replace_lower_priority_report_result =
       MaybeReplaceLowerPriorityEventLevelReport(
-          report, source_to_attribute->num_conversions, trigger.priority(),
+          report, source_to_attribute->num_conversions, priority,
           replaced_report);
   if (maybe_replace_lower_priority_report_result ==
       MaybeReplaceLowerPriorityEventLevelReportResult::kError) {
@@ -803,9 +818,8 @@
 
   if (create_report) {
     if (!StoreReport(attribution_info.source.source_id(), trigger_data,
-                     attribution_info.time, report.report_time(),
-                     trigger.priority(), report.external_report_id(),
-                     trigger.debug_key())) {
+                     attribution_info.time, report.report_time(), priority,
+                     report.external_report_id(), trigger.debug_key())) {
       return CreateReportResult(AttributionTrigger::Result::kInternalError,
                                 std::move(report));
     }
@@ -814,15 +828,14 @@
   // If a dedup key is present, store it. We do this regardless of whether
   // `create_report` is true to avoid leaking whether the report was actually
   // stored.
-  if (trigger.dedup_key().has_value()) {
+  if (dedup_key.has_value()) {
     static constexpr char kInsertDedupKeySql[] =
         "INSERT INTO dedup_keys(impression_id,dedup_key)VALUES(?,?)";
     sql::Statement insert_dedup_key_statement(
         db_->GetCachedStatement(SQL_FROM_HERE, kInsertDedupKeySql));
     insert_dedup_key_statement.BindInt64(0,
                                          *attribution_info.source.source_id());
-    insert_dedup_key_statement.BindInt64(1,
-                                         SerializeUint64(*trigger.dedup_key()));
+    insert_dedup_key_statement.BindInt64(1, SerializeUint64(*dedup_key));
     if (!insert_dedup_key_statement.Run()) {
       return CreateReportResult(AttributionTrigger::Result::kInternalError,
                                 std::move(report));
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index e0b6807..c926156d 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -15,10 +15,12 @@
 
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/check.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/guid.h"
 #include "base/memory/raw_ptr.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
@@ -88,12 +90,19 @@
   // the current timestamp.
   AttributionReport GetExpectedReport(const StoredSource& source,
                                       const AttributionTrigger& conversion) {
+    // TOO(apaseltiner): Replace this logic with explicit setting of expected
+    // values.
+    auto event_trigger = base::ranges::find(
+        conversion.event_triggers(), source.common_info().source_type(),
+        &AttributionTrigger::EventTriggerData::source_type);
+    CHECK(event_trigger != conversion.event_triggers().end());
+
     return ReportBuilder(AttributionInfoBuilder(source)
                              .SetTime(base::Time::Now())
                              .Build())
-        .SetTriggerData(conversion.trigger_data())
+        .SetTriggerData(event_trigger->data)
         .SetReportTime(source.common_info().impression_time() + kReportDelay)
-        .SetPriority(conversion.priority())
+        .SetPriority(event_trigger->priority)
         .Build();
   }
 
@@ -2180,4 +2189,35 @@
                           SourceFilterDataIs(*filter_data)));
 }
 
+TEST_F(AttributionStorageTest, NoMatchingTriggers) {
+  const auto origin = url::Origin::Create(GURL("https://r.test"));
+
+  storage()->StoreSource(
+      SourceBuilder()
+          .SetSourceType(CommonSourceInfo::SourceType::kNavigation)
+          .SetConversionOrigin(origin)
+          .SetReportingOrigin(origin)
+          .Build());
+
+  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingEventTriggers,
+            MaybeCreateAndStoreReport(AttributionTrigger(
+                net::SchemefulSite(origin), origin,
+                /*debug_key=*/absl::nullopt,
+                {AttributionTrigger::EventTriggerData(
+                    /*data=*/0,
+                    /*priority=*/0,
+                    /*dedup_key=*/absl::nullopt,
+                    CommonSourceInfo::SourceType::kEvent)})));
+
+  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
+            MaybeCreateAndStoreReport(AttributionTrigger(
+                net::SchemefulSite(origin), origin,
+                /*debug_key=*/absl::nullopt,
+                {AttributionTrigger::EventTriggerData(
+                    /*data=*/0,
+                    /*priority=*/0,
+                    /*dedup_key=*/absl::nullopt,
+                    CommonSourceInfo::SourceType::kNavigation)})));
+}
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 1a427d7..7e05c73d 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -650,11 +650,18 @@
   return sources_.Clone();
 }
 
+bool operator==(const AttributionTrigger::EventTriggerData& a,
+                const AttributionTrigger::EventTriggerData& b) {
+  const auto tie = [](const AttributionTrigger::EventTriggerData& t) {
+    return std::make_tuple(t.data, t.priority, t.dedup_key, t.source_type);
+  };
+  return tie(a) == tie(b);
+}
+
 bool operator==(const AttributionTrigger& a, const AttributionTrigger& b) {
   const auto tie = [](const AttributionTrigger& t) {
-    return std::make_tuple(t.trigger_data(), t.conversion_destination(),
-                           t.reporting_origin(), t.event_source_trigger_data(),
-                           t.priority(), t.dedup_key());
+    return std::make_tuple(t.conversion_destination(), t.reporting_origin(),
+                           t.debug_key(), t.event_triggers());
   };
   return tie(a) == tie(b);
 }
@@ -822,6 +829,9 @@
     case AttributionTrigger::Result::kExcessiveReportingOrigins:
       out << "excessiveReportingOrigins";
       break;
+    case AttributionTrigger::Result::kNoMatchingEventTriggers:
+      out << "noMatchingEventTriggers";
+      break;
   }
   return out;
 }
@@ -882,23 +892,33 @@
   return out;
 }
 
+std::ostream& operator<<(
+    std::ostream& out,
+    const AttributionTrigger::EventTriggerData& event_trigger) {
+  return out << "{data=" << event_trigger.data
+             << ",priority=" << event_trigger.priority << ",dedup_key="
+             << (event_trigger.dedup_key
+                     ? base::NumberToString(*event_trigger.dedup_key)
+                     : "null")
+             << ",source_type=" << event_trigger.source_type << "}";
+}
+
 std::ostream& operator<<(std::ostream& out,
                          const AttributionTrigger& conversion) {
-  return out << "{trigger_data=" << conversion.trigger_data()
-             << ",conversion_destination="
-             << conversion.conversion_destination().Serialize()
-             << ",reporting_origin=" << conversion.reporting_origin()
-             << ",event_source_trigger_data="
-             << conversion.event_source_trigger_data()
-             << ",priority=" << conversion.priority() << ",dedup_key="
-             << (conversion.dedup_key()
-                     ? base::NumberToString(*conversion.dedup_key())
-                     : "null")
-             << ",debug_key="
-             << (conversion.debug_key()
-                     ? base::NumberToString(*conversion.debug_key())
-                     : "null")
-             << "}";
+  out << "{conversion_destination="
+      << conversion.conversion_destination().Serialize()
+      << ",reporting_origin=" << conversion.reporting_origin() << ",debug_key="
+      << (conversion.debug_key() ? base::NumberToString(*conversion.debug_key())
+                                 : "null")
+      << "event_triggers=[";
+
+  const char* separator = "";
+  for (const auto& event_trigger : conversion.event_triggers()) {
+    out << separator << event_trigger;
+    separator = ", ";
+  }
+
+  return out << "]}";
 }
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 131223b6..eed06001 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -530,6 +530,9 @@
   blink::mojom::AttributionAggregatableSources sources_;
 };
 
+bool operator==(const AttributionTrigger::EventTriggerData& a,
+                const AttributionTrigger::EventTriggerData& b);
+
 bool operator==(const AttributionTrigger& a, const AttributionTrigger& b);
 
 bool operator==(const AttributionFilterData& a, const AttributionFilterData& b);
@@ -577,6 +580,10 @@
 std::ostream& operator<<(std::ostream& out,
                          CommonSourceInfo::SourceType source_type);
 
+std::ostream& operator<<(
+    std::ostream& out,
+    const AttributionTrigger::EventTriggerData& event_trigger);
+
 std::ostream& operator<<(std::ostream& out,
                          const AttributionTrigger& conversion);
 
diff --git a/content/browser/attribution_reporting/attribution_trigger.cc b/content/browser/attribution_reporting/attribution_trigger.cc
index 7417689b..f25ae979 100644
--- a/content/browser/attribution_reporting/attribution_trigger.cc
+++ b/content/browser/attribution_reporting/attribution_trigger.cc
@@ -10,6 +10,29 @@
 
 namespace content {
 
+AttributionTrigger::EventTriggerData::EventTriggerData(
+    uint64_t data,
+    int64_t priority,
+    absl::optional<uint64_t> dedup_key,
+    CommonSourceInfo::SourceType source_type)
+    : data(data),
+      priority(priority),
+      dedup_key(dedup_key),
+      source_type(source_type) {}
+
+AttributionTrigger::AttributionTrigger(
+    net::SchemefulSite conversion_destination,
+    url::Origin reporting_origin,
+    absl::optional<uint64_t> debug_key,
+    std::vector<EventTriggerData> event_triggers)
+    : conversion_destination_(std::move(conversion_destination)),
+      reporting_origin_(std::move(reporting_origin)),
+      debug_key_(debug_key),
+      event_triggers_(std::move(event_triggers)) {
+  DCHECK(!reporting_origin_.opaque());
+  DCHECK(!conversion_destination_.opaque());
+}
+
 AttributionTrigger::AttributionTrigger(
     uint64_t trigger_data,
     net::SchemefulSite conversion_destination,
@@ -18,16 +41,19 @@
     int64_t priority,
     absl::optional<uint64_t> dedup_key,
     absl::optional<uint64_t> debug_key)
-    : trigger_data_(trigger_data),
-      conversion_destination_(std::move(conversion_destination)),
-      reporting_origin_(std::move(reporting_origin)),
-      event_source_trigger_data_(event_source_trigger_data),
-      priority_(priority),
-      dedup_key_(dedup_key),
-      debug_key_(debug_key) {
-  DCHECK(!reporting_origin_.opaque());
-  DCHECK(!conversion_destination_.opaque());
-}
+    : AttributionTrigger(
+          std::move(conversion_destination),
+          std::move(reporting_origin),
+          debug_key,
+          std::vector<EventTriggerData>(
+              {EventTriggerData(trigger_data,
+                                priority,
+                                dedup_key,
+                                CommonSourceInfo::SourceType::kNavigation),
+               EventTriggerData(event_source_trigger_data,
+                                priority,
+                                dedup_key,
+                                CommonSourceInfo::SourceType::kEvent)})) {}
 
 AttributionTrigger::AttributionTrigger(const AttributionTrigger& other) =
     default;
diff --git a/content/browser/attribution_reporting/attribution_trigger.h b/content/browser/attribution_reporting/attribution_trigger.h
index 1535c4a1e..a75e9c1 100644
--- a/content/browser/attribution_reporting/attribution_trigger.h
+++ b/content/browser/attribution_reporting/attribution_trigger.h
@@ -7,6 +7,9 @@
 
 #include <stdint.h>
 
+#include <vector>
+
+#include "content/browser/attribution_reporting/common_source_info.h"
 #include "content/common/content_export.h"
 #include "net/base/schemeful_site.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -35,14 +38,52 @@
     kPriorityTooLow = 7,
     kDroppedForNoise = 8,
     kExcessiveReportingOrigins = 9,
-    kMaxValue = kExcessiveReportingOrigins,
+    kNoMatchingEventTriggers = 10,
+    kMaxValue = kNoMatchingEventTriggers,
   };
 
+  struct CONTENT_EXPORT EventTriggerData {
+    // Data associated with trigger.
+    // Will be sanitized to a lower entropy by the `AttributionStorageDelegate`
+    // before storage.
+    uint64_t data;
+
+    // Priority specified in conversion redirect. Used to prioritize which
+    // reports to send among multiple different reports for the same attribution
+    // source. Defaults to 0 if not provided.
+    int64_t priority;
+
+    // Key specified in conversion redirect for deduplication against existing
+    // conversions with the same source. If absent, no deduplication is
+    // performed.
+    absl::optional<uint64_t> dedup_key;
+
+    // The source type that this trigger data will match.
+    // TODO(apaseltiner): Replace this with the generalized filtering mechanism.
+    CommonSourceInfo::SourceType source_type;
+
+    EventTriggerData(uint64_t data,
+                     int64_t priority,
+                     absl::optional<uint64_t> dedup_key,
+                     CommonSourceInfo::SourceType source_type);
+  };
+
+  // Should only be created with values that the browser process has already
+  // validated. |conversion_destination| should be filled by a navigation origin
+  // known by the browser process.
+  AttributionTrigger(net::SchemefulSite conversion_destination,
+                     url::Origin reporting_origin,
+                     absl::optional<uint64_t> debug_key,
+                     std::vector<EventTriggerData> event_triggers);
+
   // Should only be created with values that the browser process has already
   // validated. |trigger_data| and |event_source_trigger_data| will be sanitized
   // to a lower entropy by the `AttributionStorageDelegate` before storage.
   // |conversion_destination| should be filled by a navigation origin known by
   // the browser process.
+  //
+  // TODO(apaseltiner): Remove this constructor once the old
+  // trigger-registration API surface is removed.
   AttributionTrigger(uint64_t trigger_data,
                      net::SchemefulSite conversion_destination,
                      url::Origin reporting_origin,
@@ -50,36 +91,28 @@
                      int64_t priority,
                      absl::optional<uint64_t> dedup_key,
                      absl::optional<uint64_t> debug_key);
+
   AttributionTrigger(const AttributionTrigger& other);
   AttributionTrigger& operator=(const AttributionTrigger& other);
   AttributionTrigger(AttributionTrigger&& other);
   AttributionTrigger& operator=(AttributionTrigger&& other);
   ~AttributionTrigger();
 
-  uint64_t trigger_data() const { return trigger_data_; }
-
   const net::SchemefulSite& conversion_destination() const {
     return conversion_destination_;
   }
 
   const url::Origin& reporting_origin() const { return reporting_origin_; }
 
-  uint64_t event_source_trigger_data() const {
-    return event_source_trigger_data_;
-  }
-
-  int64_t priority() const { return priority_; }
-
-  const absl::optional<uint64_t>& dedup_key() const { return dedup_key_; }
-
-  const absl::optional<uint64_t>& debug_key() const { return debug_key_; }
+  absl::optional<uint64_t> debug_key() const { return debug_key_; }
 
   void ClearDebugKey() { debug_key_ = absl::nullopt; }
 
- private:
-  // Data associated with trigger.
-  uint64_t trigger_data_;
+  const std::vector<EventTriggerData>& event_triggers() const {
+    return event_triggers_;
+  }
 
+ private:
   // Schemeful site that this conversion event occurred on.
   net::SchemefulSite conversion_destination_;
 
@@ -87,20 +120,9 @@
   // reports.
   url::Origin reporting_origin_;
 
-  // Event source trigger data specified in conversion redirect. Defaults to 0
-  // if not provided.
-  uint64_t event_source_trigger_data_;
-
-  // Priority specified in conversion redirect. Used to prioritize which reports
-  // to send among multiple different reports for the same attribution source.
-  // Defaults to 0 if not provided.
-  int64_t priority_;
-
-  // Key specified in conversion redirect for deduplication against existing
-  // conversions with the same source. If absent, no deduplication is performed.
-  absl::optional<uint64_t> dedup_key_;
-
   absl::optional<uint64_t> debug_key_;
+
+  std::vector<EventTriggerData> event_triggers_;
 };
 
 }  // namespace content
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index cb9d45e..c63a0345 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -150,7 +150,6 @@
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_response.mojom.h"
 #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom.h"
 #include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom.h"
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom.h"
diff --git a/content/browser/compositor/image_transport_factory_browsertest.cc b/content/browser/compositor/image_transport_factory_browsertest.cc
index 4b72a455..71843f4 100644
--- a/content/browser/compositor/image_transport_factory_browsertest.cc
+++ b/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -9,6 +9,7 @@
 #include "build/chromeos_buildflags.h"
 #include "components/viz/common/gpu/context_lost_observer.h"
 #include "components/viz/common/gpu/context_provider.h"
+#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_browser_test.h"
@@ -63,6 +64,12 @@
   run_loop.Run();
 
   context_provider->RemoveObserver(&observer);
+
+  // Close the channel to the GPU process. This is needed because the GPU
+  // channel is down by the time that the network service is flushed, but
+  // flushing the network service tries to bring it back up again and there are
+  // pending requests causing a DCHECK to hit.
+  BrowserGpuChannelHostFactory::instance()->CloseChannel();
 }
 
 }  // anonymous namespace
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index bdcf8e2..46dd9ec 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -192,6 +192,11 @@
         kErrorFetchingClientMetadataInvalidResponse: {
       return FederatedAuthRequestIssueReasonEnum::ClientMetadataInvalidResponse;
     }
+    case FederatedAuthRequestResult::
+        kErrorClientMetadataMissingPrivacyPolicyUrl: {
+      return FederatedAuthRequestIssueReasonEnum::
+          ClientMetadataMissingPrivacyPolicyUrl;
+    }
     case FederatedAuthRequestResult::kErrorFetchingSignin: {
       return FederatedAuthRequestIssueReasonEnum::ErrorFetchingSignin;
     }
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h
index cfca6f4..04106e1 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.h
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -16,6 +16,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "content/common/content_export.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "ipc/message_filter.h"
 
@@ -29,7 +30,9 @@
  public:
   static void Initialize(bool establish_gpu_channel);
   static void Terminate();
-  static BrowserGpuChannelHostFactory* instance() { return instance_; }
+  CONTENT_EXPORT static BrowserGpuChannelHostFactory* instance() {
+    return instance_;
+  }
 
   BrowserGpuChannelHostFactory(const BrowserGpuChannelHostFactory&) = delete;
   BrowserGpuChannelHostFactory& operator=(const BrowserGpuChannelHostFactory&) =
@@ -45,7 +48,7 @@
 
   // Closes the channel to the GPU process. This should be called before the IO
   // thread stops.
-  void CloseChannel();
+  CONTENT_EXPORT void CloseChannel();
 
   // Notify the BrowserGpuChannelHostFactory of visibility, used to prevent
   // timeouts while backgrounded.
@@ -84,7 +87,9 @@
 
   base::OneShotTimer timeout_;
 
-  static BrowserGpuChannelHostFactory* instance_;
+  // instance() might be inlined at a call site so instance_ must also be
+  // exported.
+  CONTENT_EXPORT static BrowserGpuChannelHostFactory* instance_;
 };
 
 }  // namespace content
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index ba07d9ae..d24e7a2 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -25,7 +25,6 @@
 #include "build/build_config.h"
 #include "content/browser/browser_url_handler_impl.h"
 #include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/network_service_instance_impl.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/content_navigation_policy.h"
@@ -4521,14 +4520,6 @@
     EXPECT_EQ(target_frame->GetSiteInstance(),
               initiator_frame->GetSiteInstance());
 
-    // Start monitoring NetworkService for crashes.
-    //
-    // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
-    // (with optional opt-out for things like NetworkServiceRestartBrowserTest).
-    bool did_network_service_crash = false;
-    base::CallbackListSubscription crash_monitoring_subscription =
-        RegisterNetworkServiceCrashHandler(base::BindLambdaForTesting(
-            [&]() { did_network_service_crash = true; }));
     // Ask for cookies in the `target_frame`.  One implicit verification here
     // is whether this step will hit any `cookie_url`-related NOTREACHED or DwoC
     // in RestrictedCookieManager::ValidateAccessToCookiesAt.  This verification
@@ -4537,21 +4528,6 @@
     // ignores possible Blink-side caching, but this is the first time the
     // renderer needs the cookies and so this is okay for this test).
     EXPECT_EQ("", EvalJs(target_frame, "document.cookie"));
-    // |network_context| might receive an error notification, but it's not
-    // guaranteed to have arrived at this point. Flush the remote to make sure
-    // the notification has been received.
-    //
-    // We flush via `initiator_frame`, because in the current set of tests, the
-    // `initiator_frame` always has a mojo connection to the NetworkService via
-    // the `network_service_disconnect_handler_holder_mojo` field of
-    // RenderFrameHostImpl.  (This is not true for the `target_frame` in tests
-    // where that frame uses the process-wide URLLoaderFactory fallback rather
-    // than creating a URLLoaderFactory via RenderFrameHostImpl.)
-    //
-    // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
-    if (!IsInProcessNetworkService())
-      initiator_frame->FlushNetworkAndNavigationInterfacesForTesting();
-    EXPECT_FALSE(did_network_service_crash);
 
     // Verify that the "about:blank" frame is able to load an image.
     VerifyImageSubresourceLoads(target_frame);
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 6517a2a..d36f59d 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/json/values_util.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -467,19 +468,15 @@
   if (IsInProcessNetworkService())
     return;
 
-  mojo::PendingRemote<network::mojom::NetworkServiceTest>
-      pending_network_service_test;
-  GetNetworkService()->BindTestInterface(
-      pending_network_service_test.InitWithNewPipeAndPassReceiver());
-
   net::EmbeddedTestServer http_server;
   http_server.AddDefaultHandlers(GetTestDataFilePath());
   http_server.RegisterRequestMonitor(base::BindLambdaForTesting(
       [&](const net::test_server::HttpRequest& request) {
         if (request.relative_url == "/hung") {
-          mojo::Remote<network::mojom::NetworkServiceTest> network_service_test(
-              std::move(pending_network_service_test));
-          network_service_test->SimulateCrash();
+          base::PostTask(
+              FROM_HERE, {BrowserThread::UI},
+              base::BindOnce(&BrowserTestBase::SimulateNetworkServiceCrash,
+                             base::Unretained(this)));
         }
       }));
   EXPECT_TRUE(http_server.Start());
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 065dae6..8ff5cd9 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -1114,6 +1114,7 @@
       network_service_test.BindNewPipeAndPassReceiver());
 
   // Crash the network service, but do not wait for full startup.
+  IgnoreNetworkServiceCrashes();
   network_service_test.set_disconnect_handler(run_loop.QuitClosure());
   network_service_test->SimulateCrash();
   run_loop.Run();
diff --git a/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc b/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
index 8eb2316..fe12ec9 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
@@ -110,7 +110,7 @@
             base::BindLambdaForTesting(
                 [&run_loop, feature](
                     blink::scheduler::WebSchedulerTrackedFeatures features) {
-                  if (features.Has(feature))
+                  if (features.Has(feature) && run_loop.running())
                     run_loop.Quit();
                 }));
     EXPECT_TRUE(NavigateToURL(shell(), url));
@@ -120,6 +120,10 @@
                   current_frame_host()->GetBackForwardCacheDisablingFeatures(),
                   kFeaturesToIgnore),
               blink::scheduler::WebSchedulerTrackedFeatures(feature));
+
+    // Close the web contents to ensure that no new notifications arrive to the
+    // function local callback above after this function has returned.
+    web_contents()->Close();
   }
 
   std::vector<int64_t> navigation_ids_;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 98a7cca..8e71ca6 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1000,6 +1000,19 @@
   return params.url;
 }
 
+bool IsAvoidUnnecessaryBeforeUnloadCheckPostTaskEnabled() {
+  return base::FeatureList::IsEnabled(
+      features::kAvoidUnnecessaryBeforeUnloadCheckPostTask);
+}
+
+bool IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled() {
+  // Only one of sync or posttask should be used. If both are set, use posttask
+  // as the sync variant is not safe for android webview.
+  return base::FeatureList::IsEnabled(
+             features::kAvoidUnnecessaryBeforeUnloadCheckSync) &&
+         !IsAvoidUnnecessaryBeforeUnloadCheckPostTaskEnabled();
+}
+
 }  // namespace
 
 class RenderFrameHostImpl::SubresourceLoaderFactoriesConfig {
@@ -7669,9 +7682,13 @@
 
     // Only run beforeunload in frames that have registered a beforeunload
     // handler. See description of SendBeforeUnload() for details on simulating
-    // beforeunload for legacy reasons.
+    // beforeunload for legacy reasons. If
+    // `kAvoidUnnecessaryBeforeUnloadCheckSync` is true and there is no
+    // beforeunload handler for the navigating frame, then do not simulate a
+    // beforeunload handler, and navigation can continue.
     const bool run_beforeunload_for_legacy_frame =
-        rfh == this && !rfh->has_before_unload_handler_;
+        rfh == this && !rfh->has_before_unload_handler_ &&
+        !IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled();
     const bool should_run_beforeunload =
         rfh->has_before_unload_handler_ || run_beforeunload_for_legacy_frame;
 
@@ -7714,11 +7731,11 @@
       continue;
 
     if (run_beforeunload_for_legacy_frame &&
-        base::FeatureList::IsEnabled(
-            features::kAvoidUnnecessaryBeforeUnloadCheck)) {
+        IsAvoidUnnecessaryBeforeUnloadCheckPostTaskEnabled()) {
       // Wait to schedule until all frames have been processed. The legacy
       // beforeunload is not needed if another frame has a beforeunload
-      // handler.
+      // handler. Note that for `kAvoidUnnecessaryBeforeUnloadCheckSync`
+      // `run_beforeunload_for_legacy_frame` is never true.
       run_beforeunload_for_legacy = true;
       continue;
     }
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 5e648b2..df1add18 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -131,7 +131,6 @@
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom-forward.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-forward.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_response.mojom-forward.h"
 #include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom-forward.h"
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom-forward.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -1759,9 +1758,6 @@
   void BindFederatedAuthRequestReceiver(
       mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver);
 
-  void BindFederatedAuthResponseReceiver(
-      mojo::PendingReceiver<blink::mojom::FederatedAuthResponse> receiver);
-
   void BindRestrictedCookieManager(
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver);
   void BindRestrictedCookieManagerWithOrigin(
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index ec76d8a..6d52ff4 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -6561,7 +6561,7 @@
  public:
   RenderFrameHostImplAvoidUnnecessaryBeforeUnloadBrowserTest() {
     scoped_feature_list_.InitAndEnableFeature(
-        features::kAvoidUnnecessaryBeforeUnloadCheck);
+        features::kAvoidUnnecessaryBeforeUnloadCheckPostTask);
   }
 
  private:
@@ -6570,7 +6570,7 @@
 
 // Ensure that navigating with a frame tree of A(B(A)) results in the right
 // number of beforeunload messages sent when the feature
-// `kAvoidUnnecessaryBeforeUnloadCheck` is set.
+// `kAvoidUnnecessaryBeforeUnloadCheckPostTask` is set.
 IN_PROC_BROWSER_TEST_F(
     RenderFrameHostImplAvoidUnnecessaryBeforeUnloadBrowserTest,
     RendererInitiatedNavigationInABA) {
diff --git a/content/browser/renderer_host/render_frame_host_impl_unittest.cc b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
index ab903f79..d0aed22 100644
--- a/content/browser/renderer_host/render_frame_host_impl_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
@@ -441,7 +441,7 @@
 TEST_F(RenderFrameHostImplTest, BeforeUnloadNotSentToRenderer) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
-      features::kAvoidUnnecessaryBeforeUnloadCheck);
+      features::kAvoidUnnecessaryBeforeUnloadCheckPostTask);
   FakeLocalFrameWithBeforeUnload local_frame(contents()->GetMainFrame());
   auto simulator = NavigationSimulatorImpl::CreateBrowserInitiated(
       GURL("https://example.com/simple.html"), contents());
@@ -466,7 +466,7 @@
 TEST_F(RenderFrameHostImplTest, BeforeUnloadSentToRenderer) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(
-      features::kAvoidUnnecessaryBeforeUnloadCheck);
+      features::kAvoidUnnecessaryBeforeUnloadCheckPostTask);
   FakeLocalFrameWithBeforeUnload local_frame(contents()->GetMainFrame());
   auto simulator = NavigationSimulatorImpl::CreateBrowserInitiated(
       GURL("https://example.com/simple.html"), contents());
@@ -691,6 +691,28 @@
                 url::Origin::Create(no_host_permissions_url), nullptr));
 }
 
+TEST_F(RenderFrameHostImplTest, NoBeforeUnloadCheckForBrowserInitiated) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kAvoidUnnecessaryBeforeUnloadCheckSync);
+  contents()->GetController().LoadURLWithParams(
+      NavigationController::LoadURLParams(
+          GURL("https://example.com/navigation.html")));
+  EXPECT_FALSE(
+      contents()->GetMainFrame()->is_waiting_for_beforeunload_completion());
+}
+
+TEST_F(RenderFrameHostImplTest, BeforeUnloadCheckForBrowserInitiated) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      features::kAvoidUnnecessaryBeforeUnloadCheckSync);
+  contents()->GetController().LoadURLWithParams(
+      NavigationController::LoadURLParams(
+          GURL("https://example.com/navigation.html")));
+  EXPECT_TRUE(
+      contents()->GetMainFrame()->is_waiting_for_beforeunload_completion());
+}
+
 class RenderFrameHostImplThirdPartyStorageTest
     : public RenderViewHostImplTestHarness,
       public testing::WithParamInterface<bool> {
diff --git a/content/browser/resources/attribution_reporting/attribution_internals.js b/content/browser/resources/attribution_reporting/attribution_internals.js
index ba51d78..ca2509163 100644
--- a/content/browser/resources/attribution_reporting/attribution_internals.js
+++ b/content/browser/resources/attribution_reporting/attribution_internals.js
@@ -550,6 +550,9 @@
       case WebUIAttributionReport_Status.kNetworkError:
         this.status = 'Network error';
         break;
+      case WebUIAttributionReport_Status.kNoMatchingEventTriggers:
+        this.status = 'Dropped due to no matching event triggers';
+        break;
     }
   }
 }
diff --git a/content/browser/web_package/OWNERS b/content/browser/web_package/OWNERS
index a19f03d76..5b2d7b8 100644
--- a/content/browser/web_package/OWNERS
+++ b/content/browser/web_package/OWNERS
@@ -1,4 +1,3 @@
-kinuko@chromium.org
 kouhei@chromium.org
 hayato@chromium.org
 horo@chromium.org
diff --git a/content/browser/webid/README.md b/content/browser/webid/README.md
index 7465b63..2affc8a 100644
--- a/content/browser/webid/README.md
+++ b/content/browser/webid/README.md
@@ -111,6 +111,3 @@
    request. It contains most of the business logic and state necessary for FedCM requests.
 - `IdPNetworkRequestManager`: Handles all fetches needed for FedCM. It ensures we use the right
   storage partition and cookie jar for each request. This class is stateless itself.
-- `FederatedAuthResponseImpl`: Concrete implementation of the mojo interface to provide a FedCM
-   response. This is to be used by the IDP login page to complete its process by providing
-   an id token.
diff --git a/content/browser/webid/fedcm_metrics.h b/content/browser/webid/fedcm_metrics.h
index e391dcab..e8feb85c 100644
--- a/content/browser/webid/fedcm_metrics.h
+++ b/content/browser/webid/fedcm_metrics.h
@@ -36,8 +36,9 @@
   kIdTokenNoResponse,
   kIdTokenInvalidResponse,
   kIdTokenInvalidRequest,
+  kClientMetadataMissingPrivacyPolicyUrl,
 
-  kMaxValue = kIdTokenInvalidRequest
+  kMaxValue = kClientMetadataMissingPrivacyPolicyUrl
 };
 
 // This enum describes the status of a revocation call to the FedCM API.
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 68ec2ed..8a069492 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -104,6 +104,11 @@
         kErrorFetchingClientMetadataInvalidResponse: {
       return "Provider's client metadata is invalid.";
     }
+    case FederatedAuthRequestResult::
+        kErrorClientMetadataMissingPrivacyPolicyUrl: {
+      return "Provider's client metadata is missing or has an invalid privacy "
+             "policy url.";
+    }
     case FederatedAuthRequestResult::kErrorFetchingSignin: {
       return "Error attempting to reach the provider's sign-in endpoint.";
     }
@@ -175,6 +180,8 @@
     case FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound:
     case FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse:
     case FederatedAuthRequestResult::
+        kErrorClientMetadataMissingPrivacyPolicyUrl:
+    case FederatedAuthRequestResult::
         kErrorFetchingClientMetadataInvalidResponse:
     case FederatedAuthRequestResult::kErrorFetchingAccountsHttpNotFound:
     case FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse:
@@ -611,6 +618,19 @@
       return;
     }
     case IdpNetworkRequestManager::FetchStatus::kSuccess: {
+      // Since the |privacy_policy_url| is required, consider the result an
+      // invalid response in the case where the parser returns an empty value
+      // for it or an invalid url.
+      GURL pp_url(data.privacy_policy_url);
+      if (!pp_url.is_valid()) {
+        RecordRequestIdTokenStatus(
+            IdTokenStatus::kClientMetadataMissingPrivacyPolicyUrl,
+            render_frame_host_->GetPageUkmSourceId());
+        CompleteRequest(FederatedAuthRequestResult::
+                            kErrorClientMetadataMissingPrivacyPolicyUrl,
+                        "", /*should_call_callback=*/false);
+        return;
+      }
       client_metadata_ = data;
 
       network_manager_->SendAccountsRequest(
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 042358f..923c996 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -161,6 +161,9 @@
 static const MockClientIdConfiguration kClientMetadataInvalidResponse{
     FetchStatus::kInvalidResponseError, "", ""};
 
+static const MockClientIdConfiguration kClientMetadataNoPrivacyPolicyUrl{
+    FetchStatus::kSuccess, "", ""};
+
 static const AuthRequestTestCase kMediatedTestCases[]{
     {"Error parsing FedCM manifest for Mediated mode missing token endpoint",
      {kIdpTestOrigin, kClientId, kNonce},
@@ -251,6 +254,15 @@
      {kToken, FetchStatus::kSuccess, kClientMetadataInvalidResponse,
       kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
       kMediatedNoop}},
+
+    {"Client metadata has no privacy policy url",
+     {kIdpTestOrigin, kClientId, kNonce},
+     {RequestIdTokenStatus::kError,
+      FederatedAuthRequestResult::kErrorClientMetadataMissingPrivacyPolicyUrl,
+      kEmptyToken},
+     {kToken, FetchStatus::kSuccess, kClientMetadataNoPrivacyPolicyUrl,
+      kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+      kMediatedNoop}},
 };
 
 // Helper class for receiving the mojo method callback.
@@ -750,7 +762,11 @@
            "metadata."},
           {FederatedAuthRequestResult::
                kErrorFetchingClientMetadataInvalidResponse,
-           "Provider's client metadata is invalid."}};
+           "Provider's client metadata is invalid."},
+          {FederatedAuthRequestResult::
+               kErrorClientMetadataMissingPrivacyPolicyUrl,
+           "Provider's client metadata is missing or has an invalid privacy "
+           "policy url."}};
   std::vector<std::string> messages =
       RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
   absl::optional<std::string> expected_message =
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index c6b7acc7..5d484d57 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -68,7 +68,7 @@
     WebRuntimeFeatures::EnableWebBluetooth(true);
 #endif
 
-#if BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
   const bool enable_canvas_2d_image_chromium =
       command_line.HasSwitch(
           blink::switches::kEnableGpuMemoryBufferCompositorResources) &&
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 0f01907..155136a 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -69,10 +69,25 @@
 #endif
 };
 
-// When enabled, the browser process will only ask the renderer process to run
-// beforeunload handlers if it knows such handlers are registered.
-const base::Feature kAvoidUnnecessaryBeforeUnloadCheck{
+// The following two features, when enabled, result in the browser process only
+// asking the renderer process to run beforeunload handlers if it knows such
+// handlers are registered. The two slightly differ in what they do and how
+// they behave:
+// . `kAvoidUnnecessaryBeforeUnloadCheckPostTask` in this case content continues
+//   to report a beforeunload handler is present (even though it isn't). When
+//   asked to dispatch the beforeunload handler, a post task is used (rather
+//   than going to the renderer).
+// . `kAvoidUnnecessaryBeforeUnloadCheckSync` in this case content does not
+//   report a beforeunload handler is present. A ramification of this is
+//   navigations that would normally check beforeunload handlers before
+//   continuing will not, and navigation will synchronously continue.
+// Only one should be used (if both are set, the first takes precedence). The
+// second is unsafe for Android webview, as the embedder may trigger
+// reentrancy which can not be changed.
+const base::Feature kAvoidUnnecessaryBeforeUnloadCheckPostTask{
     "AvoidUnnecessaryBeforeUnloadCheck", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAvoidUnnecessaryBeforeUnloadCheckSync{
+    "AvoidUnnecessaryBeforeUnloadCheckSync", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Kill switch for Background Fetch.
 const base::Feature kBackgroundFetch{"BackgroundFetch",
@@ -191,7 +206,7 @@
 // enabled.
 const base::Feature kCanvas2DImageChromium {
   "Canvas2DImageChromium",
-#if BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 830835d..e0b72af4 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -28,7 +28,10 @@
 CONTENT_EXPORT extern const base::Feature kAudioServiceLaunchOnStartup;
 CONTENT_EXPORT extern const base::Feature kAudioServiceOutOfProcess;
 CONTENT_EXPORT extern const base::Feature kAudioServiceSandbox;
-CONTENT_EXPORT extern const base::Feature kAvoidUnnecessaryBeforeUnloadCheck;
+CONTENT_EXPORT extern const base::Feature
+    kAvoidUnnecessaryBeforeUnloadCheckPostTask;
+CONTENT_EXPORT extern const base::Feature
+    kAvoidUnnecessaryBeforeUnloadCheckSync;
 CONTENT_EXPORT extern const base::Feature kBackgroundFetch;
 CONTENT_EXPORT extern const base::Feature kBackForwardCache;
 CONTENT_EXPORT extern const base::Feature kBackForwardCacheSameSiteForBots;
diff --git a/content/public/common/user_agent.h b/content/public/common/user_agent.h
index deff015..b57eb5e 100644
--- a/content/public/common/user_agent.h
+++ b/content/public/common/user_agent.h
@@ -23,7 +23,7 @@
     "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 %s"
     "Safari/537.36";
 const char kUnifiedPlatformAndroid[] = "Linux; Android 10; K";
-const char kUnifiedPlatformCrOS[] = "X11; CrOS x86_64";
+const char kUnifiedPlatformCrOS[] = "X11; CrOS x86_64 14541.0.0";
 const char kUnifiedPlatformLinux[] = "X11; Linux x86_64";
 const char kUnifiedPlatformMacOS[] = "Macintosh; Intel Mac OS X 10_15_7";
 const char kUnifiedPlatformWindows[] = "Windows NT 10.0; Win64; x64";
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 5e9059a6..9bc49c1 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -739,6 +739,14 @@
 void BrowserTestBase::SimulateNetworkServiceCrash() {
   CHECK(!IsInProcessNetworkService())
       << "Can't crash the network service if it's running in-process!";
+
+  // Check if any unexpected crashes have occurred *before* the expected crash
+  // that we will trigger/simulate below.
+  AssertThatNetworkServiceDidNotCrash();
+
+  // `network_service_test_` field might not be ready yet - some tests call
+  // SimulateNetworkServiceCrash from SetUpOnMainThread, before
+  // InitializeNetworkProcess has been called.
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
@@ -758,6 +766,10 @@
   InitializeNetworkProcess();
 }
 
+void BrowserTestBase::IgnoreNetworkServiceCrashes() {
+  network_service_test_.reset();
+}
+
 #if BUILDFLAG(IS_ANDROID)
 void BrowserTestBase::WaitUntilJavaIsReady(
     base::OnceClosure quit_closure,
@@ -880,6 +892,7 @@
       RunTestOnMainThread();
     }
     TearDownOnMainThread();
+    AssertThatNetworkServiceDidNotCrash();
   }
 
   PostRunTestOnMainThread();
@@ -956,6 +969,26 @@
   initial_web_contents_ = web_contents;
 }
 
+void BrowserTestBase::AssertThatNetworkServiceDidNotCrash() {
+  if (!IsOutOfProcessNetworkService()) {
+    return;
+  }
+
+  // TODO(https://crbug.com/1169431#c2): Enable NetworkService crash detection
+  // on Fuchsia.
+#if !BUILDFLAG(IS_FUCHSIA)
+  if (network_service_test_.is_bound()) {
+    // If there was a crash, then |network_service_test_| will receive an error
+    // notification, but it's not guaranteed to have arrived at this point.
+    // Flush the remote to make sure the notification has been received.
+    network_service_test_.FlushForTesting();
+
+    EXPECT_TRUE(network_service_test_.is_connected())
+        << "Expecting no NetworkService crashes";
+  }
+#endif
+}
+
 void BrowserTestBase::InitializeNetworkProcess() {
   if (initialized_network_process_)
     return;
@@ -980,26 +1013,26 @@
     return;
   }
 
-  mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
+  network_service_test_.reset();
   content::GetNetworkService()->BindTestInterface(
-      network_service_test.BindNewPipeAndPassReceiver());
+      network_service_test_.BindNewPipeAndPassReceiver());
 
   // Do not set up host resolver rules if we allow the test to access
   // the network.
   if (allow_network_access_to_host_resolutions_) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test->SetAllowNetworkAccessToHostResolutions();
+    network_service_test_->SetAllowNetworkAccessToHostResolutions();
     return;
   }
 
   if (replace_system_dns_config_) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test->ReplaceSystemDnsConfig();
+    network_service_test_->ReplaceSystemDnsConfig();
   }
 
   if (test_doh_config_.has_value()) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test->SetTestDohConfig(*test_doh_config_);
+    network_service_test_->SetTestDohConfig(*test_doh_config_);
   }
 
   std::vector<network::mojom::RulePtr> mojo_rules;
@@ -1066,7 +1099,7 @@
   // to dispatch a Java callback that makes network process to enter native
   // code.
   base::RunLoop loop{base::RunLoop::Type::kNestableTasksAllowed};
-  network_service_test->AddRules(std::move(mojo_rules), loop.QuitClosure());
+  network_service_test_->AddRules(std::move(mojo_rules), loop.QuitClosure());
   loop.Run();
 }
 
diff --git a/content/public/test/browser_test_base.h b/content/public/test/browser_test_base.h
index 27b0d47..36b9b67 100644
--- a/content/public/test/browser_test_base.h
+++ b/content/public/test/browser_test_base.h
@@ -16,8 +16,10 @@
 #include "build/chromeos_buildflags.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_host_resolver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/dns/public/dns_over_https_config.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/public/mojom/network_service_test.mojom.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -75,6 +77,14 @@
   // racily start before the rules have been re-applied.
   void SimulateNetworkServiceCrash();
 
+  // Ignores all future NetworkService crashes that would be otherwise detected
+  // and flagged by the AssertThatNetworkServiceDidNotCrash method.
+  //
+  // The IgnoreNetworkServiceCrashes method is useful in a test that plans to
+  // trigger crashes. Note that calling IgnoreNetworkServiceCrashes is *not*
+  // needed when triggering the crash via SimulateNetworkServiceCrash method.
+  void IgnoreNetworkServiceCrashes();
+
   // Returns the host resolver being used for the tests. Subclasses might want
   // to configure it inside tests.
   net::RuleBasedHostResolverProc* host_resolver() {
@@ -98,6 +108,10 @@
   // PreEarlyInitialization() has been called.
   virtual void CreatedBrowserMainParts(BrowserMainParts* browser_main_parts) {}
 
+  // GTest assertions that the connection to `network_service_test_` did not get
+  // dropped unexpectedly.
+  void AssertThatNetworkServiceDidNotCrash();
+
   // Sets flag to allow host resolutions to reach the network. Must be called
   // before Setup() to take effect.
   void SetAllowNetworkAccessToHostResolutions();
@@ -243,6 +257,8 @@
 
   std::unique_ptr<NoRendererCrashesAssertion> no_renderer_crashes_assertion_;
 
+  mojo::Remote<network::mojom::NetworkServiceTest> network_service_test_;
+
   bool initialized_network_process_ = false;
 
   bool allow_network_access_to_host_resolutions_ = false;
diff --git a/content/test/attribution_simulator_impl.cc b/content/test/attribution_simulator_impl.cc
index f5beb73..72432d95 100644
--- a/content/test/attribution_simulator_impl.cc
+++ b/content/test/attribution_simulator_impl.cc
@@ -237,6 +237,7 @@
       case AttributionTrigger::Result::kPriorityTooLow:
       case AttributionTrigger::Result::kDroppedForNoise:
       case AttributionTrigger::Result::kExcessiveReportingOrigins:
+      case AttributionTrigger::Result::kNoMatchingEventTriggers:
         reason << result.status();
         break;
     }
diff --git a/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html b/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html
new file mode 100644
index 0000000..75677c4
--- /dev/null
+++ b/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html
@@ -0,0 +1 @@
+Registers a trigger with aggregatable trigger data and values.
diff --git a/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html.mock-http-headers b/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html.mock-http-headers
new file mode 100644
index 0000000..90309f2
--- /dev/null
+++ b/content/test/data/attribution_reporting/register_aggregatable_trigger_data_headers.html.mock-http-headers
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Attribution-Reporting-Register-Aggregatable-Trigger-Data:[{"key_piece":"0x1","source_keys":["key1"],"filters":{"a":["b"]},"not_filters":{"c":[]}},{"key_piece":"0x0","source_keys":[],"not_filters":{"d":["e","f"],"g":[]}}]
+Attribution-Reporting-Register-Aggregatable-Values:{"key1": 123, "key2": 456}
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers
index d4aea3e1..2149ca03 100644
--- a/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers
+++ b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers
@@ -1,2 +1,5 @@
 HTTP/1.1 200 OK
-Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "1","priority":"5","deduplication_key":"1024"},{"trigger_data":"2","priority":"10"}]
\ No newline at end of file
+Attribution-Reporting-Filters:{"w":[],"x":["y","z"]}
+Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "1","priority":"5","deduplication_key":"1024","filters":{"a":["b"]},"not_filters":{"c":[]}},{"trigger_data":"2","priority":"10","not_filters":{"d":["e","f"],"g":[]}}]
+Attribution-Reporting-Register-Aggregatable-Trigger-Data:[{"key_piece":"0x1","source_keys":["key"]}]
+Attribution-Reporting-Register-Aggregatable-Values:{"key": 123}
diff --git a/content/test/fuzzer/renderer_tree_fuzzer.cc b/content/test/fuzzer/renderer_tree_fuzzer.cc
index 1cf19fd1..b85501110 100644
--- a/content/test/fuzzer/renderer_tree_fuzzer.cc
+++ b/content/test/fuzzer/renderer_tree_fuzzer.cc
@@ -153,12 +153,11 @@
   AttrPosition PickRandomAttribute(Random* rnd);
 
   void ParseJson(const base::Value& value) {
-    const base::ListValue* list;
-    if (!value.GetAsList(&list)) {
+    if (!value.is_list()) {
       return;
     }
 
-    for (const auto& listItem : list->GetListDeprecated()) {
+    for (const auto& listItem : value.GetList()) {
       std::unique_ptr<Node> node(Node::ParseJson(listItem));
       if (node) {
         push_back(std::move(node));
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index c2ecf78..73ee1a90 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -235,6 +235,7 @@
 crbug.com/883500 [ android android-chromium android-nexus-5x ] Pixel_BackgroundImage [ RetryOnFailure ]
 crbug.com/1065514 [ android android-chromium android-nexus-5x ] Pixel_WebGLReadPixelsTabSwitch [ RetryOnFailure ]
 crbug.com/1278885 [ android android-chromium android-nexus-5x skia-renderer-gl ] Pixel_OffscreenCanvasTransferToImageBitmap [ RetryOnFailure ]
+crbug.com/1302406 [ android android-chromium android-nexus-5x ] Pixel_Video_MP4_Rounded_Corner [ RetryOnFailure ]
 
 # Mark all webview tests as RetryOnFailure due to Nexus 5x driver bug.
 crbug.com/950932 [ android-webview-instrumentation android-nexus-5x ] * [ RetryOnFailure ]
diff --git a/docs/infra/new_builder.md b/docs/infra/new_builder.md
index 1dae4c6d..1074a99d 100644
--- a/docs/infra/new_builder.md
+++ b/docs/infra/new_builder.md
@@ -8,15 +8,14 @@
 ## TL;DR
 
 For a typical chromium builder using the chromium recipe, you'll need to file a
-bug for tracking purposes, acquire a host, and then land **three** CLs:
+bug for tracking purposes, acquire a host, and then land **two** CLs:
 
 1. in [infradata/config][16], modifying `chromium.star`.
-2. in [chromium/tools/build][17], modifying the chromium\_tests\_builder\_config
-   configuration.
-3. in [chromium/src][18], modifying all of the following:
-    1. LUCI service configurations in `//infra/config`
-    2. Compile configuration in `//tools/mb`
-    3. Test configuration in `//testing/buildbot`
+1. in [chromium/src][18], modifying all of the following:
+    1. LUCI service configurations and recipe configurations in
+        `//infra/config`
+    1. Compile configuration in `//tools/mb`
+    1. Test configuration in `//testing/buildbot`
 
 ## Background
 
@@ -101,119 +100,19 @@
 See [infradata docs][4] (internal) for information on how to register
 the hardware to be used by your builder.
 
-## Recipe configuration
-
-Recipes tell your builder what to do. Many require some degree of
-per-builder configuration outside of the chromium repo, though the
-specifics vary. The recipe you use depends on what you want your
-builder to do.
-
-Most builders that compile and/or test code will use one of the chromium family
-of recipes: chromium for CI builders or chromium_trybot or chromium/orchestrator
-for try builders. These recipes require per-builder configuration defined for
-the [chromium\_tests\_builder\_config][5] recipe module.
-
-### Module properties
-
-The [chromium\_tests\_builder\_config][5] module now supports module properties
-that can be used to specify the per-builder config as part of the builder's
-properties. There is starlark code that handles setting the properties correctly
-for capturing parent-child and mirroring relationships. Having the config
-specified at the builder definition simplifies adding and maintaining builders
-and removes the need to make a change to [chromium/tools/build][17].
-
-The module properties can be used if the following conditions are true:
-
-* Findit support is not required for the builder (irrelevant for try builders)
-  (support for this is intended to be enabled Q4 2022)
-* Module properties must be used for all related builders (triggered/triggering
-  builders and mirrored/mirroring builders)
-
-> There are a handful of features that are not yet supported by the starlark:
-> builders with execution mode PROVIDE_TEST_SPEC and the various non-mirrors
-> fields of TrySpec that change the try builder's behavior. If these are
-> necessary for your use-case, contact gbeaty@.
-
-If you will be using module properties, skip ahead to [Chromium
-configuration](#chromium-configuration). The details of defining the per-builder
-config will be covered there.
-
-### Recipe-based config
-
-To configure a chromium CI builder, you'll want to add a config block to the
-file in [recipe\_modules/chromium\_tests\_builder\_config][5] corresponding to
-your new builder's builder group. The format is somewhat in flux and is not very
-consistent among the different builder groups, but something like this should
-suffice:
-
-``` py
-'your-new-builder': builder_spec.BuilderSpec.create(
-  chromium_config='chromium',
-  gclient_config='chromium',
-  chromium_apply_config=['mb', 'ninja_confirm_noop'],
-  chromium_config_kwargs={
-    'BUILD_CONFIG': 'Release', # or 'Debug', as appropriate
-    'TARGET_BITS': 64, # or 32, for some mobile builders
-  },
-  simulation_platform='$PLATFORM',  # one of 'mac', 'win', or 'linux'
-
-  # There are a variety of other options; most of them are unnecessary in most
-  # cases. If you think one may be applicable, please reach out or ask your
-  # reviewer.
-)
-```
-
-For chromium try builders, you'll also want to set up mirroring.
-You can do so by adding your new try builder to [trybots.py][21].
-
-A typical entry will just reference the matching CI builder, e.g.:
-
-``` py
-TRYBOTS = try_spec.TryDatabase.create({
-  # ...
-
-  'tryserver.chromium.example': {
-      # If you want to build and test the same targets as one
-      # CI builder, you can just do this:
-      'your-new-builder': try_spec.TrySpec.create_for_single_mirror(
-          builder_group='chromium.example',
-          buildername='your-new-builder',
-      ),
-
-      # If you want to build the same targets as one CI builder
-      # but not test anything, you can do this:
-      'your-new-compile-builder': try_spec.TrySpec.create_for_single_mirror(
-          builder_group='chromium.example',
-          buildername='your-new-builder',
-          analyze_mode='compile',
-      ),
-
-      # If you want to build and test the same targets as a builder/tester
-      # CI pair, you can do this:
-      'your-new-tester': try_spec.TrySpec.create_for_single_mirror(
-          builder_group='chromium.example',
-          buildername='your-new-builder',
-          tester='your-new-tester',
-      ),
-
-      # If you want to mirror multiple try bots, please reach out.
-    },
-
-  # ...
-})
-```
-
 ## Chromium configuration
 
 Lastly, you need to configure a variety of things in the chromium repo.
 It's generally ok to land all of them in a single CL.
 
-### LUCI services
+### Starlark
 
 LUCI services used by chromium are configured in [//infra/config][6].
 
 The configuration is written in Starlark and used to generate Protobuf files
-which are also checked in to the repo.
+which are also checked in to the repo. In addition to the LUCI services
+configuration files, the starlark also generates per-builder files that are used
+by the builder's executable.
 
 Generating all of the LUCI services configuration files for the production
 builders is done by executing [main.star][22] or running
@@ -408,6 +307,7 @@
 files have default values set for all builders in each particular file.
 
 ###### Regular (non-Orchestrator) CQ builders
+
 ```starlark
 try_.builder(
     name = '$BUILDER_NAME',
@@ -416,6 +316,7 @@
 ```
 
 ###### Orchestrator CQ Builders
+
 The Orchestrator pattern is an optimization from the old chromium_trybot CQ
 builders, where compiles are triggered to run on separate beefier machines.
 It consists of the chromium/orchestrator.py and chromium/compilator.py recipes.
@@ -427,6 +328,7 @@
 orchestrator name + "-compilator", like linux-rel and linux-rel-compilator.
 
 In //infra/config/subprojects/chromium/try/tryserver.chromium.linux.star:
+
 ```starlark
 try_.orchestrator_builder(
     name = "linux-rel",
@@ -447,6 +349,7 @@
 
 In infradata/config/configs/chromium-swarm/bots/chromium/chromium.star:
 (In the [infradata/config](https://chrome-internal.googlesource.com/infradata/config/) repo)
+
 ```starlark
 try_bots({
     "linux-rel": chrome.gce_bionic(
@@ -557,10 +460,27 @@
 
 ##### Builder configuration
 
-If you've decided to use module properties to configure the
-[chromium\_tests\_builder\_config][5] module as described in (Module
-Properties)[#module-properties], then that will be specified as part of the
-builder definition in the starlark files.
+The [chromium\_tests\_builder\_config][5] module now supports module properties
+that can be used to specify the per-builder config as part of the builder's
+properties. There is starlark code that handles setting the properties correctly
+for capturing parent-child and mirroring relationships. Having the config
+specified at the builder definition simplifies adding and maintaining builders
+and removes the need to make a change to [chromium/tools/build][17]. Module
+properties must be used for all related builders (triggered/triggering builders
+and mirrored/mirroring builders).
+
+There are some configuration options present in the recipe configs that are not
+and will not be supported in the module properties: the PROVIDE_TEST_SPEC
+execution mode and mirrroring non-existent try-builders. The capabilities that
+these features provide can be achieved using supported mechanisms. Builders with
+the PROVIDE_TEST_SPEC execution mode could only appear as mirrors in a try spec
+and allowed for running the same test against multiple hardware configurations.
+This can be accomplished using variant test suites in the test specs. Within the
+recipe, a builder spec can be defined for a non-existent builder and that can
+appear as a mirror. Instead, the try builder can define its own builder spec.
+
+For the old way of defining the builder config in the recipe see the section
+titled "Recipe-based config".
 
 ###### CI builders
 
@@ -587,10 +507,11 @@
 )
 ```
 
-If the CI builder only runs tests and is triggered by another builder, they
-should set `execution_mode` to `builder_config.execution_mode.TEST` and specify
-the triggering builder as `parent`. It is an error to set `triggered_by` in the
-builder definition if `parent` is set in `builder_spec`.
+If the CI builder only runs tests and is triggered by another builder, it should
+set `execution_mode` to `builder_config.execution_mode.TEST` and specify the
+triggering builder in the `triggered_by` field. The triggered_by field must be
+set and it must contain exactly 1 element that is a reference to a builder that
+also defines a `builder_spec`.
 
 ```starlark
 ci.linux_builder(
@@ -598,7 +519,6 @@
     bootstrap = True,
     builder_spec = builder_config.builder_spec(
         execution_mode = builder_config.TEST,
-        parent = 'ci/$PARENT_BUILDER_NAME',
         chromium_config = builder_config.execution_mode.chromium_config(
             config = "chromium",
             apply_configs = ["mb"],
@@ -609,6 +529,7 @@
             config = "chromium",
         ),
     ),
+    triggered_by = ['ci/$PARENT_BUILDER_NAME'],
     ...
 )
 ```
@@ -653,6 +574,82 @@
 limited capacity, it would be overwhelmed with additional builds happening on
 the branch.
 
+## Recipe-based config
+
+If for some reason you can't use the chromium\_tests\_builder\_config module
+properties for defining your new builder, then you'll have to modify the recipe
+itself. Such cases should be rare, please contact gbeaty@ if you think you have
+such a use case.
+
+Modifying the recipe involves making a change in the [chromium/tools/build][17]
+repo. If the builder being added is a tester that will be triggered by an
+existing builder, the change to the recipe should be made after defining the
+builder in chromium/src. Otherwise, the change to the recipe should be made
+before defining the builder in chromium/src.
+
+To configure a chromium CI builder, you'll want to add a config block to the
+file in [recipe\_modules/chromium\_tests\_builder\_config][5] corresponding to
+your new builder's builder group. The format is somewhat in flux and is not very
+consistent among the different builder groups, but something like this should
+suffice:
+
+``` py
+'your-new-builder': builder_spec.BuilderSpec.create(
+  chromium_config='chromium',
+  gclient_config='chromium',
+  chromium_apply_config=['mb', 'ninja_confirm_noop'],
+  chromium_config_kwargs={
+    'BUILD_CONFIG': 'Release', # or 'Debug', as appropriate
+    'TARGET_BITS': 64, # or 32, for some mobile builders
+  },
+  simulation_platform='$PLATFORM',  # one of 'mac', 'win', or 'linux'
+
+  # There are a variety of other options; most of them are unnecessary in most
+  # cases. If you think one may be applicable, please reach out or ask your
+  # reviewer.
+)
+```
+
+For chromium try builders, you'll also want to set up mirroring.
+You can do so by adding your new try builder to [trybots.py][21].
+
+A typical entry will just reference the matching CI builder, e.g.:
+
+``` py
+TRYBOTS = try_spec.TryDatabase.create({
+  # ...
+
+  'tryserver.chromium.example': {
+      # If you want to build and test the same targets as one
+      # CI builder, you can just do this:
+      'your-new-builder': try_spec.TrySpec.create_for_single_mirror(
+          builder_group='chromium.example',
+          buildername='your-new-builder',
+      ),
+
+      # If you want to build the same targets as one CI builder
+      # but not test anything, you can do this:
+      'your-new-compile-builder': try_spec.TrySpec.create_for_single_mirror(
+          builder_group='chromium.example',
+          buildername='your-new-builder',
+          analyze_mode='compile',
+      ),
+
+      # If you want to build and test the same targets as a builder/tester
+      # CI pair, you can do this:
+      'your-new-tester': try_spec.TrySpec.create_for_single_mirror(
+          builder_group='chromium.example',
+          buildername='your-new-builder',
+          tester='your-new-tester',
+      ),
+
+      # If you want to mirror multiple try bots, please reach out.
+    },
+
+  # ...
+})
+```
+
 ## Questions? Feedback?
 
 If you're in need of further assistance, if you're not sure about
diff --git a/infra/config/generated/cq-usage/default.cfg b/infra/config/generated/cq-usage/default.cfg
index 4095ce47..236eb67 100644
--- a/infra/config/generated/cq-usage/default.cfg
+++ b/infra/config/generated/cq-usage/default.cfg
@@ -119,34 +119,3 @@
     }
   }
 }
-config_groups {
-  name: "fallback-cq"
-  gerrit {
-    url: "https://chromium-review.googlesource.com"
-    projects {
-      name: "chromium/src"
-      ref_regexp: "refs/branch-heads/.*"
-      ref_regexp_exclude: "refs/branch-heads/4515"
-      ref_regexp_exclude: "refs/branch-heads/4664"
-      ref_regexp_exclude: "refs/branch-heads/4692"
-      ref_regexp_exclude: "refs/branch-heads/4758"
-      ref_regexp_exclude: "refs/branch-heads/4844"
-      ref_regexp_exclude: "refs/branch-heads/4896"
-    }
-  }
-  verifiers {
-    tryjob {
-      builders {
-        name: "chromium/try/requires-testing-checker"
-        disable_reuse: true
-      }
-      retry_config {
-        single_quota: 1
-        global_quota: 2
-        failure_weight: 1
-        transient_failure_weight: 1
-        timeout_weight: 2
-      }
-    }
-  }
-}
diff --git a/infra/config/generated/cq-usage/full.cfg b/infra/config/generated/cq-usage/full.cfg
index 41127de..21bb826 100644
--- a/infra/config/generated/cq-usage/full.cfg
+++ b/infra/config/generated/cq-usage/full.cfg
@@ -587,34 +587,3 @@
     }
   }
 }
-config_groups {
-  name: "fallback-cq"
-  gerrit {
-    url: "https://chromium-review.googlesource.com"
-    projects {
-      name: "chromium/src"
-      ref_regexp: "refs/branch-heads/.*"
-      ref_regexp_exclude: "refs/branch-heads/4515"
-      ref_regexp_exclude: "refs/branch-heads/4664"
-      ref_regexp_exclude: "refs/branch-heads/4692"
-      ref_regexp_exclude: "refs/branch-heads/4758"
-      ref_regexp_exclude: "refs/branch-heads/4844"
-      ref_regexp_exclude: "refs/branch-heads/4896"
-    }
-  }
-  verifiers {
-    tryjob {
-      builders {
-        name: "chromium/try/requires-testing-checker"
-        disable_reuse: true
-      }
-      retry_config {
-        single_quota: 1
-        global_quota: 2
-        failure_weight: 1
-        transient_failure_weight: 1
-        timeout_weight: 2
-      }
-    }
-  }
-}
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 132a0eed..62ac9b4 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -2194,7 +2194,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -2276,7 +2276,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -15683,7 +15683,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -15765,7 +15765,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -15847,7 +15847,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -15929,7 +15929,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16011,7 +16011,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16093,7 +16093,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16257,7 +16257,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16339,7 +16339,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16421,7 +16421,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16503,7 +16503,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16585,7 +16585,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16667,7 +16667,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16749,7 +16749,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16831,7 +16831,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -16913,7 +16913,7 @@
       dimensions: "builder:ToTLinuxASanLibfuzzer"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
@@ -16994,7 +16994,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -17064,7 +17064,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -17146,7 +17146,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -17228,7 +17228,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -17310,7 +17310,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -35892,7 +35892,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -39779,11 +39779,12 @@
     builders {
       name: "win-archive-dbg (reclient shadow)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:win-archive-dbg (reclient shadow)"
+      dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -39938,11 +39939,12 @@
     builders {
       name: "win-archive-rel (reclient shadow)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:win-archive-rel (reclient shadow)"
+      dimensions: "builderless:1"
       dimensions: "cores:32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
diff --git a/infra/config/generators/cq-usage.star b/infra/config/generators/cq-usage.star
index 29be5d6..81d2885 100644
--- a/infra/config/generators/cq-usage.star
+++ b/infra/config/generators/cq-usage.star
@@ -6,11 +6,12 @@
 This generator copies commit-queue.cfg, removing builders that are experimental
 or includable_only and removing other fields that don't impact when the CQ is
 triggered or what the CQ triggers. The resultant file is output as
-cq-usage/details.cfg. This enables applying limited owners for changes that
+cq-usage/default.cfg. This enables applying limited owners for changes that
 would impact the CQ for all users.
 """
 
 load("@stdlib//internal/luci/proto.star", "cq_pb")
+load("//subprojects/chromium/fallback-cq.star", "fallback_cq")
 
 def _remove_none(l):
     return [e for e in l if e != None]
@@ -58,10 +59,18 @@
 def _generate_cq_usage(ctx):
     cfg = ctx.output["luci/commit-queue.cfg"]
     ctx.output["cq-usage/default.cfg"] = cq_pb.Config(config_groups = _remove_none(
-        [_trim_config_group(g, include_path_based = False) for g in cfg.config_groups],
+        [
+            _trim_config_group(g, include_path_based = False)
+            for g in cfg.config_groups
+            if g.name != fallback_cq.GROUP
+        ],
     ))
     ctx.output["cq-usage/full.cfg"] = cq_pb.Config(config_groups = _remove_none(
-        [_trim_config_group(g, include_path_based = True) for g in cfg.config_groups],
+        [
+            _trim_config_group(g, include_path_based = True)
+            for g in cfg.config_groups
+            if g.name != fallback_cq.GROUP
+        ],
     ))
 
 lucicfg.generator(_generate_cq_usage)
diff --git a/infra/config/subprojects/chromium/ci/chromium.clang.star b/infra/config/subprojects/chromium/ci/chromium.clang.star
index a1821a7..85f9d3a 100644
--- a/infra/config/subprojects/chromium/ci/chromium.clang.star
+++ b/infra/config/subprojects/chromium/ci/chromium.clang.star
@@ -17,7 +17,7 @@
     # Naturally the runtime will be ~4-8h on average, depending on config.
     # CFI builds will take even longer - around 11h.
     execution_timeout = 14 * time.hour,
-    os = os.LINUX_DEFAULT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
     pool = ci.DEFAULT_POOL,
     properties = {
         "perf_dashboard_machine_group": "ChromiumClang",
@@ -169,7 +169,6 @@
         category = "ToT Code Coverage",
         short_name = "and",
     ),
-    os = os.LINUX_BIONIC_REMOVE,
 )
 
 ci.builder(
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 6c4e4624..48198400 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -745,6 +745,7 @@
 
 ci.builder(
     name = "win-archive-dbg (reclient shadow)",
+    builderless = True,
     console_view_entry = consoles.console_view_entry(
         category = "win|dbg",
         short_name = "64",
@@ -758,6 +759,7 @@
 
 ci.builder(
     name = "win-archive-rel (reclient shadow)",
+    builderless = True,
     console_view_entry = consoles.console_view_entry(
         category = "win|rel",
         short_name = "64",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index eabbef7e..09b4173 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -532,6 +532,11 @@
     cores = 32,
     executable = "recipe:chromium_upload_clang",
     goma_backend = None,
+    # This builder produces the clang binaries used on all builders. Since it
+    # uses the system's sysroot when compiling, the builder needs to run on the
+    # OS version that's the oldest used on any bot.
+    # TODO(crbug.com/1199405): Move this to bionic once _all_ builders have
+    # migrated.
     os = os.LINUX_TRUSTY,
 )
 
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 21fe643..aa283d4 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -594,9 +594,7 @@
           retry_out_dir = os.path.join(
               self.out_dir, 'retry_after_crash_%d' % int(time.time()))
           result = self._run(
-              self.get_launch_command(
-                  test_app, os.path.join(retry_out_dir, str(int(time.time()))),
-                  destination))
+              self.get_launch_command(test_app, retry_out_dir, destination))
           result.report_to_result_sink()
           # Only keep the last crash status in crash retries in overall crash
           # status.
@@ -619,8 +617,8 @@
           for test in tests_to_retry:
             LOGGER.info('Retry #%s for %s.\n', i + 1, test)
             test_app.included_tests = [test]
-            retry_out_dir = os.path.join(self.out_dir, test + '_failed',
-                                         'retry_%d' % i)
+            test_retry_sub_dir = '%s_retry_%d' % (test.replace('/', '_'), i)
+            retry_out_dir = os.path.join(self.out_dir, test_retry_sub_dir)
             retry_result = self._run(
                 self.get_launch_command(test_app, retry_out_dir, destination))
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 6d88c1d..403b55b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -544,7 +544,7 @@
         This allows you to easily create calendar events using Google Lens.
       </message>
       <message name="IDS_IOS_CAMERA_USAGE_DESCRIPTION" desc="Specifies the reason for accessing the user's camera while the app is in use [Length: unlimited] [iOS only].">
-        This lets you take and upload photos, as well as scan QR codes.
+        This lets you take and upload photos, as well as scan QR codes
       </message>
       <message name="IDS_IOS_CARD_STACK_SCROLLED_NOTIFICATION" desc="The accessibility description for page scrolled notification [iOS only]">
         <ph name="INCOGNITO">$1<ex>Incognito</ex></ph> Tabs <ph name="FIRST_VISIBLE_TAB">$2<ex>3</ex></ph> through <ph name="LAST_VISIBLE_TAB">$3<ex>5</ex></ph> of <ph name="NUMBER_OF_OPEN_TABS">$4<ex>12</ex></ph>
@@ -1331,7 +1331,7 @@
         Passwords
       </message>
       <message name="IDS_IOS_MICROPHONE_USAGE_DESCRIPTION" desc="Specifies the reason for accessing the user's microphone while the app is in use [Length: unlimited] [iOS only].">
-        This lets you search faster using your voice.
+        This lets you search using your voice
       </message>
       <message name="IDS_IOS_NAVIGATION_BAR_CANCEL_BUTTON" desc="Label of the cancel button displayed on the navigation bar  [Length: 7em] [iOS only].">
         Cancel
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CAMERA_USAGE_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CAMERA_USAGE_DESCRIPTION.png.sha1
index f60dbaf..f16e0bd 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CAMERA_USAGE_DESCRIPTION.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CAMERA_USAGE_DESCRIPTION.png.sha1
@@ -1 +1 @@
-dbaef238f389d466ee0ce5677a892be2368eaf42
\ No newline at end of file
+956f0170d81fe0bcb41fb6bc43e1c4237fcd1d41
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MICROPHONE_USAGE_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MICROPHONE_USAGE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..4e1092b7
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MICROPHONE_USAGE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8ccd26fd9b54d6a104e1aa8dc5e8ab889a5dc04f
\ No newline at end of file
diff --git a/ios/components/security_interstitials/OWNERS b/ios/components/security_interstitials/OWNERS
index 78f00191..63f820f 100644
--- a/ios/components/security_interstitials/OWNERS
+++ b/ios/components/security_interstitials/OWNERS
@@ -1,3 +1,3 @@
 ajuma@chromium.org
-livvielin@chromium.org
+cthomp@chromium.org
 meacer@chromium.org
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 3e3e705d..8e5f3cb 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-cd6542a66a3c307fa0a0a0aa88b49ddafb8ded7f
\ No newline at end of file
+b3e36dfdc633c84de811c8b52a80804434e9641c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index b80ef65..f0d6ad0 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-4770e31ad0de99d1e3b0497cb241bf2b1907a20a
\ No newline at end of file
+bd1ce18ddc986f634c9338b3df34d40d87851cc0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index df3720b..38c9f8d3 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e62f7a91d9f30ce6b5594ad39972f491eca2db07
\ No newline at end of file
+1250549cdfeb7fd0aed816e1cb01382edddc9e4c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 3164ede2..586d0a7 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-ecf6fdee6e94d0d89990adae9fe78de0810c21d3
\ No newline at end of file
+797c5aba63091142558028dda478bff58608db66
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index a6205c1..9cb4713 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-8fef0ae7223faaa4c555a5a9d03145efe9c7327a
\ No newline at end of file
+1b67ea5f3aaf48cb303bb701939f5ecd04dce9ec
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 61e4b53..0efc42d 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-29f97144df53280e5e00d0d99d16905e08a97efe
\ No newline at end of file
+0bdc212e782e0e576c8b824840e0c485d5946472
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index ec21ebe..be1235f8 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b751970fec98f5d271e3f3e6f221124bd28da073
\ No newline at end of file
+17007128dee5bb1b369c15688aa88fd2e8e19e50
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 1582d4e1..1199d78 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-192c0555a9b2ce728c06ac5902e8caa600f43c25
\ No newline at end of file
+2a6f3dfb6b807e008225e4e6d4d475857dad7584
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 212837d..ab5271d8 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4cb5eff38eb3373dcd8e53ded193e0078e9a4517
\ No newline at end of file
+d9e734feb04c8bc90acc3fef1cb87285639279b8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index c7020dc..6d6d55af 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9354349c0fcfb1a2f1b0eddb00cea390e910c65e
\ No newline at end of file
+07eb34ea17cbf553d71120eaece9b83f8017a5aa
\ No newline at end of file
diff --git a/media/gpu/v4l2/test/video_decoder.cc b/media/gpu/v4l2/test/video_decoder.cc
index 810821f..d53bac166 100644
--- a/media/gpu/v4l2/test/video_decoder.cc
+++ b/media/gpu/v4l2/test/video_decoder.cc
@@ -4,6 +4,8 @@
 
 #include "media/gpu/v4l2/test/video_decoder.h"
 
+#include "base/logging.h"
+
 namespace media {
 namespace v4l2_test {
 
@@ -18,5 +20,65 @@
 
 VideoDecoder::~VideoDecoder() = default;
 
+bool VideoDecoder::Initialize() {
+  // TODO(stevecho): remove VIDIOC_ENUM_FRAMESIZES ioctl call
+  //   after b/193237015 is resolved.
+  if (!v4l2_ioctl_->EnumFrameSizes(OUTPUT_queue_->fourcc()))
+    LOG(ERROR) << "EnumFrameSizes for OUTPUT queue failed.";
+
+  if (!v4l2_ioctl_->SetFmt(OUTPUT_queue_))
+    LOG(ERROR) << "SetFmt for OUTPUT queue failed.";
+
+  gfx::Size coded_size;
+  uint32_t num_planes;
+  if (!v4l2_ioctl_->GetFmt(CAPTURE_queue_->type(), &coded_size, &num_planes))
+    LOG(ERROR) << "GetFmt for CAPTURE queue failed.";
+
+  CAPTURE_queue_->set_coded_size(coded_size);
+  CAPTURE_queue_->set_num_planes(num_planes);
+
+  // VIDIOC_TRY_FMT() ioctl is equivalent to VIDIOC_S_FMT
+  // with one exception that it does not change driver state.
+  // VIDIOC_TRY_FMT may or may not be needed; it's used by the stateful
+  // Chromium V4L2VideoDecoder backend, see b/190733055#comment78.
+  // TODO(b/190733055): try and remove it after landing all the code.
+  if (!v4l2_ioctl_->TryFmt(CAPTURE_queue_))
+    LOG(ERROR) << "TryFmt for CAPTURE queue failed.";
+
+  if (!v4l2_ioctl_->SetFmt(CAPTURE_queue_))
+    LOG(ERROR) << "SetFmt for CAPTURE queue failed.";
+
+  if (!v4l2_ioctl_->ReqBufs(OUTPUT_queue_))
+    LOG(ERROR) << "ReqBufs for OUTPUT queue failed.";
+
+  if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(OUTPUT_queue_))
+    LOG(ERROR) << "QueryAndMmapQueueBuffers for OUTPUT queue failed";
+
+  if (!v4l2_ioctl_->ReqBufs(CAPTURE_queue_))
+    LOG(ERROR) << "ReqBufs for CAPTURE queue failed.";
+
+  if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(CAPTURE_queue_))
+    LOG(ERROR) << "QueryAndMmapQueueBuffers for CAPTURE queue failed.";
+
+  // Only 1 CAPTURE buffer is needed for 1st key frame decoding. Remaining
+  // CAPTURE buffers will be queued after that.
+  if (!v4l2_ioctl_->QBuf(CAPTURE_queue_, 0))
+    LOG(ERROR) << "VIDIOC_QBUF failed for CAPTURE queue.";
+
+  int media_request_fd;
+  if (!v4l2_ioctl_->MediaIocRequestAlloc(&media_request_fd))
+    LOG(ERROR) << "MEDIA_IOC_REQUEST_ALLOC failed";
+
+  OUTPUT_queue_->set_media_request_fd(media_request_fd);
+
+  if (!v4l2_ioctl_->StreamOn(OUTPUT_queue_->type()))
+    LOG(ERROR) << "StreamOn for OUTPUT queue failed.";
+
+  if (!v4l2_ioctl_->StreamOn(CAPTURE_queue_->type()))
+    LOG(ERROR) << "StreamOn for CAPTURE queue failed.";
+
+  return true;
+}
+
 }  // namespace v4l2_test
 }  // namespace media
diff --git a/media/gpu/v4l2/test/video_decoder.h b/media/gpu/v4l2/test/video_decoder.h
index 3679e2e..518f51cd 100644
--- a/media/gpu/v4l2/test/video_decoder.h
+++ b/media/gpu/v4l2/test/video_decoder.h
@@ -35,9 +35,12 @@
   VideoDecoder(const VideoDecoder&) = delete;
   VideoDecoder& operator=(const VideoDecoder&) = delete;
 
-  // TODO(stevecho): move Initialize() implementation to VideoDecoder class
-  // because this is expected to be the same among different codecs
-  virtual bool Initialize() = 0;
+  // TODO(stevecho): consider void return type instead
+  // This function only ever returns true, even if an error occurs.
+
+  // Initializes setup needed for decoding.
+  // https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/dev-stateless-decoder.html#initialization
+  bool Initialize();
 
   virtual Result DecodeNextFrame(std::vector<char>& y_plane,
                                  std::vector<char>& u_plane,
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc
index fb356fc..bffe223 100644
--- a/media/gpu/v4l2/test/vp9_decoder.cc
+++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -287,66 +287,6 @@
                      std::move(OUTPUT_queue), std::move(CAPTURE_queue)));
 }
 
-bool Vp9Decoder::Initialize() {
-  // TODO(stevecho): remove VIDIOC_ENUM_FRAMESIZES ioctl call
-  //   after b/193237015 is resolved.
-  if (!v4l2_ioctl_->EnumFrameSizes(OUTPUT_queue_->fourcc()))
-    LOG(ERROR) << "EnumFrameSizes for OUTPUT queue failed.";
-
-  if (!v4l2_ioctl_->SetFmt(OUTPUT_queue_))
-    LOG(ERROR) << "SetFmt for OUTPUT queue failed.";
-
-  gfx::Size coded_size;
-  uint32_t num_planes;
-  if (!v4l2_ioctl_->GetFmt(CAPTURE_queue_->type(), &coded_size, &num_planes))
-    LOG(ERROR) << "GetFmt for CAPTURE queue failed.";
-
-  CAPTURE_queue_->set_coded_size(coded_size);
-  CAPTURE_queue_->set_num_planes(num_planes);
-
-  // VIDIOC_TRY_FMT() ioctl is equivalent to VIDIOC_S_FMT
-  // with one exception that it does not change driver state.
-  // VIDIOC_TRY_FMT may or may not be needed; it's used by the stateful
-  // Chromium V4L2VideoDecoder backend, see b/190733055#comment78.
-  // TODO(b/190733055): try and remove it after landing all the code.
-  if (!v4l2_ioctl_->TryFmt(CAPTURE_queue_))
-    LOG(ERROR) << "TryFmt for CAPTURE queue failed.";
-
-  if (!v4l2_ioctl_->SetFmt(CAPTURE_queue_))
-    LOG(ERROR) << "SetFmt for CAPTURE queue failed.";
-
-  if (!v4l2_ioctl_->ReqBufs(OUTPUT_queue_))
-    LOG(ERROR) << "ReqBufs for OUTPUT queue failed.";
-
-  if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(OUTPUT_queue_))
-    LOG(ERROR) << "QueryAndMmapQueueBuffers for OUTPUT queue failed";
-
-  if (!v4l2_ioctl_->ReqBufs(CAPTURE_queue_))
-    LOG(ERROR) << "ReqBufs for CAPTURE queue failed.";
-
-  if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(CAPTURE_queue_))
-    LOG(ERROR) << "QueryAndMmapQueueBuffers for CAPTURE queue failed.";
-
-  // Only 1 CAPTURE buffer is needed for 1st key frame decoding. Remaining
-  // CAPTURE buffers will be queued after that.
-  if (!v4l2_ioctl_->QBuf(CAPTURE_queue_, 0))
-    LOG(ERROR) << "VIDIOC_QBUF failed for CAPTURE queue.";
-
-  int media_request_fd;
-  if (!v4l2_ioctl_->MediaIocRequestAlloc(&media_request_fd))
-    LOG(ERROR) << "MEDIA_IOC_REQUEST_ALLOC failed";
-
-  OUTPUT_queue_->set_media_request_fd(media_request_fd);
-
-  if (!v4l2_ioctl_->StreamOn(OUTPUT_queue_->type()))
-    LOG(ERROR) << "StreamOn for OUTPUT queue failed.";
-
-  if (!v4l2_ioctl_->StreamOn(CAPTURE_queue_->type()))
-    LOG(ERROR) << "StreamOn for CAPTURE queue failed.";
-
-  return true;
-}
-
 std::set<int> Vp9Decoder::RefreshReferenceSlots(
     uint8_t refresh_frame_flags,
     scoped_refptr<MmapedBuffer> buffer,
diff --git a/media/gpu/v4l2/test/vp9_decoder.h b/media/gpu/v4l2/test/vp9_decoder.h
index 63829ee..336e357 100644
--- a/media/gpu/v4l2/test/vp9_decoder.h
+++ b/media/gpu/v4l2/test/vp9_decoder.h
@@ -33,10 +33,6 @@
       std::unique_ptr<IvfParser> ivf_parser,
       const media::IvfFileHeader& file_header);
 
-  // Initializes setup needed for decoding.
-  // https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/dev-stateless-decoder.html#initialization
-  bool Initialize() override;
-
   // Parses next frame from IVF stream and decodes the frame. This method will
   // place the Y, U, and V values into the respective vectors and update the
   // size with the display area size of the decoded frame.
diff --git a/net/BUILD.gn b/net/BUILD.gn
index cccc3f6..fbc895c 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -3172,7 +3172,9 @@
     "data/verify_certificate_chain_unittest/expired-intermediate/not-before.test",
     "data/verify_certificate_chain_unittest/expired-root/chain.pem",
     "data/verify_certificate_chain_unittest/expired-root/not-after-ta-with-constraints.test",
+    "data/verify_certificate_chain_unittest/expired-root/not-after-ta-with-expiration.test",
     "data/verify_certificate_chain_unittest/expired-root/not-after.test",
+    "data/verify_certificate_chain_unittest/expired-root/not-before-ta-with-expiration.test",
     "data/verify_certificate_chain_unittest/expired-root/not-before.test",
     "data/verify_certificate_chain_unittest/expired-target/chain.pem",
     "data/verify_certificate_chain_unittest/expired-target/not-after.test",
@@ -3355,6 +3357,8 @@
     "data/verify_certificate_chain_unittest/target-and-intermediate/distrusted-root-expired.test",
     "data/verify_certificate_chain_unittest/target-and-intermediate/distrusted-root.test",
     "data/verify_certificate_chain_unittest/target-and-intermediate/main.test",
+    "data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-constraints.test",
+    "data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-expiration.test",
     "data/verify_certificate_chain_unittest/target-and-intermediate/unspecified-trust-root.test",
     "data/verify_certificate_chain_unittest/target-eku-clientauth/any.test",
     "data/verify_certificate_chain_unittest/target-eku-clientauth/chain.pem",
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 1b8078d..c499d76 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -185,7 +185,7 @@
       return "CertVerifyProcBuiltinChromeRoots";
   }
 
-  return nullptr;
+  return "";
 }
 
 scoped_refptr<CertVerifyProc> CreateCertVerifyProc(
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index d537fc7..a78f20b 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -130,6 +130,7 @@
   KeyIdentifierMatch key_id_match = CalculateKeyIdentifierMatch(target, issuer);
   switch (issuer_trust.type) {
     case CertificateTrustType::TRUSTED_ANCHOR:
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       switch (key_id_match) {
         case kMatch:
@@ -446,6 +447,7 @@
 
   switch (last_cert_trust.type) {
     case CertificateTrustType::TRUSTED_ANCHOR:
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       return certs.back().get();
     case CertificateTrustType::UNSPECIFIED:
@@ -585,6 +587,7 @@
     // (or to the same cert if it's self-signed).
     switch (next_issuer_.trust.type) {
       case CertificateTrustType::TRUSTED_ANCHOR:
+      case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
       case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
         if (cur_path_.Empty()) {
           DVLOG(1) << "Leaf is a trust anchor, considering as UNSPECIFIED";
@@ -603,6 +606,7 @@
       // path.
       case CertificateTrustType::DISTRUSTED:
       case CertificateTrustType::TRUSTED_ANCHOR:
+      case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
       case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS: {
         // If the issuer has a known trust level, can stop building the path.
         DVLOG(2) << "CertPathIter got anchor: "
diff --git a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
index 5d68baf..6710dd1d 100644
--- a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
@@ -27,6 +27,9 @@
       case CertificateTrustType::TRUSTED_ANCHOR:
         trust_store.AddTrustAnchor(test.chain.back());
         break;
+      case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+        trust_store.AddTrustAnchorWithExpiration(test.chain.back());
+        break;
       case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
         trust_store.AddTrustAnchorWithConstraints(test.chain.back());
         break;
diff --git a/net/cert/internal/test_helpers.cc b/net/cert/internal/test_helpers.cc
index 3a38073..502d708e 100644
--- a/net/cert/internal/test_helpers.cc
+++ b/net/cert/internal/test_helpers.cc
@@ -246,6 +246,9 @@
     } else if (GetValue("last_cert_trust: ", line_piece, &value, &has_trust)) {
       if (value == "TRUSTED_ANCHOR") {
         test->last_cert_trust = CertificateTrust::ForTrustAnchor();
+      } else if (value == "TRUSTED_ANCHOR_WITH_EXPIRATION") {
+        test->last_cert_trust =
+            CertificateTrust::ForTrustAnchorEnforcingExpiration();
       } else if (value == "TRUSTED_ANCHOR_WITH_CONSTRAINTS") {
         test->last_cert_trust =
             CertificateTrust::ForTrustAnchorEnforcingConstraints();
diff --git a/net/cert/internal/trust_store.cc b/net/cert/internal/trust_store.cc
index 20d3c8d3..5266521d 100644
--- a/net/cert/internal/trust_store.cc
+++ b/net/cert/internal/trust_store.cc
@@ -14,6 +14,12 @@
   return result;
 }
 
+CertificateTrust CertificateTrust::ForTrustAnchorEnforcingExpiration() {
+  CertificateTrust result;
+  result.type = CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION;
+  return result;
+}
+
 CertificateTrust CertificateTrust::ForTrustAnchorEnforcingConstraints() {
   CertificateTrust result;
   result.type = CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS;
@@ -38,6 +44,7 @@
     case CertificateTrustType::UNSPECIFIED:
       return false;
     case CertificateTrustType::TRUSTED_ANCHOR:
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       return true;
   }
@@ -52,6 +59,7 @@
       return true;
     case CertificateTrustType::UNSPECIFIED:
     case CertificateTrustType::TRUSTED_ANCHOR:
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       return false;
   }
@@ -66,6 +74,7 @@
       return true;
     case CertificateTrustType::DISTRUSTED:
     case CertificateTrustType::TRUSTED_ANCHOR:
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       return false;
   }
diff --git a/net/cert/internal/trust_store.h b/net/cert/internal/trust_store.h
index 3788e098..e84fc08 100644
--- a/net/cert/internal/trust_store.h
+++ b/net/cert/internal/trust_store.h
@@ -27,8 +27,13 @@
   // fields in the certificate that are meaningful are its name and SPKI.
   TRUSTED_ANCHOR,
 
-  // This certificate is a trust anchor, and additionally some of the fields in
-  // the certificate (other than name and SPKI) should be used during the
+  // This certificate is a trust anchor which additionally has expiration
+  // enforced. The only fields in the certificate that are meaningful are its
+  // name, SPKI, and validity period.
+  TRUSTED_ANCHOR_WITH_EXPIRATION,
+
+  // This certificate is a trust anchor for which some of the fields in the
+  // certificate (in addition to the name and SPKI) should be used during the
   // verification process. See VerifyCertificateChain() for details on how
   // constraints are applied.
   TRUSTED_ANCHOR_WITH_CONSTRAINTS,
@@ -42,6 +47,7 @@
 // TODO(eroman): Right now this is just a glorified wrapper around an enum...
 struct NET_EXPORT CertificateTrust {
   static CertificateTrust ForTrustAnchor();
+  static CertificateTrust ForTrustAnchorEnforcingExpiration();
   static CertificateTrust ForTrustAnchorEnforcingConstraints();
   static CertificateTrust ForUnspecified();
   static CertificateTrust ForDistrusted();
diff --git a/net/cert/internal/trust_store_in_memory.cc b/net/cert/internal/trust_store_in_memory.cc
index fec38891..3ae7baf4 100644
--- a/net/cert/internal/trust_store_in_memory.cc
+++ b/net/cert/internal/trust_store_in_memory.cc
@@ -21,6 +21,12 @@
   AddCertificate(std::move(cert), CertificateTrust::ForTrustAnchor());
 }
 
+void TrustStoreInMemory::AddTrustAnchorWithExpiration(
+    scoped_refptr<ParsedCertificate> cert) {
+  AddCertificate(std::move(cert),
+                 CertificateTrust::ForTrustAnchorEnforcingExpiration());
+}
+
 void TrustStoreInMemory::AddTrustAnchorWithConstraints(
     scoped_refptr<ParsedCertificate> cert) {
   AddCertificate(std::move(cert),
diff --git a/net/cert/internal/trust_store_in_memory.h b/net/cert/internal/trust_store_in_memory.h
index 9aa4330..989cb13 100644
--- a/net/cert/internal/trust_store_in_memory.h
+++ b/net/cert/internal/trust_store_in_memory.h
@@ -35,7 +35,11 @@
   // used during verification).
   void AddTrustAnchor(scoped_refptr<ParsedCertificate> cert);
 
-  // Adds a certificate as a trust achor and extracts anchor constraints from
+  // Adds a certificate as a trust anchor which will have expiration enforced.
+  // See VerifyCertificateChain for details.
+  void AddTrustAnchorWithExpiration(scoped_refptr<ParsedCertificate> cert);
+
+  // Adds a certificate as a trust anchor and extracts anchor constraints from
   // the certificate. See VerifyCertificateChain for details.
   void AddTrustAnchorWithConstraints(scoped_refptr<ParsedCertificate> cert);
 
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index cc73230..10a1ff0 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -485,6 +485,7 @@
   // follows the description in RFC 5937
   void ProcessRootCertificate(const ParsedCertificate& cert,
                               const CertificateTrust& trust,
+                              const der::GeneralizedTime& time,
                               KeyPurpose required_key_purpose,
                               CertErrors* errors,
                               bool* shortcircuit_chain_validation);
@@ -1122,6 +1123,7 @@
 
 void PathVerifier::ProcessRootCertificate(const ParsedCertificate& cert,
                                           const CertificateTrust& trust,
+                                          const der::GeneralizedTime& time,
                                           KeyPurpose required_key_purpose,
                                           CertErrors* errors,
                                           bool* shortcircuit_chain_validation) {
@@ -1138,11 +1140,12 @@
       *shortcircuit_chain_validation = true;
       break;
     case CertificateTrustType::TRUSTED_ANCHOR:
+      break;
+    case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+      VerifyTimeValidity(cert, time, errors);
+      break;
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
-      // If the trust anchor has constraints, enforce them.
-      if (trust.type == CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS) {
-        ApplyTrustAnchorConstraints(cert, required_key_purpose, errors);
-      }
+      ApplyTrustAnchorConstraints(cert, required_key_purpose, errors);
       break;
   }
   if (*shortcircuit_chain_validation)
@@ -1261,7 +1264,7 @@
 
     if (is_root_cert) {
       bool shortcircuit_chain_validation = false;
-      ProcessRootCertificate(cert, last_cert_trust, required_key_purpose,
+      ProcessRootCertificate(cert, last_cert_trust, time, required_key_purpose,
                              cert_errors, &shortcircuit_chain_validation);
       if (shortcircuit_chain_validation) {
         // Chains that don't start from a trusted root should short-circuit the
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index e28f1870..378a52d 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -208,6 +208,10 @@
 // SPKI, are checked during verification. This is the usual
 // interpretation for a "trust anchor".
 //
+// TRUSTED_ANCHOR_WITH_EXPIRATION:
+//
+// The validity period of the root is checked, in addition to Subject and SPKI.
+//
 // TRUSTED_ANCHOR_WITH_CONSTRAINTS:
 //
 // Only a subset of extensions and properties from the certificate are checked,
diff --git a/net/cert/internal/verify_certificate_chain_typed_unittest.h b/net/cert/internal/verify_certificate_chain_typed_unittest.h
index 50042c75..31a7922 100644
--- a/net/cert/internal/verify_certificate_chain_typed_unittest.h
+++ b/net/cert/internal/verify_certificate_chain_typed_unittest.h
@@ -45,6 +45,8 @@
 
 TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Simple) {
   this->RunTest("target-and-intermediate/main.test");
+  this->RunTest("target-and-intermediate/ta-with-expiration.test");
+  this->RunTest("target-and-intermediate/ta-with-constraints.test");
 }
 
 TYPED_TEST_P(VerifyCertificateChainSingleRootTest, BasicConstraintsCa) {
@@ -106,7 +108,9 @@
   this->RunTest("expired-intermediate/not-before.test");
   this->RunTest("expired-intermediate/not-after.test");
   this->RunTest("expired-root/not-before.test");
+  this->RunTest("expired-root/not-before-ta-with-expiration.test");
   this->RunTest("expired-root/not-after.test");
+  this->RunTest("expired-root/not-after-ta-with-expiration.test");
   this->RunTest("expired-root/not-after-ta-with-constraints.test");
 }
 
diff --git a/net/data/verify_certificate_chain_unittest/README b/net/data/verify_certificate_chain_unittest/README
index f1cef92..539c1c5 100644
--- a/net/data/verify_certificate_chain_unittest/README
+++ b/net/data/verify_certificate_chain_unittest/README
@@ -50,6 +50,7 @@
       certificate in the chain (i.e. whether it is a trust anchor or not). This
       maps to the CertificateTrustType enum. Possible values are:
           "TRUSTED_ANCHOR"
+          "TRUSTED_ANCHOR_WITH_EXPIRATION"
           "TRUSTED_ANCHOR_WITH_CONSTRAINTS"
           "UNSPECIFIED"
           "DISTRUSTED"
diff --git a/net/data/verify_certificate_chain_unittest/expired-root/not-after-ta-with-expiration.test b/net/data/verify_certificate_chain_unittest/expired-root/not-after-ta-with-expiration.test
new file mode 100644
index 0000000..26840ab
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/expired-root/not-after-ta-with-expiration.test
@@ -0,0 +1,8 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR_WITH_EXPIRATION
+utc_time: 151102120000Z
+key_purpose: SERVER_AUTH
+expected_errors:
+----- Certificate i=2 (CN=Root) -----
+ERROR: Time is after notAfter
+
diff --git a/net/data/verify_certificate_chain_unittest/expired-root/not-before-ta-with-expiration.test b/net/data/verify_certificate_chain_unittest/expired-root/not-before-ta-with-expiration.test
new file mode 100644
index 0000000..78a9ae7e9
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/expired-root/not-before-ta-with-expiration.test
@@ -0,0 +1,8 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR_WITH_EXPIRATION
+utc_time: 150201120000Z
+key_purpose: SERVER_AUTH
+expected_errors:
+----- Certificate i=2 (CN=Root) -----
+ERROR: Time is before notBefore
+
diff --git a/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-constraints.test b/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-constraints.test
new file mode 100644
index 0000000..14a6e03
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-constraints.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR_WITH_CONSTRAINTS
+utc_time: DEFAULT
+key_purpose: SERVER_AUTH
+expected_errors:
diff --git a/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-expiration.test b/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-expiration.test
new file mode 100644
index 0000000..fc8361e9
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/target-and-intermediate/ta-with-expiration.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR_WITH_EXPIRATION
+utc_time: DEFAULT
+key_purpose: SERVER_AUTH
+expected_errors:
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index fb4fa2c..ae2b15c 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -193,6 +193,9 @@
                    static_cast<int>(CertificateTrustType::UNSPECIFIED));
     dict.SetIntKey("TRUSTED_ANCHOR",
                    static_cast<int>(CertificateTrustType::TRUSTED_ANCHOR));
+    dict.SetIntKey(
+        "TRUSTED_ANCHOR_WITH_EXPIRATION",
+        static_cast<int>(CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION));
     dict.SetIntKey("TRUSTED_ANCHOR_WITH_CONSTRAINTS",
                    static_cast<int>(
                        CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS));
diff --git a/net/ssl/client_cert_identity.h b/net/ssl/client_cert_identity.h
index 835a6bf1..d7351943 100644
--- a/net/ssl/client_cert_identity.h
+++ b/net/ssl/client_cert_identity.h
@@ -6,14 +6,9 @@
 #define NET_SSL_CLIENT_CERT_IDENTITY_H_
 
 #include "base/callback.h"
-#include "build/build_config.h"
 #include "net/base/net_export.h"
 #include "net/cert/x509_certificate.h"
 
-#if BUILDFLAG(IS_APPLE)
-#include <Security/SecBase.h>
-#endif
-
 namespace base {
 class Time;
 }
@@ -40,11 +35,6 @@
       base::OnceCallback<void(scoped_refptr<SSLPrivateKey>)>
           private_key_callback) = 0;
 
-#if BUILDFLAG(IS_APPLE)
-  // Returns the SecIdentityRef for this identity.
-  virtual SecIdentityRef sec_identity_ref() const = 0;
-#endif
-
   // Acquires the private key for |identity|, taking ownership of |identity| so
   // that the caller does not need to manage its lifetime. The other semantics
   // are the same as for AcquirePrivateKey above.
diff --git a/net/ssl/client_cert_identity_mac.cc b/net/ssl/client_cert_identity_mac.cc
index c9f62fa..143297c 100644
--- a/net/ssl/client_cert_identity_mac.cc
+++ b/net/ssl/client_cert_identity_mac.cc
@@ -25,8 +25,4 @@
       .Run(CreateSSLPrivateKeyForSecIdentity(certificate(), identity_.get()));
 }
 
-SecIdentityRef ClientCertIdentityMac::sec_identity_ref() const {
-  return identity_.get();
-}
-
 }  // namespace net
diff --git a/net/ssl/client_cert_identity_mac.h b/net/ssl/client_cert_identity_mac.h
index 0fbbb05..8d4cdf48 100644
--- a/net/ssl/client_cert_identity_mac.h
+++ b/net/ssl/client_cert_identity_mac.h
@@ -20,9 +20,10 @@
                         base::ScopedCFTypeRef<SecIdentityRef> sec_identity);
   ~ClientCertIdentityMac() override;
 
+  SecIdentityRef sec_identity_ref() const { return identity_.get(); }
+
   void AcquirePrivateKey(base::OnceCallback<void(scoped_refptr<SSLPrivateKey>)>
                              private_key_callback) override;
-  SecIdentityRef sec_identity_ref() const override;
 
  private:
   base::ScopedCFTypeRef<SecIdentityRef> identity_;
diff --git a/net/ssl/client_cert_identity_test_util.cc b/net/ssl/client_cert_identity_test_util.cc
index ef8d9f0..4349122 100644
--- a/net/ssl/client_cert_identity_test_util.cc
+++ b/net/ssl/client_cert_identity_test_util.cc
@@ -9,7 +9,6 @@
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
-#include "build/build_config.h"
 #include "net/ssl/ssl_private_key.h"
 #include "net/ssl/test_ssl_private_key.h"
 #include "net/test/cert_test_util.h"
@@ -78,15 +77,6 @@
   std::move(private_key_callback).Run(key_);
 }
 
-#if BUILDFLAG(IS_APPLE)
-SecIdentityRef FakeClientCertIdentity::sec_identity_ref() const {
-  // Any tests that depend on having a real SecIdentityRef should use a real
-  // ClientCertIdentityMac.
-  NOTREACHED();
-  return nullptr;
-}
-#endif
-
 ClientCertIdentityList FakeClientCertIdentityListFromCertificateList(
     const CertificateList& certs) {
   ClientCertIdentityList result;
diff --git a/net/ssl/client_cert_identity_test_util.h b/net/ssl/client_cert_identity_test_util.h
index 98474a57..2f402a4 100644
--- a/net/ssl/client_cert_identity_test_util.h
+++ b/net/ssl/client_cert_identity_test_util.h
@@ -5,7 +5,6 @@
 #ifndef NET_SSL_CLIENT_CERT_IDENTITY_TEST_UTIL_H_
 #define NET_SSL_CLIENT_CERT_IDENTITY_TEST_UTIL_H_
 
-#include "build/build_config.h"
 #include "net/ssl/client_cert_identity.h"
 
 namespace base {
@@ -45,9 +44,6 @@
   // ClientCertIdentity implementation:
   void AcquirePrivateKey(base::OnceCallback<void(scoped_refptr<SSLPrivateKey>)>
                              private_key_callback) override;
-#if BUILDFLAG(IS_APPLE)
-  SecIdentityRef sec_identity_ref() const override;
-#endif
 
  private:
   scoped_refptr<SSLPrivateKey> key_;
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc
index c53de3e..49a85a8f 100644
--- a/net/ssl/client_cert_store_mac.cc
+++ b/net/ssl/client_cert_store_mac.cc
@@ -45,6 +45,9 @@
 
 namespace {
 
+using ClientCertIdentityMacList =
+    std::vector<std::unique_ptr<ClientCertIdentityMac>>;
+
 // Gets the issuer for a given cert, starting with the cert itself and
 // including the intermediate and finally root certificates (if any).
 // This function calls SecTrust but doesn't actually pay attention to the trust
@@ -96,7 +99,7 @@
 // according to Keychain Services, rather than using |identity|'s intermediate
 // certificates. If it is, |*identity| is updated to include the intermediates.
 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
-                          ClientCertIdentity* identity) {
+                          ClientCertIdentityMac* identity) {
   DCHECK(identity);
   DCHECK(identity->sec_identity_ref());
 
@@ -203,13 +206,14 @@
 // full certificate chains. If it is false, only the the certificates and their
 // intermediates (available via X509Certificate::intermediate_buffers())
 // will be considered.
-void GetClientCertsImpl(std::unique_ptr<ClientCertIdentity> preferred_identity,
-                        ClientCertIdentityList regular_identities,
-                        const SSLCertRequestInfo& request,
-                        bool query_keychain,
-                        ClientCertIdentityList* selected_identities) {
+void GetClientCertsImpl(
+    std::unique_ptr<ClientCertIdentityMac> preferred_identity,
+    ClientCertIdentityMacList regular_identities,
+    const SSLCertRequestInfo& request,
+    bool query_keychain,
+    ClientCertIdentityList* selected_identities) {
   scoped_refptr<X509Certificate> preferred_cert_orig;
-  ClientCertIdentityList preliminary_list(std::move(regular_identities));
+  ClientCertIdentityMacList preliminary_list = std::move(regular_identities);
   if (preferred_identity) {
     preferred_cert_orig = preferred_identity->certificate();
     preliminary_list.insert(preliminary_list.begin(),
@@ -218,7 +222,7 @@
 
   selected_identities->clear();
   for (size_t i = 0; i < preliminary_list.size(); ++i) {
-    std::unique_ptr<ClientCertIdentity>& cert = preliminary_list[i];
+    std::unique_ptr<ClientCertIdentityMac>& cert = preliminary_list[i];
     if (cert->certificate()->HasExpired())
       continue;
 
@@ -262,8 +266,8 @@
 // |sec_identity| matches the |preferred_sec_identity|.
 void AddIdentity(ScopedCFTypeRef<SecIdentityRef> sec_identity,
                  SecIdentityRef preferred_sec_identity,
-                 ClientCertIdentityList* regular_identities,
-                 std::unique_ptr<ClientCertIdentity>* preferred_identity) {
+                 ClientCertIdentityMacList* regular_identities,
+                 std::unique_ptr<ClientCertIdentityMac>* preferred_identity) {
   OSStatus err;
   ScopedCFTypeRef<SecCertificateRef> cert_handle;
   err = SecIdentityCopyCertificate(sec_identity.get(),
@@ -318,8 +322,8 @@
   }
 
   // Now enumerate the identities in the available keychains.
-  std::unique_ptr<ClientCertIdentity> preferred_identity;
-  ClientCertIdentityList regular_identities;
+  std::unique_ptr<ClientCertIdentityMac> preferred_identity;
+  ClientCertIdentityMacList regular_identities;
 
   SecIdentitySearchRef search = NULL;
   OSStatus err;
@@ -404,7 +408,7 @@
 }
 
 bool ClientCertStoreMac::SelectClientCertsForTesting(
-    ClientCertIdentityList input_identities,
+    ClientCertIdentityMacList input_identities,
     const SSLCertRequestInfo& request,
     ClientCertIdentityList* selected_identities) {
   GetClientCertsImpl(NULL, std::move(input_identities), request, false,
@@ -413,8 +417,8 @@
 }
 
 bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
-    std::unique_ptr<ClientCertIdentity> preferred_identity,
-    ClientCertIdentityList regular_identities,
+    std::unique_ptr<ClientCertIdentityMac> preferred_identity,
+    ClientCertIdentityMacList regular_identities,
     const SSLCertRequestInfo& request,
     ClientCertIdentityList* selected_identities) {
   GetClientCertsImpl(std::move(preferred_identity),
diff --git a/net/ssl/client_cert_store_mac.h b/net/ssl/client_cert_store_mac.h
index 2d0f700..1884f014 100644
--- a/net/ssl/client_cert_store_mac.h
+++ b/net/ssl/client_cert_store_mac.h
@@ -12,6 +12,8 @@
 
 namespace net {
 
+class ClientCertIdentityMac;
+
 class NET_EXPORT ClientCertStoreMac : public ClientCertStore {
  public:
   ClientCertStoreMac();
@@ -34,17 +36,18 @@
   // creating a list of certificates that otherwise would be extracted from the
   // system store and filtering it using the common logic (less adequate than
   // the approach used on Windows).
-  bool SelectClientCertsForTesting(ClientCertIdentityList input_identities,
-                                   const SSLCertRequestInfo& cert_request_info,
-                                   ClientCertIdentityList* selected_identities);
+  bool SelectClientCertsForTesting(
+      std::vector<std::unique_ptr<ClientCertIdentityMac>> input_identities,
+      const SSLCertRequestInfo& cert_request_info,
+      ClientCertIdentityList* selected_identities);
 
   // Testing hook specific to Mac, where the internal logic recognizes preferred
   // certificates for particular domains. If the preferred certificate is
   // present in the output list (i.e. it doesn't get filtered out), it should
   // always come first.
   bool SelectClientCertsGivenPreferredForTesting(
-      std::unique_ptr<ClientCertIdentity> preferred_identity,
-      ClientCertIdentityList regular_identities,
+      std::unique_ptr<ClientCertIdentityMac> preferred_identity,
+      std::vector<std::unique_ptr<ClientCertIdentityMac>> regular_identities,
       const SSLCertRequestInfo& request,
       ClientCertIdentityList* selected_identities);
 };
diff --git a/net/ssl/client_cert_store_mac_unittest.cc b/net/ssl/client_cert_store_mac_unittest.cc
index 4dd4d6fe..a38359f 100644
--- a/net/ssl/client_cert_store_mac_unittest.cc
+++ b/net/ssl/client_cert_store_mac_unittest.cc
@@ -6,19 +6,36 @@
 
 #include <memory>
 
+#include "net/ssl/client_cert_identity_mac.h"
 #include "net/ssl/client_cert_identity_test_util.h"
 #include "net/ssl/client_cert_store_unittest-inl.h"
 #include "net/ssl/ssl_private_key.h"
 
 namespace net {
 
+std::vector<std::unique_ptr<ClientCertIdentityMac>>
+ClientCertIdentityMacListFromCertificateList(const CertificateList& certs) {
+  // This doesn't quite construct a real `ClientCertIdentityMac` the
+  // `SecIdentityRef` is null. This means `SelectClientCertsForTesting` must
+  // turn off the KeyChain query. If this becomes an issue, change
+  // client_cert_store_unittest-inl.h to pass in the key data and use
+  // `ScopedTestKeychain` with `ImportCertAndKeyToKeychain`.
+  std::vector<std::unique_ptr<ClientCertIdentityMac>> identities;
+  identities.reserve(certs.size());
+  for (const auto& cert : certs) {
+    identities.push_back(std::make_unique<ClientCertIdentityMac>(
+        cert, base::ScopedCFTypeRef<SecIdentityRef>()));
+  }
+  return identities;
+}
+
 class ClientCertStoreMacTestDelegate {
  public:
   bool SelectClientCerts(const CertificateList& input_certs,
                          const SSLCertRequestInfo& cert_request_info,
                          ClientCertIdentityList* selected_certs) {
     return store_.SelectClientCertsForTesting(
-        FakeClientCertIdentityListFromCertificateList(input_certs),
+        ClientCertIdentityMacListFromCertificateList(input_certs),
         cert_request_info, selected_certs);
   }
 
@@ -37,12 +54,12 @@
       const CertificateList& regular_certs,
       const SSLCertRequestInfo& request,
       ClientCertIdentityList* selected_certs) {
-    std::unique_ptr<ClientCertIdentity> preferred_identity(
-        std::make_unique<FakeClientCertIdentity>(preferred_cert, nullptr));
+    auto preferred_identity = std::make_unique<ClientCertIdentityMac>(
+        preferred_cert, base::ScopedCFTypeRef<SecIdentityRef>());
 
     return store_.SelectClientCertsGivenPreferredForTesting(
         std::move(preferred_identity),
-        FakeClientCertIdentityListFromCertificateList(regular_certs), request,
+        ClientCertIdentityMacListFromCertificateList(regular_certs), request,
         selected_certs);
   }
 
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index f1af57f..eba81b7 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -239,10 +239,6 @@
     // Notifies the client that the document has failed to load.
     virtual void DocumentLoadFailed() {}
 
-    // Asks the client to set the last plugin instance when applicable.
-    // TODO(crbug.com/702993): Remove after migrating away from PPAPI.
-    virtual void SetLastPluginInstance() {}
-
     // Notifies that an unsupported feature in the PDF was encountered.
     virtual void DocumentHasUnsupportedFeature(const std::string& feature) {}
 
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index 8fb0686..3cc2ec1 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -796,7 +796,7 @@
     last_progress_sent_ = 0;
 
   UrlRequest request;
-  request.url = RewriteRequestUrl(url);
+  request.url = static_cast<std::string>(url);
   request.method = "GET";
   request.ignore_redirects = true;
 
@@ -809,10 +809,6 @@
                      GetWeakPtr(), std::move(loader)));
 }
 
-std::string PdfViewPluginBase::RewriteRequestUrl(base::StringPiece url) const {
-  return std::string(url);
-}
-
 void PdfViewPluginBase::InvalidateAfterPaintDone() {
   if (deferred_invalidates_.empty())
     return;
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h
index 085757e8..5cc8d235 100644
--- a/pdf/pdf_view_plugin_base.h
+++ b/pdf/pdf_view_plugin_base.h
@@ -228,12 +228,6 @@
   // frame's origin.
   virtual std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() = 0;
 
-  // Rewrites the request URL just before sending to the URL loader.
-  //
-  // TODO(crbug.com/1238829): This is a workaround for Pepper not supporting
-  // chrome-untrusted://print/ URLs.
-  virtual std::string RewriteRequestUrl(base::StringPiece url) const;
-
   bool HandleInputEvent(const blink::WebInputEvent& event);
 
   // Handles `postMessage()` calls from the embedder.
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 8c3cf63..2aefaab 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -549,9 +549,6 @@
   IFSDK_PAUSE::version = 1;
   IFSDK_PAUSE::user = nullptr;
   IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
-
-  // PreviewModeClient does not know its pp::Instance.
-  SetLastInstance();
 }
 
 PDFiumEngine::~PDFiumEngine() {
@@ -1013,8 +1010,6 @@
 
   KillFormFocus();
 
-  SetLastInstance();
-
   return print_.PrintPagesAsPdf(page_numbers, print_params);
 }
 
@@ -3145,7 +3140,6 @@
   DCHECK_LT(static_cast<size_t>(progressive_index), progressive_paints_.size());
 
   last_progressive_start_time_ = base::Time::Now();
-  SetLastInstance();
 
   int page_index = progressive_paints_[progressive_index].page_index();
   DCHECK(PageIndexInBounds(page_index));
@@ -3643,7 +3637,6 @@
     FORM_DoPageAAction(old_page, form(), FPDFPAGE_AACTION_CLOSE);
   }
   most_visible_page_ = index;
-  SetLastInstance();
   if (most_visible_page_ != -1 && called_do_document_action_) {
     FPDF_PAGE new_page = pages_[most_visible_page_]->GetPage();
     FORM_DoPageAAction(new_page, form(), FPDFPAGE_AACTION_OPEN);
@@ -4312,10 +4305,6 @@
   pending_thumbnails_.erase(it);
 }
 
-void PDFiumEngine::SetLastInstance() {
-  client_->SetLastPluginInstance();
-}
-
 PDFiumEngine::ProgressivePaint::ProgressivePaint(int index,
                                                  const gfx::Rect& rect)
     : page_index_(index), rect_(rect) {}
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index 2fea7e4..e3acf993 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -651,10 +651,6 @@
   // requests the thumbnail for that page.
   void MaybeRequestPendingThumbnail(int page_index);
 
-  // Keeps track of the most recently used plugin instance.
-  // TODO(crbug.com/702993): Remove when PPAPI is gone.
-  void SetLastInstance();
-
   const raw_ptr<PDFEngine::Client> client_;
 
   // The current document layout.
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 3a7b620..df70b8d7 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -423,8 +423,8 @@
 
 void It2MeNativeMessagingHost::SendOutgoingIq(const std::string& iq) {
   std::unique_ptr<base::DictionaryValue> message(new base::DictionaryValue());
-  message->SetString(kMessageType, kSendOutgoingIqMessage);
-  message->SetString(kIq, iq);
+  message->SetStringKey(kMessageType, kSendOutgoingIqMessage);
+  message->SetStringKey(kIq, iq);
 
   SendMessageToClient(std::move(message));
 }
@@ -433,10 +433,11 @@
     std::unique_ptr<base::DictionaryValue> response,
     protocol::ErrorCode error_code) const {
   DCHECK(task_runner()->BelongsToCurrentThread());
-  response->SetString(kMessageType, kErrorMessage);
-  response->SetString(kErrorMessageCode, ErrorCodeToString(error_code));
+  response->SetStringKey(kMessageType, kErrorMessage);
+  response->SetStringKey(kErrorMessageCode, ErrorCodeToString(error_code));
   // TODO(kelvinp): Remove this after M61 Webapp is pushed to 100%.
-  response->SetString(kErrorMessageDescription, ErrorCodeToString(error_code));
+  response->SetStringKey(kErrorMessageDescription,
+                         ErrorCodeToString(error_code));
   SendMessageToClient(std::move(response));
 
   // Trigger a host shutdown by sending an empty message.
@@ -447,7 +448,7 @@
   DCHECK(task_runner()->BelongsToCurrentThread());
 
   auto message = std::make_unique<base::DictionaryValue>();
-  message->SetString(kMessageType, kPolicyErrorMessage);
+  message->SetStringKey(kMessageType, kPolicyErrorMessage);
   SendMessageToClient(std::move(message));
   client_->CloseChannel(std::string());
 }
@@ -460,22 +461,22 @@
 
   std::unique_ptr<base::DictionaryValue> message(new base::DictionaryValue());
 
-  message->SetString(kMessageType, kHostStateChangedMessage);
-  message->SetString(kState, It2MeHostStateToString(state));
+  message->SetStringKey(kMessageType, kHostStateChangedMessage);
+  message->SetStringKey(kState, It2MeHostStateToString(state));
 
   switch (state_) {
     case It2MeHostState::kReceivedAccessCode:
-      message->SetString(kAccessCode, access_code_);
-      message->SetInteger(kAccessCodeLifetime,
-                          access_code_lifetime_.InSeconds());
+      message->SetStringKey(kAccessCode, access_code_);
+      message->SetIntKey(kAccessCodeLifetime,
+                         access_code_lifetime_.InSeconds());
       break;
 
     case It2MeHostState::kConnected:
-      message->SetString(kClient, client_username_);
+      message->SetStringKey(kClient, client_username_);
       break;
 
     case It2MeHostState::kDisconnected:
-      message->SetString(kDisconnectReason, ErrorCodeToString(error_code));
+      message->SetStringKey(kDisconnectReason, ErrorCodeToString(error_code));
       client_username_.clear();
       break;
 
@@ -483,11 +484,11 @@
       // kError is an internal-only state, sent to the web-app by a separate
       // "error" message so that errors that occur before the "connect" message
       // is sent can be communicated.
-      message->SetString(kMessageType, kErrorMessage);
-      message->SetString(kErrorMessageCode, ErrorCodeToString(error_code));
+      message->SetStringKey(kMessageType, kErrorMessage);
+      message->SetStringKey(kErrorMessageCode, ErrorCodeToString(error_code));
       // TODO(kelvinp): Remove this after M61 Webapp is pushed to 100%.
-      message->SetString(kErrorMessageDescription,
-                         ErrorCodeToString(error_code));
+      message->SetStringKey(kErrorMessageDescription,
+                            ErrorCodeToString(error_code));
       break;
 
     default:
@@ -509,10 +510,10 @@
 
   std::unique_ptr<base::DictionaryValue> message(new base::DictionaryValue());
 
-  message->SetString(kMessageType, kNatPolicyChangedMessage);
-  message->SetBoolean(kNatPolicyChangedMessageNatEnabled,
+  message->SetStringKey(kMessageType, kNatPolicyChangedMessage);
+  message->SetBoolKey(kNatPolicyChangedMessageNatEnabled,
                       nat_traversal_enabled);
-  message->SetBoolean(kNatPolicyChangedMessageRelayEnabled,
+  message->SetBoolKey(kNatPolicyChangedMessageRelayEnabled,
                       relay_connections_allowed);
   SendMessageToClient(std::move(message));
 }
diff --git a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
index 48a8d63..02f2b71 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
@@ -88,10 +88,10 @@
 
 base::DictionaryValue CreateConnectMessage(int id) {
   base::DictionaryValue connect_message;
-  connect_message.SetInteger(kMessageId, id);
-  connect_message.SetString(kMessageType, kConnectMessage);
-  connect_message.SetString(kUserName, kTestClientUsername);
-  connect_message.SetString(kAuthServiceWithToken, "oauth2:sometoken");
+  connect_message.SetIntKey(kMessageId, id);
+  connect_message.SetStringKey(kMessageType, kConnectMessage);
+  connect_message.SetStringKey(kUserName, kTestClientUsername);
+  connect_message.SetStringKey(kAuthServiceWithToken, "oauth2:sometoken");
   connect_message.SetKey(
       kIceConfig,
       base::Value::FromUniquePtrValue(base::JSONReader::ReadDeprecated(
@@ -103,8 +103,8 @@
 
 base::DictionaryValue CreateDisconnectMessage(int id) {
   base::DictionaryValue disconnect_message;
-  disconnect_message.SetInteger(kMessageId, id);
-  disconnect_message.SetString(kMessageType, kDisconnectMessage);
+  disconnect_message.SetIntKey(kMessageId, id);
+  disconnect_message.SetStringKey(kMessageType, kDisconnectMessage);
   return disconnect_message;
 }
 
@@ -530,8 +530,8 @@
 void It2MeNativeMessagingHostTest::TestBadRequest(const base::Value& message,
                                                   bool expect_error_response) {
   base::DictionaryValue good_message;
-  good_message.SetString(kMessageType, kHelloMessage);
-  good_message.SetInteger(kMessageId, 1);
+  good_message.SetStringKey(kMessageType, kHelloMessage);
+  good_message.SetIntKey(kMessageId, 1);
 
   WriteMessageToInputPipe(good_message);
   WriteMessageToInputPipe(message);
@@ -622,8 +622,8 @@
 TEST_F(It2MeNativeMessagingHostTest, Hello) {
   int next_id = 0;
   base::DictionaryValue message;
-  message.SetInteger(kMessageId, ++next_id);
-  message.SetString(kMessageType, kHelloMessage);
+  message.SetIntKey(kMessageId, ++next_id);
+  message.SetStringKey(kMessageType, kHelloMessage);
   WriteMessageToInputPipe(message);
 
   VerifyHelloResponse(next_id);
@@ -632,9 +632,9 @@
 // Verify that response ID matches request ID.
 TEST_F(It2MeNativeMessagingHostTest, Id) {
   base::DictionaryValue message;
-  message.SetString(kMessageType, kHelloMessage);
+  message.SetStringKey(kMessageType, kHelloMessage);
   WriteMessageToInputPipe(message);
-  message.SetString(kMessageId, "42");
+  message.SetStringKey(kMessageId, "42");
   WriteMessageToInputPipe(message);
 
   std::unique_ptr<base::DictionaryValue> response = ReadMessageFromOutputPipe();
@@ -661,7 +661,7 @@
        ConnectRespectsSuppressUserDialogsParameterOnChromeOsOnly) {
   int next_id = 1;
   base::DictionaryValue connect_message = CreateConnectMessage(next_id);
-  connect_message.SetBoolean(kSuppressUserDialogs, true);
+  connect_message.SetBoolKey(kSuppressUserDialogs, true);
   WriteMessageToInputPipe(connect_message);
   VerifyConnectResponses(next_id);
 #if BUILDFLAG(IS_CHROMEOS_ASH) || !defined(NDEBUG)
@@ -678,7 +678,7 @@
        ConnectRespectsSuppressNotificationsParameterOnChromeOsOnly) {
   int next_id = 1;
   base::DictionaryValue connect_message = CreateConnectMessage(next_id);
-  connect_message.SetBoolean(kSuppressNotifications, true);
+  connect_message.SetBoolKey(kSuppressNotifications, true);
   WriteMessageToInputPipe(connect_message);
   VerifyConnectResponses(next_id);
 #if BUILDFLAG(IS_CHROMEOS_ASH) || !defined(NDEBUG)
@@ -707,14 +707,14 @@
 // Verify rejection if type is unrecognized.
 TEST_F(It2MeNativeMessagingHostTest, InvalidType) {
   base::DictionaryValue message;
-  message.SetString(kMessageType, "xxx");
+  message.SetStringKey(kMessageType, "xxx");
   TestBadRequest(message, true);
 }
 
 // Verify rejection if type is unrecognized.
 TEST_F(It2MeNativeMessagingHostTest, BadPoliciesBeforeConnect) {
   base::DictionaryValue bad_policy;
-  bad_policy.SetInteger(policy::key::kRemoteAccessHostFirewallTraversal, 1);
+  bad_policy.SetIntKey(policy::key::kRemoteAccessHostFirewallTraversal, 1);
   SetPolicies(bad_policy);
   WriteMessageToInputPipe(CreateConnectMessage(1));
   VerifyPolicyErrorResponse();
@@ -723,7 +723,7 @@
 // Verify rejection if type is unrecognized.
 TEST_F(It2MeNativeMessagingHostTest, BadPoliciesAfterConnect) {
   base::DictionaryValue bad_policy;
-  bad_policy.SetInteger(policy::key::kRemoteAccessHostFirewallTraversal, 1);
+  bad_policy.SetIntKey(policy::key::kRemoteAccessHostFirewallTraversal, 1);
   WriteMessageToInputPipe(CreateConnectMessage(1));
   VerifyConnectResponses(1);
   SetPolicies(bad_policy);
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py
index 744d779..d5d3125 100755
--- a/remoting/host/linux/linux_me2me_host.py
+++ b/remoting/host/linux/linux_me2me_host.py
@@ -32,6 +32,7 @@
 import shlex
 import signal
 import socket
+import struct
 import subprocess
 import syslog
 import tempfile
@@ -590,9 +591,9 @@
                        (self.session_proc, "session"),
                        (self.pre_session_proc, "pre-session"),
                        (self.server_proc, "display server")]:
-      logging.info("Shutting down %s: %s", name, proc and proc.pid)
       if proc is not None:
-        logging.info("Sending SIGTERM to %s proc.", name)
+        logging.info("Sending SIGTERM to %s proc (pid=%s)",
+                     name, proc and proc.pid)
         try:
           psutil_proc = psutil.Process(proc.pid)
           psutil_proc.terminate()
@@ -792,8 +793,9 @@
 
   WL_SOCKET_CHECK_DELAY_SECONDS = 1
   WL_SOCKET_CHECK_TIMEOUT_SECONDS = 30
-  # We scan for the unused socket starting from number 0. If we are not able to
-  # find anything between 0 and 100 then we error out since there could be a
+  WL_SERVER_REPLY_TIMEOUT_SECONDS = 1
+  # We scan for the unused socket starting from number 1. If we are not able to
+  # find anything between 1 and 100 then we error out since there could be a
   # socket leak and we don't want to keep retrying forever.
   MAX_WAYLAND_SOCKET_NUM = 100
 
@@ -878,7 +880,7 @@
   def _gnome_shell_cmd(self, screen_width=1280, screen_height=720):
     return ["gnome-shell", "--wayland", "--headless", "--virtual-monitor",
             "%sx%s" % (screen_width, screen_height), "--wayland-display",
-            self._wayland_socket, "--no-x11"]
+            self._wayland_socket, "--no-x11", "--replace"]
 
   def _launch_server(self, *args, **kwargs):
     if not self._is_gnome_shell_present():
@@ -978,31 +980,30 @@
     If the connection succeeds, it means that the server is still up and
     running.
     """
-    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
     try:
-      sock.connect(os.path.join(self.runtime_dir, self._wayland_socket))
-      # Asks the server for the global registry object
-      # (See: https://wayland-book.com/registry.html)
-      sock.sendall(struct.pack(">III", 0x00000001, 0x000C0001, 0x00000002))
+      with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
+        sock.connect(os.path.join(self.runtime_dir, self._wayland_socket))
+        # Asks the server for the global registry object
+        # (See: https://wayland-book.com/registry.html)
+        sock.sendall(struct.pack("<III", 0x00000001, 0x000C0001, 0x00000002))
 
-      num_bytes_received = 0
-      NUM_BYTES_EXPECTED = 32
-      sock.settimeout(1)  # We don't want to wait forever for a reply
-      while num_bytes_received < NUM_BYTES_EXPECTED:
-          data = sock.recv(NUM_BYTES_EXPECTED)
-          if len(data) == 0:  # Expect empty reply if server dies
-             break
-          num_bytes_received += len(data)
-          logging.debug("Wayland server replied with: %s" % data)
-      if not num_bytes_received:
-        # If we don't receive a reply at all then the server is likely not
-        # listening on the socket.
-        return False
+        num_bytes_received = 0
+        NUM_BYTES_EXPECTED = 32
+        # We don't want to wait forever for a reply so we set a timeout here.
+        sock.settimeout(self.WL_SERVER_REPLY_TIMEOUT_SECONDS)
+        while num_bytes_received < NUM_BYTES_EXPECTED:
+            data = sock.recv(NUM_BYTES_EXPECTED)
+            if len(data) == 0:  # Expect empty reply if server dies
+               break
+            num_bytes_received += len(data)
+            logging.debug("Wayland server replied with: %s" % data)
+        if not num_bytes_received:
+          # If we don't receive a reply at all then the server is likely not
+          # listening on the socket.
+          return False
     except socket.error as err:
         logging.error("Wayland server is not responding: %s" % err)
         return False
-    finally:
-      sock.close()
     return True
 
 
diff --git a/remoting/host/policy_watcher.cc b/remoting/host/policy_watcher.cc
index 91c437c..bfd9667 100644
--- a/remoting/host/policy_watcher.cc
+++ b/remoting/host/policy_watcher.cc
@@ -174,30 +174,30 @@
 
 std::unique_ptr<base::DictionaryValue> PolicyWatcher::GetDefaultPolicies() {
   auto result = std::make_unique<base::DictionaryValue>();
-  result->SetBoolean(key::kRemoteAccessHostFirewallTraversal, true);
-  result->SetBoolean(key::kRemoteAccessHostRequireCurtain, false);
-  result->SetBoolean(key::kRemoteAccessHostMatchUsername, false);
+  result->SetBoolKey(key::kRemoteAccessHostFirewallTraversal, true);
+  result->SetBoolKey(key::kRemoteAccessHostRequireCurtain, false);
+  result->SetBoolKey(key::kRemoteAccessHostMatchUsername, false);
   result->Set(key::kRemoteAccessHostClientDomainList,
               std::make_unique<base::ListValue>());
   result->Set(key::kRemoteAccessHostDomainList,
               std::make_unique<base::ListValue>());
-  result->SetString(key::kRemoteAccessHostTokenUrl, std::string());
-  result->SetString(key::kRemoteAccessHostTokenValidationUrl, std::string());
-  result->SetString(key::kRemoteAccessHostTokenValidationCertificateIssuer,
-                    std::string());
-  result->SetBoolean(key::kRemoteAccessHostAllowClientPairing, true);
-  result->SetBoolean(key::kRemoteAccessHostAllowGnubbyAuth, true);
-  result->SetBoolean(key::kRemoteAccessHostAllowRelayedConnection, true);
-  result->SetString(key::kRemoteAccessHostUdpPortRange, "");
-  result->SetBoolean(key::kRemoteAccessHostAllowUiAccessForRemoteAssistance,
+  result->SetStringKey(key::kRemoteAccessHostTokenUrl, std::string());
+  result->SetStringKey(key::kRemoteAccessHostTokenValidationUrl, std::string());
+  result->SetStringKey(key::kRemoteAccessHostTokenValidationCertificateIssuer,
+                       std::string());
+  result->SetBoolKey(key::kRemoteAccessHostAllowClientPairing, true);
+  result->SetBoolKey(key::kRemoteAccessHostAllowGnubbyAuth, true);
+  result->SetBoolKey(key::kRemoteAccessHostAllowRelayedConnection, true);
+  result->SetStringKey(key::kRemoteAccessHostUdpPortRange, "");
+  result->SetBoolKey(key::kRemoteAccessHostAllowUiAccessForRemoteAssistance,
                      false);
-  result->SetInteger(key::kRemoteAccessHostClipboardSizeBytes, -1);
-  result->SetBoolean(key::kRemoteAccessHostAllowRemoteSupportConnections, true);
+  result->SetIntKey(key::kRemoteAccessHostClipboardSizeBytes, -1);
+  result->SetBoolKey(key::kRemoteAccessHostAllowRemoteSupportConnections, true);
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  result->SetBoolean(key::kRemoteAccessHostAllowFileTransfer, true);
-  result->SetBoolean(key::kRemoteAccessHostEnableUserInterface, true);
-  result->SetBoolean(key::kRemoteAccessHostAllowRemoteAccessConnections, true);
-  result->SetInteger(key::kRemoteAccessHostMaximumSessionDurationMinutes, 0);
+  result->SetBoolKey(key::kRemoteAccessHostAllowFileTransfer, true);
+  result->SetBoolKey(key::kRemoteAccessHostEnableUserInterface, true);
+  result->SetBoolKey(key::kRemoteAccessHostAllowRemoteAccessConnections, true);
+  result->SetIntKey(key::kRemoteAccessHostMaximumSessionDurationMinutes, 0);
 #endif
   return result;
 }
diff --git a/remoting/host/setup/me2me_native_messaging_host.cc b/remoting/host/setup/me2me_native_messaging_host.cc
index 1c8a2e2..01b3878 100644
--- a/remoting/host/setup/me2me_native_messaging_host.cc
+++ b/remoting/host/setup/me2me_native_messaging_host.cc
@@ -119,7 +119,7 @@
     return;
   }
 
-  response->SetString("type", type + "Response");
+  response->SetStringKey("type", type + "Response");
 
   if (type == "hello") {
     ProcessHello(std::move(message_dict), std::move(response));
@@ -180,7 +180,7 @@
     std::unique_ptr<base::DictionaryValue> response) {
   DCHECK(task_runner()->BelongsToCurrentThread());
 
-  response->SetString("version", STRINGIZE(VERSION));
+  response->SetStringKey("version", STRINGIZE(VERSION));
   auto supported_features_list = std::make_unique<base::ListValue>();
   for (const char* feature : kSupportedFeatures) {
     supported_features_list->Append(feature);
@@ -244,7 +244,7 @@
     std::unique_ptr<base::DictionaryValue> response) {
   DCHECK(task_runner()->BelongsToCurrentThread());
 
-  response->SetString("hostname", net::GetHostName());
+  response->SetStringKey("hostname", net::GetHostName());
   SendMessageToClient(std::move(response));
 }
 
@@ -267,7 +267,7 @@
     OnError("'pin' not found: " + message_json);
     return;
   }
-  response->SetString("hash", MakeHostPinHash(host_id, pin));
+  response->SetStringKey("hash", MakeHostPinHash(host_id, pin));
   SendMessageToClient(std::move(response));
 }
 
@@ -277,8 +277,8 @@
   DCHECK(task_runner()->BelongsToCurrentThread());
 
   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
-  response->SetString("privateKey", key_pair->ToString());
-  response->SetString("publicKey", key_pair->GetPublicKey());
+  response->SetStringKey("privateKey", key_pair->ToString());
+  response->SetStringKey("publicKey", key_pair->GetPublicKey());
   SendMessageToClient(std::move(response));
 }
 
@@ -423,22 +423,22 @@
   DaemonController::State state = daemon_controller_->GetState();
   switch (state) {
     case DaemonController::STATE_NOT_IMPLEMENTED:
-      response->SetString("state", "NOT_IMPLEMENTED");
+      response->SetStringKey("state", "NOT_IMPLEMENTED");
       break;
     case DaemonController::STATE_STOPPED:
-      response->SetString("state", "STOPPED");
+      response->SetStringKey("state", "STOPPED");
       break;
     case DaemonController::STATE_STARTING:
-      response->SetString("state", "STARTING");
+      response->SetStringKey("state", "STARTING");
       break;
     case DaemonController::STATE_STARTED:
-      response->SetString("state", "STARTED");
+      response->SetStringKey("state", "STARTED");
       break;
     case DaemonController::STATE_STOPPING:
-      response->SetString("state", "STOPPING");
+      response->SetStringKey("state", "STOPPING");
       break;
     case DaemonController::STATE_UNKNOWN:
-      response->SetString("state", "UNKNOWN");
+      response->SetStringKey("state", "UNKNOWN");
       break;
   }
   SendMessageToClient(std::move(response));
@@ -449,8 +449,8 @@
     std::unique_ptr<base::DictionaryValue> response) {
   DCHECK(task_runner()->BelongsToCurrentThread());
 
-  response->SetString("clientId", google_apis::GetOAuth2ClientID(
-      google_apis::CLIENT_REMOTING_HOST));
+  response->SetStringKey("clientId", google_apis::GetOAuth2ClientID(
+                                         google_apis::CLIENT_REMOTING_HOST));
   SendMessageToClient(std::move(response));
 }
 
@@ -516,9 +516,9 @@
     const DaemonController::UsageStatsConsent& consent) {
   DCHECK(task_runner()->BelongsToCurrentThread());
 
-  response->SetBoolean("supported", consent.supported);
-  response->SetBoolean("allowed", consent.allowed);
-  response->SetBoolean("setByPolicy", consent.set_by_policy);
+  response->SetBoolKey("supported", consent.supported);
+  response->SetBoolKey("allowed", consent.allowed);
+  response->SetBoolKey("setByPolicy", consent.set_by_policy);
   SendMessageToClient(std::move(response));
 }
 
@@ -529,13 +529,13 @@
 
   switch (result) {
     case DaemonController::RESULT_OK:
-      response->SetString("result", "OK");
+      response->SetStringKey("result", "OK");
       break;
     case DaemonController::RESULT_FAILED:
-      response->SetString("result", "FAILED");
+      response->SetStringKey("result", "FAILED");
       break;
     case DaemonController::RESULT_CANCELLED:
-      response->SetString("result", "CANCELLED");
+      response->SetStringKey("result", "CANCELLED");
       break;
   }
   SendMessageToClient(std::move(response));
@@ -546,7 +546,7 @@
     bool result) {
   DCHECK(task_runner()->BelongsToCurrentThread());
 
-  response->SetBoolean("result", result);
+  response->SetBoolKey("result", result);
   SendMessageToClient(std::move(response));
 }
 
@@ -557,9 +557,9 @@
   DCHECK(task_runner()->BelongsToCurrentThread());
 
   if (!user_email.empty()) {
-    response->SetString("userEmail", user_email);
+    response->SetStringKey("userEmail", user_email);
   }
-  response->SetString("refreshToken", refresh_token);
+  response->SetStringKey("refreshToken", refresh_token);
   SendMessageToClient(std::move(response));
 }
 
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index 96697a1..2bce5ef 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -462,7 +462,7 @@
 
 void Me2MeNativeMessagingHostTest::TestBadRequest(const base::Value& message) {
   base::DictionaryValue good_message;
-  good_message.SetString("type", "hello");
+  good_message.SetStringKey("type", "hello");
 
   // This test currently relies on synchronous processing of hello messages and
   // message parameters verification.
@@ -483,60 +483,60 @@
 TEST_F(Me2MeNativeMessagingHostTest, All) {
   int next_id = 0;
   base::DictionaryValue message;
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "hello");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "hello");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getHostName");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getHostName");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getPinHash");
-  message.SetString("hostId", "my_host");
-  message.SetString("pin", "1234");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getPinHash");
+  message.SetStringKey("hostId", "my_host");
+  message.SetStringKey("pin", "1234");
   WriteMessageToInputPipe(message);
 
   message.DictClear();
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "generateKeyPair");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "generateKeyPair");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getDaemonConfig");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getDaemonConfig");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getUsageStatsConsent");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getUsageStatsConsent");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "stopDaemon");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "stopDaemon");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getDaemonState");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getDaemonState");
   WriteMessageToInputPipe(message);
 
   // Following messages require a "config" dictionary.
   base::DictionaryValue config;
-  config.SetBoolean("update", true);
+  config.SetBoolKey("update", true);
   message.Set("config", config.CreateDeepCopy());
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "updateDaemonConfig");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "updateDaemonConfig");
   WriteMessageToInputPipe(message);
 
   config.DictClear();
-  config.SetBoolean("start", true);
+  config.SetBoolKey("start", true);
   message.Set("config", config.CreateDeepCopy());
-  message.SetBoolean("consent", true);
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "startDaemon");
+  message.SetBoolKey("consent", true);
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "startDaemon");
   WriteMessageToInputPipe(message);
 
-  message.SetInteger("id", next_id++);
-  message.SetString("type", "getCredentialsFromAuthCode");
-  message.SetString("authorizationCode", "fake_auth_code");
+  message.SetIntKey("id", next_id++);
+  message.SetStringKey("type", "getCredentialsFromAuthCode");
+  message.SetStringKey("authorizationCode", "fake_auth_code");
   WriteMessageToInputPipe(message);
 
   void (*verify_routines[])(std::unique_ptr<base::DictionaryValue>) = {
@@ -576,9 +576,9 @@
 // Verify that response ID matches request ID.
 TEST_F(Me2MeNativeMessagingHostTest, Id) {
   base::DictionaryValue message;
-  message.SetString("type", "hello");
+  message.SetStringKey("type", "hello");
   WriteMessageToInputPipe(message);
-  message.SetString("id", "42");
+  message.SetStringKey("id", "42");
   WriteMessageToInputPipe(message);
 
   std::unique_ptr<base::DictionaryValue> response = ReadMessageFromOutputPipe();
@@ -607,47 +607,47 @@
 // Verify rejection if type is unrecognized.
 TEST_F(Me2MeNativeMessagingHostTest, InvalidType) {
   base::DictionaryValue message;
-  message.SetString("type", "xxx");
+  message.SetStringKey("type", "xxx");
   TestBadRequest(message);
 }
 
 // Verify rejection if getPinHash request has no hostId.
 TEST_F(Me2MeNativeMessagingHostTest, GetPinHashNoHostId) {
   base::DictionaryValue message;
-  message.SetString("type", "getPinHash");
-  message.SetString("pin", "1234");
+  message.SetStringKey("type", "getPinHash");
+  message.SetStringKey("pin", "1234");
   TestBadRequest(message);
 }
 
 // Verify rejection if getPinHash request has no pin.
 TEST_F(Me2MeNativeMessagingHostTest, GetPinHashNoPin) {
   base::DictionaryValue message;
-  message.SetString("type", "getPinHash");
-  message.SetString("hostId", "my_host");
+  message.SetStringKey("type", "getPinHash");
+  message.SetStringKey("hostId", "my_host");
   TestBadRequest(message);
 }
 
 // Verify rejection if updateDaemonConfig request has invalid config.
 TEST_F(Me2MeNativeMessagingHostTest, UpdateDaemonConfigInvalidConfig) {
   base::DictionaryValue message;
-  message.SetString("type", "updateDaemonConfig");
-  message.SetString("config", "xxx");
+  message.SetStringKey("type", "updateDaemonConfig");
+  message.SetStringKey("config", "xxx");
   TestBadRequest(message);
 }
 
 // Verify rejection if startDaemon request has invalid config.
 TEST_F(Me2MeNativeMessagingHostTest, StartDaemonInvalidConfig) {
   base::DictionaryValue message;
-  message.SetString("type", "startDaemon");
-  message.SetString("config", "xxx");
-  message.SetBoolean("consent", true);
+  message.SetStringKey("type", "startDaemon");
+  message.SetStringKey("config", "xxx");
+  message.SetBoolKey("consent", true);
   TestBadRequest(message);
 }
 
 // Verify rejection if startDaemon request has no "consent" parameter.
 TEST_F(Me2MeNativeMessagingHostTest, StartDaemonNoConsent) {
   base::DictionaryValue message;
-  message.SetString("type", "startDaemon");
+  message.SetStringKey("type", "startDaemon");
   message.Set("config", base::DictionaryValue().CreateDeepCopy());
   TestBadRequest(message);
 }
@@ -655,7 +655,7 @@
 // Verify rejection if getCredentialsFromAuthCode has no auth code.
 TEST_F(Me2MeNativeMessagingHostTest, GetCredentialsFromAuthCodeNoAuthCode) {
   base::DictionaryValue message;
-  message.SetString("type", "getCredentialsFromAuthCode");
+  message.SetStringKey("type", "getCredentialsFromAuthCode");
   TestBadRequest(message);
 }
 
diff --git a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
index 376e855..bcce681 100644
--- a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
+++ b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -45,6 +45,9 @@
 namespace sandbox {
 
 using bpf_dsl::Allow;
+using bpf_dsl::Arg;
+using bpf_dsl::Error;
+using bpf_dsl::If;
 using bpf_dsl::ResultExpr;
 using bpf_dsl::Trap;
 
@@ -512,16 +515,21 @@
     DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
     // Broker everything that we're supposed to broker.
     if (broker_process_->IsSyscallAllowed(sysno)) {
-      return sandbox::bpf_dsl::Trap(
-          sandbox::syscall_broker::BrokerClient::SIGSYS_Handler,
-          broker_process_->GetBrokerClientSignalBased());
+      return Trap(BrokerClient::SIGSYS_Handler,
+                  broker_process_->GetBrokerClientSignalBased());
     }
 
     // Otherwise, if this is a syscall that takes a pathname but isn't an
     // allowed command, deny it.
     if (broker_process_->IsSyscallBrokerable(sysno,
                                              /*fast_check_in_client=*/false)) {
-      return bpf_dsl::Error(denied_errno_);
+      return Error(denied_errno_);
+    }
+
+    if (sysno == __NR_statx) {
+      const Arg<int> mask(3);
+      return If(mask == STATX_BASIC_STATS, Error(ENOSYS))
+          .Else(Error(denied_errno_));
     }
 
     // Allow everything else that doesn't take a pathname.
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index aec0ef7..88c4c95 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -31,7 +31,6 @@
   deps = [
     "//base",
     "//build:chromeos_buildflags",
-    "//components/cbor",
     "//components/web_package",
     "//mojo/public/cpp/bindings",
     "//net",
diff --git a/services/data_decoder/DEPS b/services/data_decoder/DEPS
index 7ad730d3..3ff21a83 100644
--- a/services/data_decoder/DEPS
+++ b/services/data_decoder/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+components/cbor",
   "+components/web_package",
   "+device/bluetooth/public",
   "+gin",
diff --git a/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java b/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
index 64a52939..5170690 100644
--- a/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
+++ b/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
@@ -8,9 +8,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.location.LocationManager;
 import android.os.Build;
 
@@ -27,6 +25,7 @@
 import org.mockito.stubbing.Answer;
 import org.robolectric.ParameterizedRobolectricTestRunner;
 import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLocationManager;
@@ -58,19 +57,11 @@
                 {LocationProviderType.ANDROID}, {LocationProviderType.GMS_CORE}});
     }
 
-    // TODO(1300069): Replace with non-mock after updating to robolectric 4.7
-    // which fixes NoClassDefFoundError:
-    // android/location/GnssAntennaInfo$Listener
-    @Mock
-    private Context mContext;
-
     // Member variables for LocationProviderType.GMS_CORE case.
     @Mock
     private GoogleApiClient mGoogleApiClient;
     private boolean mGoogleApiClientIsConnected;
 
-    // Member variables for LocationProviderType.ANDROID case.
-    @Mock
     private LocationManager mLocationManager;
     private ShadowLocationManager mShadowLocationManager;
 
@@ -86,9 +77,7 @@
     public void setUp() {
         ShadowLog.stream = System.out;
         MockitoAnnotations.initMocks(this);
-
-        mContext = Mockito.mock(Context.class);
-        when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
+        mLocationManager = RuntimeEnvironment.application.getSystemService(LocationManager.class);
     }
 
     /**
@@ -148,7 +137,6 @@
 
         // Robolectric has a ShadowLocationManager class that mocks the behaviour of the real
         // class very closely. Use it here.
-        mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
         mShadowLocationManager = Shadows.shadowOf(mLocationManager);
         locationProviderAndroid.setLocationManagerForTesting(mLocationManager);
         LocationProviderFactory.setLocationProviderImpl(locationProviderAndroid);
diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc
index b2897ab..aab41c25 100644
--- a/services/network/first_party_sets/first_party_sets.cc
+++ b/services/network/first_party_sets/first_party_sets.cc
@@ -378,15 +378,12 @@
   InvokePendingQueries();
 }
 
-void FirstPartySets::SetPersistedSets(base::StringPiece raw_sets) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  raw_persisted_sets_ = static_cast<std::string>(raw_sets);
-  ClearSiteDataOnChangedSetsIfReady();
-}
-
-void FirstPartySets::SetOnSiteDataCleared(
+void FirstPartySets::SetPersistedSetsAndOnSiteDataCleared(
+    base::StringPiece raw_sets,
     base::OnceCallback<void(const std::string&)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!callback.is_null());
+  raw_persisted_sets_ = static_cast<std::string>(raw_sets);
   on_site_data_cleared_ = std::move(callback);
   ClearSiteDataOnChangedSetsIfReady();
 }
diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h
index 14d52289..94c3f1d8 100644
--- a/services/network/first_party_sets/first_party_sets.h
+++ b/services/network/first_party_sets/first_party_sets.h
@@ -93,13 +93,18 @@
   // Receives the completed First-Party Sets from `sets_loader_` and stores it
   // in the `sets_`.
   void SetCompleteSets(FlattenedSets sets);
-  // Sets the `raw_persisted_sets_`, which is a JSON-encoded
-  // string representation of a map of site -> site.
-  void SetPersistedSets(base::StringPiece persisted_sets);
-  // Sets the `on_site_data_cleared_` callback, which takes input of a
-  // JSON-encoded string representation of a map of site -> site.
-  void SetOnSiteDataCleared(
+
+  // Sets the `raw_persisted_sets_`, which is a JSON-encoded string
+  // representation of a map of site -> site, and saves a callback to be called
+  // once site data has been cleared appropriately. The callback receives a
+  // serialized representation of the current First-Party Sets as an argument,
+  // and must not be null. (In practice, this callback will be used to update
+  // the on-disk persisted sets, so that the next run of Chromium doesn't clear
+  // more data than necessary.)
+  void SetPersistedSetsAndOnSiteDataCleared(
+      base::StringPiece persisted_sets,
       base::OnceCallback<void(const std::string&)> callback);
+
   // Sets the enabled_ attribute for testing.
   void SetEnabledForTesting(bool enabled);
 
diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc
index cda7981b..58f9530 100644
--- a/services/network/first_party_sets/first_party_sets_unittest.cc
+++ b/services/network/first_party_sets/first_party_sets_unittest.cc
@@ -390,9 +390,8 @@
   {
     FirstPartySets sets(true);
     callback_calls = 0;
-    sets.SetPersistedSets("{}");
+    sets.SetPersistedSetsAndOnSiteDataCleared("{}", callback);
     sets.SetManuallySpecifiedSet("");
-    sets.SetOnSiteDataCleared(callback);
     EXPECT_EQ(callback_calls, 0);
   }
   // manual sets not ready.
@@ -400,8 +399,7 @@
     FirstPartySets sets(true);
     callback_calls = 0;
     SetComponentSets(sets, "[]");
-    sets.SetPersistedSets("{}");
-    sets.SetOnSiteDataCleared(callback);
+    sets.SetPersistedSetsAndOnSiteDataCleared("{}", callback);
     env().RunUntilIdle();
     EXPECT_EQ(callback_calls, 0);
   }
@@ -411,17 +409,6 @@
     callback_calls = 0;
     SetComponentSets(sets, "[]");
     sets.SetManuallySpecifiedSet("");
-    sets.SetOnSiteDataCleared(callback);
-    env().RunUntilIdle();
-    EXPECT_EQ(callback_calls, 0);
-  }
-  // callback not set.
-  {
-    FirstPartySets sets(true);
-    callback_calls = 0;
-    SetComponentSets(sets, "[]");
-    sets.SetManuallySpecifiedSet("");
-    sets.SetPersistedSets("{}");
     env().RunUntilIdle();
     EXPECT_EQ(callback_calls, 0);
   }
@@ -434,10 +421,9 @@
   SetComponentSets(R"({"owner": "https://example.test", "members": )"
                    R"(["https://member1.test"]})");
   sets().SetManuallySpecifiedSet("https://example2.test,https://member2.test");
-  sets().SetPersistedSets(
+  sets().SetPersistedSetsAndOnSiteDataCleared(
       R"({"https://example.test":"https://example.test",
-            "https://member1.test":"https://example.test"})");
-  sets().SetOnSiteDataCleared(
+            "https://member1.test":"https://example.test"})",
       base::BindLambdaForTesting([&](const std::string& got) {
         EXPECT_EQ(got, R"({"https://member1.test":"https://example.test",)"
                        R"("https://member2.test":"https://example2.test"})");
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 1d3b21d..897ec99 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -805,8 +805,8 @@
     const std::string& persisted_sets,
     mojom::NetworkService::SetPersistedFirstPartySetsAndGetCurrentSetsCallback
         callback) {
-  first_party_sets_->SetPersistedSets(persisted_sets);
-  first_party_sets_->SetOnSiteDataCleared(std::move(callback));
+  first_party_sets_->SetPersistedSetsAndOnSiteDataCleared(persisted_sets,
+                                                          std::move(callback));
 }
 
 void NetworkService::SetExplicitlyAllowedPorts(
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index a7a9576..914b0d1 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1778,7 +1778,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R101-14541.0.0",
+        "cros_img": "atlas-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_ATLAS_LKGM",
         "resultdb": {
           "enable": true,
@@ -1838,7 +1838,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R101-14541.0.0",
+        "cros_img": "eve-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_EVE_LKGM",
         "resultdb": {
           "enable": true,
@@ -1943,7 +1943,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -1958,7 +1958,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_HANA_LKGM",
         "resultdb": {
           "enable": true,
@@ -1973,7 +1973,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "ozone_unittests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -1987,7 +1987,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "ozone_unittests_HANA_LKGM",
         "resultdb": {
           "enable": true,
@@ -2001,7 +2001,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "viz_unittests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -2015,7 +2015,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "viz_unittests_HANA_LKGM",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index b0a555c..8791948d 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env vpython3
 # Copyright 2016 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.
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index e8d237b7f..a9bc48f 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1125,7 +1125,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R101-14541.0.0",
+        "cros_img": "octopus-release/R101-14542.0.0",
         "name": "lacros_fyi_tast_tests_OCTOPUS_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1169,7 +1169,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R101-14541.0.0",
+        "cros_img": "octopus-release/R101-14542.0.0",
         "name": "ozone_unittests_OCTOPUS_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1217,7 +1217,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_KEVIN_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1228,7 +1228,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "lacros_all_tast_tests_HANA_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1239,7 +1239,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "ozone_unittests_KEVIN_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1249,7 +1249,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "ozone_unittests_HANA_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1259,7 +1259,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R101-14541.0.0",
+        "cros_img": "kevin-release/R101-14542.0.0",
         "name": "viz_unittests_KEVIN_LKGM",
         "swarming": {},
         "test": "viz_unittests",
@@ -1269,7 +1269,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R101-14541.0.0",
+        "cros_img": "hana-release/R101-14542.0.0",
         "name": "viz_unittests_HANA_LKGM",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5d661b92..494bc20 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -732,7 +732,7 @@
     'skylab': {
       'cros_board': 'atlas',
       'cros_chrome_version': '101.0.4907.0',
-      'cros_img': 'atlas-release/R101-14541.0.0',
+      'cros_img': 'atlas-release/R101-14542.0.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_LKGM',
@@ -768,7 +768,7 @@
     'skylab': {
       'cros_board': 'eve',
       'cros_chrome_version': '101.0.4907.0',
-      'cros_img': 'eve-release/R101-14541.0.0',
+      'cros_img': 'eve-release/R101-14542.0.0',
     },
     'enabled': True,
     'identifier': 'EVE_LKGM',
@@ -804,7 +804,7 @@
     'skylab': {
       'cros_board': 'kevin',
       'cros_chrome_version': '101.0.4907.0',
-      'cros_img': 'kevin-release/R101-14541.0.0',
+      'cros_img': 'kevin-release/R101-14542.0.0',
     },
     'enabled': True,
     'identifier': 'KEVIN_LKGM',
@@ -813,7 +813,7 @@
     'skylab': {
       'cros_board': 'hana',
       'cros_chrome_version': '101.0.4907.0',
-      'cros_img': 'hana-release/R101-14541.0.0',
+      'cros_img': 'hana-release/R101-14542.0.0',
     },
     'enabled': True,
     'identifier': 'HANA_LKGM',
@@ -822,7 +822,7 @@
     'skylab': {
       'cros_board': 'octopus',
       'cros_chrome_version': '101.0.4907.0',
-      'cros_img': 'octopus-release/R101-14541.0.0',
+      'cros_img': 'octopus-release/R101-14542.0.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_LKGM',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 36eadec4..35559ff 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5228,6 +5228,33 @@
                     ]
                 },
                 {
+                    "name": "EightSecondWindow",
+                    "params": {
+                        "seconds": "8"
+                    },
+                    "enable_features": [
+                        "SlidingWindowForDroppedFrameCounter"
+                    ]
+                },
+                {
+                    "name": "TenSecondWindow",
+                    "params": {
+                        "seconds": "10"
+                    },
+                    "enable_features": [
+                        "SlidingWindowForDroppedFrameCounter"
+                    ]
+                },
+                {
+                    "name": "FifteenSecondWindow",
+                    "params": {
+                        "seconds": "15"
+                    },
+                    "enable_features": [
+                        "SlidingWindowForDroppedFrameCounter"
+                    ]
+                },
+                {
                     "name": "TwentySecondWindow",
                     "params": {
                         "seconds": "20"
diff --git a/third_party/blink/public/common/attribution_reporting/constants.h b/third_party/blink/public/common/attribution_reporting/constants.h
index a352117f..adc79b0 100644
--- a/third_party/blink/public/common/attribution_reporting/constants.h
+++ b/third_party/blink/public/common/attribution_reporting/constants.h
@@ -12,7 +12,9 @@
 constexpr size_t kMaxAttributionFiltersPerSource = 50;
 
 constexpr size_t kMaxBytesPerAttributionAggregatableKeyId = 25;
-constexpr size_t kMaxAttributionAggregatableKeysPerSource = 50;
+constexpr size_t kMaxAttributionAggregatableKeysPerSourceOrTrigger = 50;
+
+constexpr size_t kMaxAttributionAggregatableTriggerDataPerTrigger = 50;
 
 constexpr size_t kMaxAttributionEventTriggerData = 10;
 
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index e5e2bc9f..c52017a0 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -791,6 +791,7 @@
       ClientMetadataHttpNotFound
       ClientMetadataNoResponse
       ClientMetadataInvalidResponse
+      ClientMetadataMissingPrivacyPolicyUrl
       ErrorFetchingSignin
       InvalidSigninResponse
       AccountsHttpNotFound
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 8c39a9b..0bec7c5 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -204,7 +204,6 @@
     "webaudio/audio_context_manager.mojom",
     "webdatabase/web_database.mojom",
     "webid/federated_auth_request.mojom",
-    "webid/federated_auth_response.mojom",
     "webpreferences/web_preferences.mojom",
     "websockets/websocket_connector.mojom",
     "webtransport/web_transport_connector.mojom",
diff --git a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
index 9b4e164..8d0ac29d 100644
--- a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
+++ b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
@@ -31,6 +31,24 @@
   map<string, AttributionAggregatableKey> sources;
 };
 
+// Struct containing the trigger-side aggregatable data.
+struct AttributionAggregatableTriggerData {
+  AttributionAggregatableKey key;
+  array<string> source_keys;
+  AttributionFilterData filters;
+  AttributionFilterData not_filters;
+};
+
+// Struct containing all the aggregatable trigger data.
+struct AttributionAggregatableTrigger {
+  array<AttributionAggregatableTriggerData> trigger_data;
+};
+
+// Struct containing the aggregatable values.
+struct AttributionAggregatableValues {
+  map<string, uint32> values;
+};
+
 struct AttributionSourceData {
   // Target site where this source will be triggered.
   //
@@ -84,6 +102,14 @@
   // Key which allows deduplication against existing attributions for the same
   // source.
   AttributionTriggerDedupKey? dedup_key;
+
+  // If non-empty, this trigger will be ignored unless the attributed source's
+  // filter data matches.
+  AttributionFilterData filters;
+
+  // If non-empty, this trigger will be ignored unless the attributed source's
+  // filter data does *NOT* match.
+  AttributionFilterData not_filters;
 };
 
 // Represents a request from a reporting origin to trigger attribution on a
@@ -97,6 +123,17 @@
   // List of all event trigger data objects declared by the event trigger
   // header. This data is arbitrarily set by the reporting_origin.
   array<EventTriggerData> event_triggers;
+
+  // If non-empty, this trigger will be ignored unless the attributed source's
+  // filter data matches.
+  AttributionFilterData filters;
+
+  // Contains a list of trigger data that generates trigger-side aggregatable
+  // key pieces.
+  AttributionAggregatableTrigger aggregatable_trigger;
+
+  // Lists an amount of an abstract "value" to contribute to each key.
+  AttributionAggregatableValues aggregatable_values;
 };
 
 // Browser-process interface responsible for processing attribution
@@ -110,4 +147,4 @@
   // Called when trigger data from the renderer is available for a given
   // attributionsrc request.
   TriggerDataAvailable(AttributionTriggerData data);
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/public/mojom/devtools/inspector_issue.mojom b/third_party/blink/public/mojom/devtools/inspector_issue.mojom
index 70badc9..16176e33 100644
--- a/third_party/blink/public/mojom/devtools/inspector_issue.mojom
+++ b/third_party/blink/public/mojom/devtools/inspector_issue.mojom
@@ -191,6 +191,7 @@
   kErrorFetchingClientMetadataHttpNotFound,
   kErrorFetchingClientMetadataNoResponse,
   kErrorFetchingClientMetadataInvalidResponse,
+  kErrorClientMetadataMissingPrivacyPolicyUrl,
   kErrorFetchingAccountsHttpNotFound,
   kErrorFetchingAccountsNoResponse,
   kErrorFetchingAccountsInvalidResponse,
diff --git a/third_party/blink/public/mojom/webid/federated_auth_response.mojom b/third_party/blink/public/mojom/webid/federated_auth_response.mojom
deleted file mode 100644
index e8376d1..0000000
--- a/third_party/blink/public/mojom/webid/federated_auth_response.mojom
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-// Implementation of the proposed WebId API.
-//
-// Proposal: https://github.com/WICG/WebID
-
-enum ProvideIdTokenStatus {
-  kSuccess,
-  kErrorTooManyResponses,
-  kError,
-};
-
-
-// Creates a federated sign-in response.
-// This interface is called from the provider renderer process and is
-// implemented in the browser process.
-interface FederatedAuthResponse {
-  // Provides an IdToken that is passed to the pending request. This is meant to
-  // be used by the IDP generating the token. Empty string resolves the pending
-  // request with an error.
-  // Returns a status indicating if the the pending RP request was resolved.
-  ProvideIdToken(string id_token) => (ProvideIdTokenStatus status);
-};
-
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
index 02556e8..deb4e61 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
@@ -133,7 +133,8 @@
   }
 
   const auto* array = JSONArray::Cast(json.get());
-  if (!array || array->size() > kMaxAttributionAggregatableKeysPerSource) {
+  if (!array ||
+      array->size() > kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
     return ResponseParseResult<mojom::blink::AttributionAggregatableSources>(
         ResponseParseStatus::kInvalidFormat);
   }
@@ -305,10 +306,172 @@
       }
     }
 
+    event_trigger->filters = mojom::blink::AttributionFilterData::New();
+    if (!ParseAttributionFilterData(object_val->Get("filters"),
+                                    *event_trigger->filters)) {
+      return false;
+    }
+
+    event_trigger->not_filters = mojom::blink::AttributionFilterData::New();
+    if (!ParseAttributionFilterData(object_val->Get("not_filters"),
+                                    *event_trigger->not_filters)) {
+      return false;
+    }
+
     event_trigger_data.push_back(std::move(event_trigger));
   }
 
   return true;
 }
 
+bool ParseFilters(const AtomicString& json_string,
+                  mojom::blink::AttributionFilterData& filter_data) {
+  // TODO(apaseltiner): Consider applying a max stack depth to this.
+  std::unique_ptr<JSONValue> json = ParseJSON(json_string);
+  if (!json)
+    return false;
+
+  return ParseAttributionFilterData(json.get(), filter_data);
+}
+
+ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>
+ParseAttributionAggregatableTrigger(const AtomicString& json_string) {
+  if (json_string.IsNull()) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+        ResponseParseStatus::kNotFound);
+  }
+
+  std::unique_ptr<JSONValue> json = ParseJSON(json_string);
+  if (!json) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+        ResponseParseStatus::kParseError);
+  }
+
+  const auto* array = JSONArray::Cast(json.get());
+  if (!array ||
+      array->size() > kMaxAttributionAggregatableTriggerDataPerTrigger) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+        ResponseParseStatus::kInvalidFormat);
+  }
+
+  const wtf_size_t num_trigger_data = array->size();
+
+  auto trigger = mojom::blink::AttributionAggregatableTrigger::New();
+  trigger->trigger_data.ReserveInitialCapacity(num_trigger_data);
+
+  for (wtf_size_t i = 0; i < num_trigger_data; ++i) {
+    JSONValue* value = array->at(i);
+    DCHECK(value);
+
+    const auto* object = JSONObject::Cast(value);
+    if (!object) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    auto trigger_data = mojom::blink::AttributionAggregatableTriggerData::New();
+
+    trigger_data->key = ParseAttributionAggregatableKey(object);
+    if (!trigger_data->key) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    JSONArray* source_keys_val = object->GetArray("source_keys");
+    if (!source_keys_val ||
+        source_keys_val->size() >
+            kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    const wtf_size_t num_sources_keys = source_keys_val->size();
+    trigger_data->source_keys.ReserveInitialCapacity(num_sources_keys);
+
+    for (wtf_size_t j = 0; j < num_sources_keys; ++j) {
+      JSONValue* source_key_val = source_keys_val->at(j);
+      DCHECK(source_key_val);
+
+      String source_key;
+      if (!source_key_val->AsString(&source_key) ||
+          source_key.CharactersSizeInBytes() >
+              kMaxBytesPerAttributionAggregatableKeyId) {
+        return ResponseParseResult<
+            mojom::blink::AttributionAggregatableTrigger>(
+            ResponseParseStatus::kInvalidFormat);
+      }
+      trigger_data->source_keys.push_back(std::move(source_key));
+    }
+
+    trigger_data->filters = mojom::blink::AttributionFilterData::New();
+    if (!ParseAttributionFilterData(object->Get("filters"),
+                                    *trigger_data->filters)) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    trigger_data->not_filters = mojom::blink::AttributionFilterData::New();
+    if (!ParseAttributionFilterData(object->Get("not_filters"),
+                                    *trigger_data->not_filters)) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    trigger->trigger_data.push_back(std::move(trigger_data));
+  }
+
+  return ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>(
+      ResponseParseStatus::kSuccess, std::move(trigger));
+}
+
+ResponseParseResult<mojom::blink::AttributionAggregatableValues>
+ParseAttributionAggregatableValues(const AtomicString& json_string) {
+  if (json_string.IsNull()) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+        ResponseParseStatus::kNotFound);
+  }
+
+  std::unique_ptr<JSONValue> json = ParseJSON(json_string);
+  if (!json) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+        ResponseParseStatus::kParseError);
+  }
+
+  const auto* object = JSONObject::Cast(json.get());
+  if (!object ||
+      object->size() > kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+    return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+        ResponseParseStatus::kInvalidFormat);
+  }
+
+  const wtf_size_t num_values = object->size();
+
+  auto values = mojom::blink::AttributionAggregatableValues::New();
+  values->values.ReserveCapacityForSize(num_values);
+
+  for (wtf_size_t i = 0; i < num_values; ++i) {
+    JSONObject::Entry entry = object->at(i);
+    String key_id = entry.first;
+    JSONValue* value = entry.second;
+    DCHECK(value);
+
+    if (key_id.CharactersSizeInBytes() >
+        kMaxBytesPerAttributionAggregatableKeyId) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    int key_value;
+    if (!value->AsInteger(&key_value) || key_value <= 0) {
+      return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+          ResponseParseStatus::kInvalidFormat);
+    }
+
+    values->values.insert(std::move(key_id), key_value);
+  }
+
+  return ResponseParseResult<mojom::blink::AttributionAggregatableValues>(
+      ResponseParseStatus::kSuccess, std::move(values));
+}
+
 }  // namespace blink::attribution_response_parsing
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.h b/third_party/blink/renderer/core/frame/attribution_response_parsing.h
index 128b636..6f8f57cb 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing.h
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.h
@@ -65,6 +65,38 @@
     const AtomicString& json_string,
     WTF::Vector<mojom::blink::EventTriggerDataPtr>& event_trigger_data);
 
+// Parses filter header of the form:
+//
+// {
+//   "abc": [],
+//   "xyz": ["123", "456"]
+// }
+//
+// Returns whether parsing was successful.
+CORE_EXPORT bool ParseFilters(const AtomicString& json_string,
+                              mojom::blink::AttributionFilterData& filter_data);
+
+// Example JSON schema:
+// [{
+//   "key_piece": "0x400",
+//   "source_keys": ["campaignCounts"]
+// },
+// {
+//   "key_piece": "0xA80",
+//   "source_keys": ["geoValue"]
+// }]
+
+CORE_EXPORT ResponseParseResult<mojom::blink::AttributionAggregatableTrigger>
+ParseAttributionAggregatableTrigger(const AtomicString& json_string);
+
+// Example JSON schema:
+// {
+//  "campaignCounts": 32768,
+//  "geoValue": 1664
+// }
+CORE_EXPORT ResponseParseResult<mojom::blink::AttributionAggregatableValues>
+ParseAttributionAggregatableValues(const AtomicString& json_string);
+
 }  // namespace blink::attribution_response_parsing
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_ATTRIBUTION_RESPONSE_PARSING_H_
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
index 3a0eb3d..a4a75d5 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
@@ -38,6 +38,45 @@
   mojom::blink::AttributionAggregatableSources sources_;
 };
 
+class AttributionFilterDataBuilder {
+ public:
+  AttributionFilterDataBuilder() = default;
+  ~AttributionFilterDataBuilder() = default;
+
+  AttributionFilterDataBuilder& AddFilter(String filter_name,
+                                          Vector<String> filter_values) {
+    filters_.filter_values.insert(std::move(filter_name),
+                                  std::move(filter_values));
+    return *this;
+  }
+
+  mojom::blink::AttributionFilterDataPtr Build() const {
+    return filters_.Clone();
+  }
+
+ private:
+  mojom::blink::AttributionFilterData filters_;
+};
+
+class AggregatableTriggerBuilder {
+ public:
+  AggregatableTriggerBuilder() = default;
+  ~AggregatableTriggerBuilder() = default;
+
+  AggregatableTriggerBuilder& AddTriggerData(
+      mojom::blink::AttributionAggregatableTriggerDataPtr trigger) {
+    trigger_.trigger_data.push_back(std::move(trigger));
+    return *this;
+  }
+
+  mojom::blink::AttributionAggregatableTriggerPtr Build() const {
+    return trigger_.Clone();
+  }
+
+ private:
+  mojom::blink::AttributionAggregatableTrigger trigger_;
+};
+
 }  // namespace
 
 TEST(AttributionResponseParsingTest, ParseAttributionAggregatableSources) {
@@ -134,8 +173,8 @@
    private:
     String GetKey(wtf_size_t index) const {
       // Note that this might not be robust as
-      // `blink::kMaxAttributionAggregatableKeysPerSource` varies which might
-      // generate invalid JSON.
+      // `blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger` varies which
+      // might generate invalid JSON.
       return String(
           std::string(key_size, 'A' + index % 26 + 32 * (index / 26)));
     }
@@ -143,9 +182,10 @@
 
   const AttributionAggregatableSourcesSizeTestCase kTestCases[] = {
       {"empty", true, 0, 0},
-      {"max_keys", true, blink::kMaxAttributionAggregatableKeysPerSource, 1},
+      {"max_keys", true,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
       {"too_many_keys", false,
-       blink::kMaxAttributionAggregatableKeysPerSource + 1, 1},
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
       {"max_key_size", true, 1,
        blink::kMaxBytesPerAttributionAggregatableKeyId},
       {"excessive_key_size", false, 1,
@@ -162,4 +202,264 @@
   }
 }
 
+TEST(AttributionResponseParsingTest, ParseAttributionAggregatableTrigger) {
+  const struct {
+    String description;
+    AtomicString header;
+    ResponseParseStatus status;
+    mojom::blink::AttributionAggregatableTriggerPtr value;
+  } kTestCases[] = {
+      {"No header", AtomicString(), ResponseParseStatus::kNotFound,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Empty header", "", ResponseParseStatus::kParseError,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Invalid JSON", "{", ResponseParseStatus::kParseError,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Missing source_keys field", R"([{"key_piece":"0x400"}])",
+       ResponseParseStatus::kInvalidFormat,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Missing key_piece field", R"([{"source_keys":["key"]}])",
+       ResponseParseStatus::kInvalidFormat,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Invalid key", R"([{"key_piece":"0xG00","source_keys":["key"]}])",
+       ResponseParseStatus::kInvalidFormat,
+       mojom::blink::AttributionAggregatableTrigger::New()},
+      {"Valid trigger", R"([{"key_piece":"0x400","source_keys":["key"]}])",
+       ResponseParseStatus::kSuccess,
+       AggregatableTriggerBuilder()
+           .AddTriggerData(
+               mojom::blink::AttributionAggregatableTriggerData::New(
+                   mojom::blink::AttributionAggregatableKey::New(
+                       /*high_bits=*/0, /*low_bits=*/1024),
+                   /*source_keys=*/Vector<String>{"key"},
+                   /*filters=*/mojom::blink::AttributionFilterData::New(),
+                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+           .Build()},
+      {"Valid trigger with filters",
+       AtomicString(R"([{"key_piece":"0x400","source_keys":["key"],)") +
+           R"("filters":{"filter":["value1"]},"not_filters":{"filter":["value2"]}}])",
+       ResponseParseStatus::kSuccess,
+       AggregatableTriggerBuilder()
+           .AddTriggerData(
+               mojom::blink::AttributionAggregatableTriggerData::New(
+                   mojom::blink::AttributionAggregatableKey::New(
+                       /*high_bits=*/0, /*low_bits=*/1024),
+                   /*source_keys=*/Vector<String>{"key"},
+                   /*filters=*/
+                   AttributionFilterDataBuilder()
+                       .AddFilter("filter", Vector<String>{"value1"})
+                       .Build(),
+                   /*not_filters=*/
+                   AttributionFilterDataBuilder()
+                       .AddFilter("filter", Vector<String>{"value2"})
+                       .Build()))
+           .Build()},
+      {"Two valid trigger data",
+       AtomicString(R"([{"key_piece":"0x400","source_keys":["key1"]},)") +
+           R"({"key_piece":"0xA80","source_keys":["key2"]}])",
+       ResponseParseStatus::kSuccess,
+       AggregatableTriggerBuilder()
+           .AddTriggerData(
+               mojom::blink::AttributionAggregatableTriggerData::New(
+                   mojom::blink::AttributionAggregatableKey::New(
+                       /*high_bits=*/0, /*low_bits=*/1024),
+                   /*source_keys=*/Vector<String>{"key1"},
+                   /*filters=*/mojom::blink::AttributionFilterData::New(),
+                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+           .AddTriggerData(
+               mojom::blink::AttributionAggregatableTriggerData::New(
+                   mojom::blink::AttributionAggregatableKey::New(
+                       /*high_bits=*/0, /*low_bits=*/2688),
+                   /*source_keys=*/Vector<String>{"key2"},
+                   /*filters=*/mojom::blink::AttributionFilterData::New(),
+                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+           .Build()},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    auto result = ParseAttributionAggregatableTrigger(test_case.header);
+    EXPECT_EQ(test_case.status, result.status) << test_case.description;
+    EXPECT_EQ(test_case.value, result.value) << test_case.description;
+  }
+}
+
+TEST(AttributionResponseParsingTest,
+     ParseAttributionAggregatableTrigger_CheckSize) {
+  struct AttributionAggregatableTriggerSizeTestCase {
+    String description;
+    bool valid;
+    wtf_size_t trigger_data_count;
+    wtf_size_t key_count;
+    wtf_size_t key_size;
+
+    AtomicString GetHeader() const {
+      StringBuilder builder;
+      String key = GetKey();
+
+      const char* separator = "";
+      for (wtf_size_t i = 0u; i < trigger_data_count; ++i) {
+        builder.Append(separator);
+        builder.Append("{\"key_piece\":\"0x1\",\"source_keys\":[");
+
+        const char* sub_separator = "";
+        for (wtf_size_t j = 0u; j < key_count; ++j) {
+          builder.Append(sub_separator);
+          builder.Append("\"");
+          builder.Append(key);
+          builder.Append("\"");
+          sub_separator = ",";
+        }
+
+        builder.Append("]}");
+        separator = ",";
+      }
+
+      return "[" + builder.ToAtomicString() + "]";
+    }
+
+    mojom::blink::AttributionAggregatableTriggerPtr GetValue() const {
+      AggregatableTriggerBuilder builder;
+      if (!valid)
+        return builder.Build();
+
+      for (wtf_size_t i = 0u; i < trigger_data_count; ++i) {
+        builder.AddTriggerData(
+            mojom::blink::AttributionAggregatableTriggerData::New(
+                mojom::blink::AttributionAggregatableKey::New(
+                    /*high_bits=*/0, /*low_bits=*/1),
+                /*source_keys=*/Vector<String>(key_count, GetKey()),
+                /*filters=*/mojom::blink::AttributionFilterData::New(),
+                /*not_filters=*/mojom::blink::AttributionFilterData::New()));
+      }
+      return builder.Build();
+    }
+
+   private:
+    String GetKey() const { return String(std::string(key_size, 'A')); }
+  };
+
+  const AttributionAggregatableTriggerSizeTestCase kTestCases[] = {
+      {"empty", true, 0, 0, 0},
+      {"max_trigger_data", true,
+       blink::kMaxAttributionAggregatableTriggerDataPerTrigger, 0, 0},
+      {"too_many_trigger_data", false,
+       blink::kMaxAttributionAggregatableTriggerDataPerTrigger + 1, 0, 0},
+      {"max_key_count", true, 1,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 0},
+      {"too many keys", false, 1,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 0},
+      {"max_key_size", true, 1, 1, kMaxBytesPerAttributionAggregatableKeyId},
+      {"excessive_key_size", false, 1, 1,
+       kMaxBytesPerAttributionAggregatableKeyId + 1},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    auto result = ParseAttributionAggregatableTrigger(test_case.GetHeader());
+
+    EXPECT_EQ(result.status, test_case.valid
+                                 ? ResponseParseStatus::kSuccess
+                                 : ResponseParseStatus::kInvalidFormat)
+        << test_case.description;
+    EXPECT_EQ(result.value, test_case.GetValue()) << test_case.description;
+  }
+}
+
+TEST(AttributionResponseParsingTest, ParseAttributionAggregatableValues) {
+  const struct {
+    AtomicString description;
+    AtomicString header;
+    ResponseParseStatus status;
+    mojom::blink::AttributionAggregatableValuesPtr value;
+  } kTestCases[] = {
+      {"No header", AtomicString(), ResponseParseStatus::kNotFound,
+       mojom::blink::AttributionAggregatableValues::New()},
+      {"Empty header", "", ResponseParseStatus::kParseError,
+       mojom::blink::AttributionAggregatableValues::New()},
+      {"Invalid JSON", "{", ResponseParseStatus::kParseError,
+       mojom::blink::AttributionAggregatableValues::New()},
+      {"Invalid value", R"({"key":-1})", ResponseParseStatus::kInvalidFormat,
+       mojom::blink::AttributionAggregatableValues::New()},
+      {"Valid value", R"({"key":123})", ResponseParseStatus::kSuccess,
+       mojom::blink::AttributionAggregatableValues::New(
+           HashMap<String, uint32_t>{{"key", 123}})},
+      {"Two valid values", R"({"key1":123,"key2":456})",
+       ResponseParseStatus::kSuccess,
+       mojom::blink::AttributionAggregatableValues::New(
+           HashMap<String, uint32_t>{{"key1", 123}, {"key2", 456}})},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    auto result = ParseAttributionAggregatableValues(test_case.header);
+    EXPECT_EQ(test_case.status, result.status) << test_case.description;
+    EXPECT_EQ(test_case.value, result.value) << test_case.description;
+  }
+}
+
+TEST(AttributionResponseParsingTest,
+     ParseAttributionAggregatableValues_CheckSize) {
+  struct AttributionAggregatableValuesSizeTestCase {
+    String description;
+    bool valid;
+    wtf_size_t key_count;
+    wtf_size_t key_size;
+
+    AtomicString GetHeader() const {
+      StringBuilder builder;
+      const char* separator = "";
+      for (wtf_size_t i = 0u; i < key_count; ++i) {
+        builder.Append(separator);
+        builder.Append("\"");
+        builder.Append(GetKey(i));
+        builder.Append("\":");
+        builder.AppendNumber(i + 1);
+        separator = ",";
+      }
+      return "{" + builder.ToAtomicString() + "}";
+    }
+
+    mojom::blink::AttributionAggregatableValuesPtr GetValue() const {
+      auto aggregatable_values =
+          mojom::blink::AttributionAggregatableValues::New();
+      if (!valid)
+        return aggregatable_values;
+
+      for (wtf_size_t i = 0u; i < key_count; ++i) {
+        aggregatable_values->values.insert(GetKey(i), i + 1);
+      }
+      return aggregatable_values;
+    }
+
+   private:
+    String GetKey(wtf_size_t index) const {
+      // Note that this might not be robust as
+      // `blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger` varies which
+      // might generate invalid JSON characters.
+      return String(
+          std::string(key_size, 'A' + index % 26 + 32 * (index / 26)));
+    }
+  };
+
+  const AttributionAggregatableValuesSizeTestCase kTestCases[] = {
+      {"empty", true, 0, 0},
+      {"max_keys", true,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
+      {"too_many_keys", false,
+       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
+      {"max_key_size", true, 1,
+       blink::kMaxBytesPerAttributionAggregatableKeyId},
+      {"excessive_key_size", false, 1,
+       blink::kMaxBytesPerAttributionAggregatableKeyId + 1},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    auto result = ParseAttributionAggregatableValues(test_case.GetHeader());
+
+    EXPECT_EQ(result.status, test_case.valid
+                                 ? ResponseParseStatus::kSuccess
+                                 : ResponseParseStatus::kInvalidFormat)
+        << test_case.description;
+    EXPECT_EQ(result.value, test_case.GetValue()) << test_case.description;
+  }
+}
+
 }  // namespace blink::attribution_response_parsing
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 7897122..4732058e 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -175,7 +175,12 @@
       context.type == AttributionSrcType::kUndetermined ||
       context.type == AttributionSrcType::kTrigger;
   if (can_process_trigger &&
-      headers.Contains(http_names::kAttributionReportingRegisterEventTrigger)) {
+      (headers.Contains(
+           http_names::kAttributionReportingRegisterEventTrigger) ||
+       (headers.Contains(
+            http_names::kAttributionReportingRegisterAggregatableTriggerData) &&
+        headers.Contains(
+            http_names::kAttributionReportingRegisterAggregatableValues)))) {
     context.type = AttributionSrcType::kTrigger;
     HandleTriggerRegistration(resource, response, context);
   }
@@ -237,12 +242,42 @@
       SecurityOrigin::Create(response.CurrentRequestUrl());
 
   // Populate event triggers.
-  bool success = attribution_response_parsing::ParseEventTriggerData(
-      response.HttpHeaderField(
-          http_names::kAttributionReportingRegisterEventTrigger),
-      trigger_data->event_triggers);
-  if (!success)
+  const AtomicString& event_triggers_json = response.HttpHeaderField(
+      http_names::kAttributionReportingRegisterEventTrigger);
+  if (!event_triggers_json.IsNull() &&
+      !attribution_response_parsing::ParseEventTriggerData(
+          event_triggers_json, trigger_data->event_triggers)) {
     return;
+  }
+
+  const AtomicString& aggregatable_trigger_json = response.HttpHeaderField(
+      http_names::kAttributionReportingRegisterAggregatableTriggerData);
+  auto aggregatable_trigger =
+      attribution_response_parsing::ParseAttributionAggregatableTrigger(
+          aggregatable_trigger_json);
+  if (IsResponseParseError(aggregatable_trigger.status))
+    return;
+
+  trigger_data->aggregatable_trigger = std::move(aggregatable_trigger.value);
+
+  const AtomicString& aggregatable_values_json = response.HttpHeaderField(
+      http_names::kAttributionReportingRegisterAggregatableValues);
+  auto aggregatable_values =
+      attribution_response_parsing::ParseAttributionAggregatableValues(
+          aggregatable_values_json);
+  if (IsResponseParseError(aggregatable_values.status))
+    return;
+
+  trigger_data->aggregatable_values = std::move(aggregatable_values.value);
+
+  trigger_data->filters = mojom::blink::AttributionFilterData::New();
+
+  const AtomicString& filter_json =
+      response.HttpHeaderField(http_names::kAttributionReportingFilters);
+  if (!filter_json.IsNull() && !attribution_response_parsing::ParseFilters(
+                                   filter_json, *trigger_data->filters)) {
+    return;
+  }
 
   context.data_host->TriggerDataAvailable(std::move(trigger_data));
 }
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
index 39b33ce..adc6d86 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
@@ -3778,59 +3778,55 @@
   };
 }
 
-/**
- * @constructor
- * @extends ListCell
- */
-function WeekNumberCell() {
-  ListCell.call(this);
-  this.element.classList.add(WeekNumberCell.ClassNameWeekNumberCell);
-  this.element.style.width =
-      (WeekNumberCell.Width - WeekNumberCell.SeparatorWidth) + 'px';
-  this.element.style.height = WeekNumberCell.GetHeight() + 'px';
-  this.element.style.lineHeight =
-      (WeekNumberCell.GetHeight() - WeekNumberCell.PaddingSize * 2) + 'px';
-  /**
-   * @type {?Week}
-   */
-  this.week = null;
-};
+// ----------------------------------------------------------------
 
-{
-  WeekNumberCell.prototype = Object.create(ListCell.prototype);
+class WeekNumberCell extends ListCell {
+  constructor() {
+    super();
+    this.element.classList.add(WeekNumberCell.ClassNameWeekNumberCell);
+    this.element.style.width =
+        (WeekNumberCell.Width - WeekNumberCell.SeparatorWidth) + 'px';
+    this.element.style.height = WeekNumberCell.GetHeight() + 'px';
+    this.element.style.lineHeight =
+        (WeekNumberCell.GetHeight() - WeekNumberCell.PaddingSize * 2) + 'px';
+    /**
+     * @type {?Week}
+     */
+    this.week = null;
+  }
 
-  WeekNumberCell.Width = 48;
-  WeekNumberCell._Height = DayCell._Height;
-  WeekNumberCell.GetHeight = function() {
+  static Width = 48;
+  static _Height = DayCell._Height;
+  static GetHeight() {
     return WeekNumberCell._Height;
-  };
-  WeekNumberCell.SeparatorWidth = 1;
-  WeekNumberCell.PaddingSize = 1;
-  WeekNumberCell.ClassNameWeekNumberCell = 'week-number-cell';
-  WeekNumberCell.ClassNameHighlighted = 'highlighted';
-  WeekNumberCell.ClassNameDisabled = 'disabled';
+  }
+  static SeparatorWidth = 1;
+  static PaddingSize = 1;
+  static ClassNameWeekNumberCell = 'week-number-cell';
+  static ClassNameHighlighted = 'highlighted';
+  static ClassNameDisabled = 'disabled';
 
-  WeekNumberCell._recycleBin = [];
+  static _recycleBin = [];
 
   /**
    * @return {!Array}
    * @override
    */
-  WeekNumberCell.prototype._recycleBin = function() {
+  _recycleBin() {
     return WeekNumberCell._recycleBin;
-  };
+  }
 
   /**
    * @return {!WeekNumberCell}
    */
-  WeekNumberCell.recycleOrCreate = function() {
+  static recycleOrCreate() {
     return WeekNumberCell._recycleBin.pop() || new WeekNumberCell();
-  };
+  }
 
   /**
    * @param {!Week} week
    */
-  WeekNumberCell.prototype.reset = function(week) {
+  reset(week) {
     this.week = week;
     this.element.id = week.toString();
     this.element.setAttribute('role', 'gridcell');
@@ -3840,17 +3836,17 @@
             week.year, week.week, week.firstDay().format()));
     this.element.textContent = localizeNumber(this.week.week.toString());
     this.show();
-  };
+  }
 
   /**
    * @override
    */
-  WeekNumberCell.prototype.throwAway = function() {
-    ListCell.prototype.throwAway.call(this);
+  throwAway() {
+    super.throwAway();
     this.week = null;
-  };
+  }
 
-  WeekNumberCell.prototype.setHighlighted = function(highlighted) {
+  setHighlighted(highlighted) {
     if (highlighted) {
       this.element.classList.add(WeekNumberCell.ClassNameHighlighted);
       this.element.setAttribute('aria-selected', 'true');
@@ -3858,16 +3854,18 @@
       this.element.classList.remove(WeekNumberCell.ClassNameHighlighted);
       this.element.setAttribute('aria-selected', 'false');
     }
-  };
+  }
 
-  WeekNumberCell.prototype.setDisabled = function(disabled) {
+  setDisabled(disabled) {
     if (disabled)
       this.element.classList.add(WeekNumberCell.ClassNameDisabled);
     else
       this.element.classList.remove(WeekNumberCell.ClassNameDisabled);
-  };
+  }
 }
 
+// ----------------------------------------------------------------
+
 /**
  * @constructor
  * @extends View
@@ -3905,60 +3903,56 @@
   };
 }
 
-/**
- * @constructor
- * @extends ListCell
- */
-function CalendarRowCell() {
-  ListCell.call(this);
+// ----------------------------------------------------------------
 
-  this.element.classList.add(CalendarRowCell.ClassNameCalendarRowCell);
-  if (global.params.weekStartDay === WeekDay.Sunday) {
-    this.element.classList.add(CalendarRowCell.ClassNameWeekStartsOnSunday);
+class CalendarRowCell extends ListCell {
+  constructor() {
+    super();
+
+    this.element.classList.add(CalendarRowCell.ClassNameCalendarRowCell);
+    if (global.params.weekStartDay === WeekDay.Sunday) {
+      this.element.classList.add(CalendarRowCell.ClassNameWeekStartsOnSunday);
+    }
+    this.element.style.height = CalendarRowCell.GetHeight() + 'px';
+    this.element.setAttribute('role', 'row');
+
+    /**
+     * @type {!Array}
+     * @protected
+     */
+    this._dayCells = [];
+    /**
+     * @type {!number}
+     */
+    this.row = 0;
+    /**
+     * @type {?CalendarTableView}
+     */
+    this.calendarTableView = null;
   }
-  this.element.style.height = CalendarRowCell.GetHeight() + 'px';
-  this.element.setAttribute('role', 'row');
 
-  /**
-   * @type {!Array}
-   * @protected
-   */
-  this._dayCells = [];
-  /**
-   * @type {!number}
-   */
-  this.row = 0;
-  /**
-   * @type {?CalendarTableView}
-   */
-  this.calendarTableView = null;
-}
-
-{
-  CalendarRowCell.prototype = Object.create(ListCell.prototype);
-
-  CalendarRowCell._Height = DayCell._Height;
-  CalendarRowCell.GetHeight = function() {
+  static _Height = DayCell._Height;
+  static GetHeight() {
     return CalendarRowCell._Height;
-  };
-  CalendarRowCell.ClassNameCalendarRowCell = 'calendar-row-cell';
-  CalendarRowCell.ClassNameWeekStartsOnSunday = 'week-starts-on-sunday';
+  }
+  static ClassNameCalendarRowCell = 'calendar-row-cell';
+  static ClassNameWeekStartsOnSunday = 'week-starts-on-sunday';
 
-  CalendarRowCell._recycleBin = [];
+  static _recycleBin = [];
 
   /**
    * @return {!Array}
    * @override
    */
-  CalendarRowCell.prototype._recycleBin = function() {
+  _recycleBin() {
     return CalendarRowCell._recycleBin;
-  };
+  }
 
   /**
    * @param {!number} row
    * @param {!CalendarTableView} calendarTableView
    */
-  CalendarRowCell.prototype.reset = function(row, calendarTableView) {
+  reset(row, calendarTableView) {
     this.row = row;
     this.calendarTableView = calendarTableView;
     if (this.calendarTableView.hasWeekNumberColumn) {
@@ -3976,19 +3970,19 @@
       day = day.next();
     }
     this.show();
-  };
+  }
 
   /**
    * @override
    */
-  CalendarRowCell.prototype.throwAway = function() {
-    ListCell.prototype.throwAway.call(this);
+  throwAway() {
+    super.throwAway();
     if (this.weekNumberCell)
       this.calendarTableView.throwAwayWeekNumberCell(this.weekNumberCell);
     this._dayCells.forEach(
         this.calendarTableView.throwAwayDayCell, this.calendarTableView);
     this._dayCells.length = 0;
-  };
+  }
 }
 
 // ----------------------------------------------------------------
@@ -5031,3 +5025,4 @@
 window.Day = Day;
 window.Month = Month;
 window.Week = Week;
+window.WeekNumberCell = WeekNumberCell;
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 8d5ac4e..36b0cb84 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -514,7 +514,6 @@
 
 bool SVGImage::ApplyShader(cc::PaintFlags& flags,
                            const SkMatrix& local_matrix,
-                           const gfx::RectF& dst_rect,
                            const gfx::RectF& src_rect,
                            const ImageDrawOptions& draw_options) {
   const DrawInfo draw_info(gfx::SizeF(intrinsic_size_), 1, NullURL(),
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.h b/third_party/blink/renderer/core/svg/graphics/svg_image.h
index 5a7239c3..ba796d31 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.h
@@ -197,7 +197,6 @@
                     const gfx::RectF& unzoomed_src_rect);
   bool ApplyShader(cc::PaintFlags&,
                    const SkMatrix& local_matrix,
-                   const gfx::RectF& dst_rect,
                    const gfx::RectF& src_rect,
                    const ImageDrawOptions&) override;
   bool ApplyShaderForContainer(const DrawInfo&,
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.cc
index 6aaa42c..865c489 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.cc
@@ -64,7 +64,6 @@
 
 bool SVGImageForContainer::ApplyShader(cc::PaintFlags& flags,
                                        const SkMatrix& local_matrix,
-                                       const gfx::RectF& dst_rect,
                                        const gfx::RectF& src_rect,
                                        const ImageDrawOptions& draw_options) {
   const SVGImage::DrawInfo draw_info(container_size_, zoom_, url_,
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
index d3e07c3..7fc79c1c 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
@@ -77,7 +77,6 @@
 
   bool ApplyShader(cc::PaintFlags&,
                    const SkMatrix& local_matrix,
-                   const gfx::RectF& dst_rect,
                    const gfx::RectF& src_rect,
                    const ImageDrawOptions& draw_options) override;
 
diff --git a/third_party/blink/renderer/modules/autofill_assistant/node_signals.cc b/third_party/blink/renderer/modules/autofill_assistant/node_signals.cc
index 78837d0..4b10944 100644
--- a/third_party/blink/renderer/modules/autofill_assistant/node_signals.cc
+++ b/third_party/blink/renderer/modules/autofill_assistant/node_signals.cc
@@ -319,9 +319,83 @@
   AddStringIfNotNullOrEmpty(GetLabelFromGeometry(element), &features->text);
 }
 
+void AddFirstLegendOfFieldset(Node* node, WebVector<WebString>* header_text) {
+  Element* fieldset = DynamicTo<Element>(node);
+  if (!IsVisible(*fieldset)) {
+    return;
+  }
+  for (Element& legend : ElementTraversal::DescendantsOf(*fieldset)) {
+    // Add the first legend only.
+    if (legend.HasTagName(html_names::kLegendTag) &&
+        AddStringIfNotNullOrEmpty(legend.innerText(), header_text)) {
+      return;
+    }
+  }
+}
+
+// Add text signals when the |element| is a descendant of one or more
+// fieldset(s).
+// Example: <fieldset><legend>Legend</legend><input/><fieldset>
+void GetFieldsetLegends(const Element& element,
+                        AutofillAssistantContextFeatures* features) {
+  Node* document = element.ownerDocument();
+  Node* node = element.parentNode();
+  while (node != document) {
+    if (!node) {
+      break;
+    }
+    if (node->IsElementNode() && node->HasTagName(html_names::kFormTag)) {
+      break;
+    }
+    if (node->IsElementNode() && node->HasTagName(html_names::kFieldsetTag)) {
+      AddFirstLegendOfFieldset(node, &features->header_text);
+    }
+    node = node->parentNode();
+  }
+}
+
+bool IsHeader(const Element& element) {
+  return element.HasTagName(html_names::kH1Tag) ||
+         element.HasTagName(html_names::kH2Tag) ||
+         element.HasTagName(html_names::kH3Tag) ||
+         element.HasTagName(html_names::kH4Tag) ||
+         element.HasTagName(html_names::kH4Tag) ||
+         element.HasTagName(html_names::kH5Tag);
+}
+
+void AddHeaders(const Element& element,
+                AutofillAssistantContextFeatures* features) {
+  WebVector<Element*> headers_before;
+  for (Element& node :
+       ElementTraversal::DescendantsOf(*element.ownerDocument())) {
+    if (&node == &element) {
+      break;
+    }
+    if (IsHeader(node) && IsVisible(node)) {
+      headers_before.emplace_back(&node);
+    }
+  }
+  for (int i = static_cast<int>(headers_before.size()) - 1; i >= 0; --i) {
+    Element* header = headers_before[i];
+    // Demand header to be above the element or to have an overlap in
+    // y-coordinates. I.e. top (Y) of header must be higher (<) than bottom
+    // of element.
+    if (header->BoundingBox().Y() >= element.BoundingBox().Bottom()) {
+      continue;
+    }
+    // Only use the header which is the lowest in hierarchy.
+    if (AddStringIfNotNullOrEmpty(header->innerText(),
+                                  &features->header_text)) {
+      break;
+    }
+  }
+}
+
 void CollectContextFeatures(const Element& element,
                             AutofillAssistantContextFeatures* features) {
-  // TODO(b/204839535): Implement
+  GetFieldsetLegends(element, features);
+  AddHeaders(element, features);
+  // TODO(b/204839535): Implement form type.
 }
 
 void CollectSignalsForNode(
diff --git a/third_party/blink/renderer/modules/autofill_assistant/node_signals_test.cc b/third_party/blink/renderer/modules/autofill_assistant/node_signals_test.cc
index 7a977b9..00dfa5c7 100644
--- a/third_party/blink/renderer/modules/autofill_assistant/node_signals_test.cc
+++ b/third_party/blink/renderer/modules/autofill_assistant/node_signals_test.cc
@@ -231,43 +231,186 @@
   EXPECT_EQ(results[0].label_features.text[1], "Name");  // From Aria
 }
 
-TEST_F(NodeSignalsTest, GetLabelFromGeometry) {
+TEST_F(NodeSignalsTest, GetLabelFromGeometryWithNestedElements) {
   // Make sure nested elements don't get added multiple times.
   // Make sure elements that are not "pure text" get added.
   SetBodyContent(R"(
     <div><div>Label <i class="icon"></i></div></div>
     <div><input></div>)");
 
-  WebVector<AutofillAssistantNodeSignals> results_1 =
+  WebVector<AutofillAssistantNodeSignals> results =
       GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
-  ASSERT_EQ(results_1.size(), 1u);
+  ASSERT_EQ(results.size(), 1u);
 
-  ASSERT_EQ(results_1[0].label_features.text.size(), 1u);
-  EXPECT_EQ(results_1[0].label_features.text[0], "Label");
+  ASSERT_EQ(results[0].label_features.text.size(), 1u);
+  EXPECT_EQ(results[0].label_features.text[0], "Label");
+}
 
+TEST_F(NodeSignalsTest,
+       GetLabelFromGeometryWithNestedElementsClosestLabelOnly) {
   // Make sure for nested elements only the "closest" gets added.
   SetBodyContent(R"(
     <div>Label <div>Sublabel</div></div>
     <div><input></div>)");
 
-  WebVector<AutofillAssistantNodeSignals> results_2 =
+  WebVector<AutofillAssistantNodeSignals> results =
       GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
-  ASSERT_EQ(results_2.size(), 1u);
+  ASSERT_EQ(results.size(), 1u);
 
-  ASSERT_EQ(results_2[0].label_features.text.size(), 1u);
-  EXPECT_EQ(results_2[0].label_features.text[0], "Sublabel");
+  ASSERT_EQ(results[0].label_features.text.size(), 1u);
+  EXPECT_EQ(results[0].label_features.text[0], "Sublabel");
+}
 
-  // Make sure for all inline elements get added.
+TEST_F(NodeSignalsTest, GetLabelFromGeometryAddInlineElements) {
   SetBodyContent(R"(
     <div>Name <i>First</i></div>
     <div><input></div>)");
 
-  WebVector<AutofillAssistantNodeSignals> results_3 =
+  WebVector<AutofillAssistantNodeSignals> results =
       GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
-  ASSERT_EQ(results_3.size(), 1u);
+  ASSERT_EQ(results.size(), 1u);
 
-  ASSERT_EQ(results_3[0].label_features.text.size(), 1u);
-  EXPECT_EQ(results_3[0].label_features.text[0], "Name  First");
+  ASSERT_EQ(results[0].label_features.text.size(), 1u);
+  EXPECT_EQ(results[0].label_features.text[0], "Name  First");
+}
+
+TEST_F(NodeSignalsTest, AddFieldsetLegendInsideForm) {
+  SetBodyContent(R"(
+    <fieldset>
+      <legend>Outside</legend>
+      <form>
+        <fieldset>
+          <legend>Inside</legend>
+          <input>
+        </fieldset>
+      </form>
+    </fieldset>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 1u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "Inside");
+}
+
+TEST_F(NodeSignalsTest, AddFieldsetLegendFromParentNodesOnly) {
+  SetBodyContent(R"(
+    <fieldset>
+      <legend>Unrelated</legend>
+    </fieldset>
+    <fieldset>
+      <legend>Related</legend>
+      <input>
+    </fieldset>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 1u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "Related");
+}
+
+TEST_F(NodeSignalsTest, AddFieldsetLegendFromAllParentalFieldsets) {
+  SetBodyContent(R"(
+    <fieldset>
+      <legend>Outer</legend>
+      <fieldset>
+        <legend>Inner</legend>
+        <input>
+      </fieldset>
+    </fieldset>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 2u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "Inner");
+  EXPECT_EQ(results[0].context_features.header_text[1], "Outer");
+}
+
+TEST_F(NodeSignalsTest, AddFieldsetLegendFirstLabelOnly) {
+  SetBodyContent(R"(
+    <fieldset>
+      <legend>A</legend>
+      <legend>B</legend>
+      <input>
+    </fieldset>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 1u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "A");
+}
+
+TEST_F(NodeSignalsTest, AddHeadersDeepestOnly) {
+  SetBodyContent(R"(
+    <div>
+      <h1>A</h1>
+      <div>
+        <h2>B</h2>
+        <div>
+          <input>
+        </div>
+      </div>
+    </div>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 1u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "B");
+}
+
+TEST_F(NodeSignalsTest, AddHeadersOnlyIfAboveInHierarchy) {
+  SetBodyContent(R"(
+    <h1>A</h1>
+    <input style="position: absolute; top: 200px;">
+    <h1>B</h1>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  ASSERT_EQ(results[0].context_features.header_text.size(), 1u);
+  EXPECT_EQ(results[0].context_features.header_text[0], "A");
+}
+
+TEST_F(NodeSignalsTest, DoNotAddEmptyHeaders) {
+  SetBodyContent(R"(
+    <h1></h1>
+    <input>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  EXPECT_TRUE(results[0].context_features.header_text.empty());
+}
+
+TEST_F(NodeSignalsTest, AddHeadersOnlyIfAboveVisually) {
+  SetBodyContent(R"(
+    <h1 style="position: absolute; top: 100px;">Header</h1>
+    <input>
+  )");
+
+  WebVector<AutofillAssistantNodeSignals> results =
+      GetAutofillAssistantNodeSignals(WebDocument(&GetDocument()));
+  ASSERT_EQ(results.size(), 1u);
+
+  EXPECT_TRUE(results[0].context_features.header_text.empty());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
index 0990907..24cc874 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
@@ -11,7 +11,6 @@
 #include "third_party/blink/public/mojom/sms/webotp_service.mojom-blink.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-blink.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_response.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/modules/credentialmanager/federated_credential.cc b/third_party/blink/renderer/modules/credentialmanager/federated_credential.cc
index 0feac3c..38168df 100644
--- a/third_party/blink/renderer/modules/credentialmanager/federated_credential.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/federated_credential.cc
@@ -121,6 +121,11 @@
   return true;
 }
 
+ScriptPromise FederatedCredential::logout() {
+  // TODO(https://crbug.com/1266054): Implement.
+  return ScriptPromise();
+}
+
 ScriptPromise FederatedCredential::logoutRps(
     ScriptState* script_state,
     const HeapVector<Member<FederatedCredentialLogoutRpsRequest>>&
diff --git a/third_party/blink/renderer/modules/credentialmanager/federated_credential.h b/third_party/blink/renderer/modules/credentialmanager/federated_credential.h
index afb0aa9..750d4c1d 100644
--- a/third_party/blink/renderer/modules/credentialmanager/federated_credential.h
+++ b/third_party/blink/renderer/modules/credentialmanager/federated_credential.h
@@ -67,6 +67,8 @@
     return g_empty_string;
   }
 
+  ScriptPromise logout();
+
   static ScriptPromise logoutRps(
       ScriptState*,
       const HeapVector<Member<FederatedCredentialLogoutRpsRequest>>&);
diff --git a/third_party/blink/renderer/modules/credentialmanager/federated_credential.idl b/third_party/blink/renderer/modules/credentialmanager/federated_credential.idl
index dcb551b..34f1b4b9 100644
--- a/third_party/blink/renderer/modules/credentialmanager/federated_credential.idl
+++ b/third_party/blink/renderer/modules/credentialmanager/federated_credential.idl
@@ -25,6 +25,9 @@
     // [2] https://fedidcg.github.io/FedCM/#use-cases-explicit-sign-in
     [RuntimeEnabled=WebID] readonly attribute FederatedCredentialApprover approvedBy;
 
+    // https://fedidcg.github.io/FedCM/#browser-api-rp-sign-out
+    [RuntimeEnabled=WebID] Promise<void> logout();
+
     // Allows IDPs to logout the user out of all of the logged in RPs.
     [RuntimeEnabled=WebID, CallWith=ScriptState]
     static Promise<void> logoutRps(optional sequence<FederatedCredentialLogoutRpsRequest> logout_requests = []);
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.cc b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
index 86d74e7..7c56df0a4 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.cc
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
@@ -98,7 +98,7 @@
 ManifestParser::ManifestParser(const String& data,
                                const KURL& manifest_url,
                                const KURL& document_url,
-                               const ExecutionContext* execution_context)
+                               ExecutionContext* execution_context)
     : data_(data),
       manifest_url_(manifest_url),
       document_url_(document_url),
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.h b/third_party/blink/renderer/modules/manifest/manifest_parser.h
index 56b2006..7b9118a 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.h
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.h
@@ -39,7 +39,7 @@
   ManifestParser(const String& data,
                  const KURL& manifest_url,
                  const KURL& document_url,
-                 const ExecutionContext* execution_context);
+                 ExecutionContext* execution_context);
 
   ManifestParser(const ManifestParser&) = delete;
   ManifestParser& operator=(const ManifestParser&) = delete;
@@ -474,7 +474,7 @@
   const String data_;
   KURL manifest_url_;
   KURL document_url_;
-  const ExecutionContext* execution_context_;
+  ExecutionContext* execution_context_;
 
   // The total number of file extensions seen so far while parsing
   // `file_handlers` `accept` entries.
diff --git a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
index 34884c63..b04419d 100644
--- a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
@@ -85,28 +85,23 @@
   AudioBus* output_bus = Output(0).Bus();
   DCHECK(output_bus);
 
-  float threshold = threshold_->FinalValue();
-  float knee = knee_->FinalValue();
-  float ratio = ratio_->FinalValue();
-  float attack = attack_->FinalValue();
-  float release = release_->FinalValue();
-
   dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamThreshold,
-                                          threshold);
-  dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamKnee, knee);
+                                          threshold_->FinalValue());
+  dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamKnee,
+                                          knee_->FinalValue());
   dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamRatio,
-                                          ratio);
+                                          ratio_->FinalValue());
   dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamAttack,
-                                          attack);
+                                          attack_->FinalValue());
   dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamRelease,
-                                          release);
+                                          release_->FinalValue());
 
   scoped_refptr<AudioBus> input_bus = Input(0).Bus();
   dynamics_compressor_->Process(input_bus.get(), output_bus, frames_to_process);
 
-  float reduction =
-      dynamics_compressor_->ParameterValue(DynamicsCompressor::kParamReduction);
-  reduction_.store(reduction, std::memory_order_relaxed);
+  reduction_.store(
+      dynamics_compressor_->ParameterValue(DynamicsCompressor::kParamReduction),
+      std::memory_order_relaxed);
 }
 
 void DynamicsCompressorHandler::ProcessOnlyAudioParams(
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 4ced8312..5be8b6a 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -839,8 +839,6 @@
     "graphics/dark_mode_color_filter.h",
     "graphics/dark_mode_filter.cc",
     "graphics/dark_mode_filter.h",
-    "graphics/dark_mode_filter_helper.cc",
-    "graphics/dark_mode_filter_helper.h",
     "graphics/dark_mode_image_cache.h",
     "graphics/dark_mode_image_classifier.cc",
     "graphics/dark_mode_image_classifier.h",
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
index 016df6b4..46f42d7 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -34,7 +34,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h"
 #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
@@ -311,11 +310,10 @@
 
   const cc::PaintFlags* image_flags = &flags;
   absl::optional<cc::PaintFlags> dark_mode_flags;
-  if (auto* dark_mode_filter = draw_options.dark_mode_filter) {
+  if (draw_options.dark_mode_filter) {
     dark_mode_flags = flags;
-    DarkModeFilterHelper::ApplyFilterToImage(*dark_mode_filter, this,
-                                             &dark_mode_flags.value(),
-                                             gfx::RectFToSkRect(src_rect));
+    draw_options.dark_mode_filter->ApplyFilterToImage(
+        this, &dark_mode_flags.value(), gfx::RectFToSkRect(src_rect));
     image_flags = &dark_mode_flags.value();
   }
   canvas->drawImageRect(
diff --git a/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc b/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
index 01dd7c73..42c36457 100644
--- a/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
@@ -25,7 +25,6 @@
 
 #include "third_party/blink/renderer/platform/graphics/crossfade_generated_image.h"
 
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "ui/gfx/geometry/rect_f.h"
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
index 596c595..5df22e82 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -7,23 +7,78 @@
 #include <cmath>
 
 #include "base/check_op.h"
+#include "base/command_line.h"
 #include "base/notreached.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_image_cache.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/wtf/hash_functions.h"
 #include "third_party/blink/renderer/platform/wtf/lru_cache.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 
 namespace blink {
+
 namespace {
 
 const size_t kMaxCacheSize = 1024u;
 const int kMinImageLength = 8;
 const int kMaxImageLength = 100;
 
+bool IsRasterSideDarkModeForImagesEnabled() {
+  static bool enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableRasterSideDarkModeForImages);
+  return enabled;
+}
+
+bool ShouldUseRasterSidePath(Image* image) {
+  DCHECK(image);
+
+  // Raster-side path is not enabled.
+  if (!IsRasterSideDarkModeForImagesEnabled())
+    return false;
+
+  // Raster-side path is only supported for bitmap images.
+  return image->IsBitmapImage();
+}
+
+sk_sp<SkColorFilter> GetDarkModeFilterForImageOnMainThread(
+    DarkModeFilter* filter,
+    Image* image,
+    const SkIRect& rounded_src) {
+  SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.DarkMode.ApplyToImageOnMainThread");
+
+  sk_sp<SkColorFilter> color_filter;
+  DarkModeImageCache* cache = image->GetDarkModeImageCache();
+  DCHECK(cache);
+  if (cache->Exists(rounded_src)) {
+    color_filter = cache->Get(rounded_src);
+  } else {
+    // Performance warning: Calling AsSkBitmapForCurrentFrame() will
+    // synchronously decode image.
+    SkBitmap bitmap =
+        image->AsSkBitmapForCurrentFrame(kDoNotRespectImageOrientation);
+    SkPixmap pixmap;
+    bitmap.peekPixels(&pixmap);
+    color_filter = filter->GenerateImageFilter(pixmap, rounded_src);
+
+    // Using blink side dark mode for images, it is hard to implement
+    // caching mechanism for partially loaded bitmap image content, as
+    // content id for the image frame being rendered gets decided during
+    // rastering only. So caching of dark mode result will be deferred until
+    // default frame is completely received. This will help get correct
+    // classification results for incremental content received for the given
+    // image.
+    if (!image->IsBitmapImage() || image->CurrentFrameIsComplete())
+      cache->Add(rounded_src, color_filter);
+  }
+  return color_filter;
+}
+
 }  // namespace
 
 // DarkModeInvertedColorCache - Implements cache for inverted colors.
@@ -93,6 +148,34 @@
   return color;
 }
 
+void DarkModeFilter::ApplyFilterToImage(Image* image,
+                                        cc::PaintFlags* flags,
+                                        const SkRect& src) {
+  DCHECK(image);
+  DCHECK(flags);
+  DCHECK_NE(GetDarkModeImagePolicy(), DarkModeImagePolicy::kFilterNone);
+
+  if (GetDarkModeImagePolicy() == DarkModeImagePolicy::kFilterAll) {
+    flags->setColorFilter(GetImageFilter());
+    return;
+  }
+
+  // Raster-side dark mode path - Just set the dark mode on flags and dark
+  // mode will be applied at compositor side during rasterization.
+  if (ShouldUseRasterSidePath(image)) {
+    flags->setUseDarkModeForImage(true);
+    return;
+  }
+
+  // Blink-side dark mode path - Apply dark mode to images in main thread
+  // only. If the result is not cached, calling this path is expensive and
+  // will block main thread.
+  sk_sp<SkColorFilter> color_filter =
+      GetDarkModeFilterForImageOnMainThread(this, image, src.roundOut());
+  if (color_filter)
+    flags->setColorFilter(std::move(color_filter));
+}
+
 bool DarkModeFilter::ShouldApplyFilterToImage(const SkIRect& dst,
                                               const SkIRect& src) const {
   DarkModeImagePolicy image_policy = GetDarkModeImagePolicy();
@@ -119,8 +202,9 @@
   return dst.width() <= kMaxImageLength && dst.height() <= kMaxImageLength;
 }
 
-sk_sp<SkColorFilter> DarkModeFilter::ApplyToImage(const SkPixmap& pixmap,
-                                                  const SkIRect& src) const {
+sk_sp<SkColorFilter> DarkModeFilter::GenerateImageFilter(
+    const SkPixmap& pixmap,
+    const SkIRect& src) const {
   DCHECK(immutable_.settings.image_policy == DarkModeImagePolicy::kFilterSmart);
   DCHECK(immutable_.image_filter);
 
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
index 9c6c52b4..042d6d48 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
@@ -22,6 +22,7 @@
 class DarkModeImageClassifier;
 class DarkModeColorFilter;
 class DarkModeInvertedColorCache;
+class Image;
 
 class PLATFORM_EXPORT DarkModeFilter {
  public:
@@ -32,9 +33,8 @@
 
   enum class ElementRole { kForeground, kListSymbol, kBackground, kSVG };
 
-  DarkModeImagePolicy GetDarkModeImagePolicy() const;
-
   SkColor InvertColorIfNeeded(SkColor color, ElementRole element_role);
+
   absl::optional<cc::PaintFlags> ApplyToFlagsIfNeeded(
       const cc::PaintFlags& flags,
       ElementRole element_role);
@@ -51,15 +51,12 @@
   // deciding appropriate function call. This function should be called only if
   // image policy is set to DarkModeImagePolicy::kFilterSmart. This API is
   // thread-safe.
-  sk_sp<SkColorFilter> ApplyToImage(const SkPixmap& pixmap,
-                                    const SkIRect& src) const;
+  sk_sp<SkColorFilter> GenerateImageFilter(const SkPixmap& pixmap,
+                                           const SkIRect& src) const;
 
-  // Returns dark mode color filter for images. Before calling this function
-  // ImageShouldHaveFilterAppliedBasedOnSizes() must be called for early out or
-  // deciding appropriate function call. This function should be called only if
-  // image policy is set to DarkModeImagePolicy::kFilterAll. This API is
-  // thread-safe.
-  sk_sp<SkColorFilter> GetImageFilter() const;
+  void ApplyFilterToImage(Image* image,
+                          cc::PaintFlags* flags,
+                          const SkRect& src);
 
  private:
   struct ImmutableData {
@@ -79,6 +76,15 @@
 
   bool ShouldApplyToColor(SkColor color, ElementRole role);
 
+  // Returns dark mode color filter for images. Before calling this function
+  // ImageShouldHaveFilterAppliedBasedOnSizes() must be called for early out or
+  // deciding appropriate function call. This function should be called only if
+  // image policy is set to DarkModeImagePolicy::kFilterAll. This API is
+  // thread-safe.
+  sk_sp<SkColorFilter> GetImageFilter() const;
+
+  DarkModeImagePolicy GetDarkModeImagePolicy() const;
+
   // This is read-only data and is thread-safe.
   const ImmutableData immutable_;
 
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.cc
deleted file mode 100644
index 4c3f34d19..0000000
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h"
-
-#include "base/command_line.h"
-#include "third_party/blink/public/common/switches.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_image_cache.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
-#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
-
-namespace blink {
-
-namespace {
-
-bool IsRasterSideDarkModeForImagesEnabled() {
-  static bool enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableRasterSideDarkModeForImages);
-  return enabled;
-}
-
-bool ShouldUseRasterSidePath(Image* image) {
-  DCHECK(image);
-
-  // Raster-side path is not enabled.
-  if (!IsRasterSideDarkModeForImagesEnabled())
-    return false;
-
-  // Raster-side path is only supported for bitmap images.
-  return image->IsBitmapImage();
-}
-
-sk_sp<SkColorFilter> GetDarkModeFilterForImageOnMainThread(
-    DarkModeFilter& filter,
-    Image* image,
-    const SkIRect& rounded_src) {
-  SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.DarkMode.ApplyToImageOnMainThread");
-
-  sk_sp<SkColorFilter> color_filter;
-  DarkModeImageCache* cache = image->GetDarkModeImageCache();
-  DCHECK(cache);
-  if (cache->Exists(rounded_src)) {
-    color_filter = cache->Get(rounded_src);
-  } else {
-    // Performance warning: Calling AsSkBitmapForCurrentFrame() will
-    // synchronously decode image.
-    SkBitmap bitmap =
-        image->AsSkBitmapForCurrentFrame(kDoNotRespectImageOrientation);
-    SkPixmap pixmap;
-    bitmap.peekPixels(&pixmap);
-    color_filter = filter.ApplyToImage(pixmap, rounded_src);
-
-    // Using blink side dark mode for images, it is hard to implement
-    // caching mechanism for partially loaded bitmap image content, as
-    // content id for the image frame being rendered gets decided during
-    // rastering only. So caching of dark mode result will be deferred until
-    // default frame is completely received. This will help get correct
-    // classification results for incremental content received for the given
-    // image.
-    if (!image->IsBitmapImage() || image->CurrentFrameIsComplete())
-      cache->Add(rounded_src, color_filter);
-  }
-  return color_filter;
-}
-
-}  // namespace
-
-// static
-void DarkModeFilterHelper::ApplyFilterToImage(DarkModeFilter& filter,
-                                              Image* image,
-                                              cc::PaintFlags* flags,
-                                              const SkRect& src) {
-  DCHECK(image);
-  DCHECK(flags);
-  DCHECK_NE(filter.GetDarkModeImagePolicy(), DarkModeImagePolicy::kFilterNone);
-
-  if (filter.GetDarkModeImagePolicy() == DarkModeImagePolicy::kFilterAll) {
-    flags->setColorFilter(filter.GetImageFilter());
-    return;
-  }
-
-  // Raster-side dark mode path - Just set the dark mode on flags and dark
-  // mode will be applied at compositor side during rasterization.
-  if (ShouldUseRasterSidePath(image)) {
-    flags->setUseDarkModeForImage(true);
-    return;
-  }
-
-  // Blink-side dark mode path - Apply dark mode to images in main thread
-  // only. If the result is not cached, calling this path is expensive and
-  // will block main thread.
-  sk_sp<SkColorFilter> color_filter =
-      GetDarkModeFilterForImageOnMainThread(filter, image, src.roundOut());
-  if (color_filter)
-    flags->setColorFilter(std::move(color_filter));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h
deleted file mode 100644
index d3eda07..0000000
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_HELPER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_HELPER_H_
-
-#include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/skia/include/core/SkRect.h"
-
-namespace cc {
-class PaintFlags;
-}
-
-namespace blink {
-
-class DarkModeFilter;
-class Image;
-
-class PLATFORM_EXPORT DarkModeFilterHelper {
- public:
-  static void ApplyFilterToImage(DarkModeFilter& filter,
-                                 Image* image,
-                                 cc::PaintFlags* flags,
-                                 const SkRect& src);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_HELPER_H_
diff --git a/third_party/blink/renderer/platform/graphics/gradient.h b/third_party/blink/renderer/platform/graphics/gradient.h
index 7bcd3b2..5ce9b97 100644
--- a/third_party/blink/renderer/platform/graphics/gradient.h
+++ b/third_party/blink/renderer/platform/graphics/gradient.h
@@ -32,7 +32,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -50,6 +49,7 @@
 namespace blink {
 
 struct ImageDrawOptions;
+class DarkModeFilter;
 
 class PLATFORM_EXPORT Gradient : public RefCounted<Gradient> {
   USING_FAST_MALLOC(Gradient);
diff --git a/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc b/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
index 54d6b5b..6f839ad 100644
--- a/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
@@ -64,7 +64,6 @@
 
 bool GradientGeneratedImage::ApplyShader(cc::PaintFlags& flags,
                                          const SkMatrix& local_matrix,
-                                         const gfx::RectF& dst_rect,
                                          const gfx::RectF& src_rect,
                                          const ImageDrawOptions& draw_options) {
   DCHECK(gradient_);
diff --git a/third_party/blink/renderer/platform/graphics/gradient_generated_image.h b/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
index 1757b43..a0ed35c7 100644
--- a/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
+++ b/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
@@ -45,7 +45,6 @@
 
   bool ApplyShader(cc::PaintFlags&,
                    const SkMatrix&,
-                   const gfx::RectF& dst_rect,
                    const gfx::RectF& src_rect,
                    const ImageDrawOptions&) override;
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 9f4d264..7a4df60 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -817,8 +817,8 @@
   if (use_shader) {
     const SkMatrix local_matrix = SkMatrix::RectToRect(
         gfx::RectFToSkRect(visible_src), gfx::RectFToSkRect(dest.Rect()));
-    use_shader = image->ApplyShader(image_flags, local_matrix, dest.Rect(),
-                                    src_rect, draw_options);
+    use_shader =
+        image->ApplyShader(image_flags, local_matrix, src_rect, draw_options);
   }
 
   if (use_shader) {
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index 05d6c9cc..de65afdf 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -38,7 +38,6 @@
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_image_cache.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
 #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
@@ -292,9 +291,9 @@
   cc::PaintFlags flags(base_flags);
   flags.setColor(tile_shader ? SK_ColorBLACK : SK_ColorTRANSPARENT);
   flags.setShader(std::move(tile_shader));
-  if (auto* dark_mode_filter = draw_options.dark_mode_filter) {
-    DarkModeFilterHelper::ApplyFilterToImage(*dark_mode_filter, this, &flags,
-                                             gfx::RectToSkRect(subset_rect));
+  if (draw_options.dark_mode_filter) {
+    draw_options.dark_mode_filter->ApplyFilterToImage(
+        this, &flags, gfx::RectToSkRect(subset_rect));
   }
 
   context.DrawRect(gfx::RectFToSkRect(dest_rect), flags,
@@ -330,7 +329,6 @@
 
 bool Image::ApplyShader(cc::PaintFlags& flags,
                         const SkMatrix& local_matrix,
-                        const gfx::RectF& dst_rect,
                         const gfx::RectF& src_rect,
                         const ImageDrawOptions& draw_options) {
   // Default shader impl: attempt to build a shader based on the current frame
@@ -339,9 +337,9 @@
   if (!image)
     return false;
 
-  if (auto* dark_mode_filter = draw_options.dark_mode_filter) {
-    DarkModeFilterHelper::ApplyFilterToImage(*dark_mode_filter, this, &flags,
-                                             gfx::RectFToSkRect(src_rect));
+  if (draw_options.dark_mode_filter) {
+    draw_options.dark_mode_filter->ApplyFilterToImage(
+        this, &flags, gfx::RectFToSkRect(src_rect));
   }
   flags.setShader(PaintShader::MakeImage(image, SkTileMode::kClamp,
                                          SkTileMode::kClamp, &local_matrix));
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index 0f7a0416..312860b 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -303,7 +303,6 @@
   // of that function the shader should use a clamping tile mode if possible.
   virtual bool ApplyShader(cc::PaintFlags&,
                            const SkMatrix& local_matrix,
-                           const gfx::RectF& dst_rect,
                            const gfx::RectF& src_rect,
                            const ImageDrawOptions& draw_options);
 
diff --git a/third_party/blink/renderer/platform/graphics/raster_dark_mode_filter_impl.cc b/third_party/blink/renderer/platform/graphics/raster_dark_mode_filter_impl.cc
index 3e25e7d..24a2bfd 100644
--- a/third_party/blink/renderer/platform/graphics/raster_dark_mode_filter_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/raster_dark_mode_filter_impl.cc
@@ -17,7 +17,7 @@
 sk_sp<SkColorFilter> RasterDarkModeFilterImpl::ApplyToImage(
     const SkPixmap& pixmap,
     const SkIRect& src) const {
-  return dark_mode_filter_->ApplyToImage(pixmap, src);
+  return dark_mode_filter_->GenerateImageFilter(pixmap, src);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/network/http_names.json5 b/third_party/blink/renderer/platform/network/http_names.json5
index 63880453..83d9223 100644
--- a/third_party/blink/renderer/platform/network/http_names.json5
+++ b/third_party/blink/renderer/platform/network/http_names.json5
@@ -23,7 +23,10 @@
     "Access-Control-Request-Headers",
     "Access-Control-Request-Method",
     "Allow-CSP-From",
+    "Attribution-Reporting-Filters",
     "Attribution-Reporting-Register-Aggregatable-Source",
+    "Attribution-Reporting-Register-Aggregatable-Trigger-Data",
+    "Attribution-Reporting-Register-Aggregatable-Values",
     "Attribution-Reporting-Register-Event-Trigger",
     "Attribution-Reporting-Register-Source",
     "Cache-Control",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ef9eb9d..a21c9e1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -201,13 +201,6 @@
 
 # --- Skia roll test suppressions
 
-# TODO(michaelludwig) - crbug.com/1298194 remove after
-# https://skia-review.googlesource.com/c/skia/+/502059 lands and rolls into chromium
-crbug.com/1298194 virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference.html [ Failure Pass ]
-crbug.com/1298194 virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-circumference.html [ Failure Pass ]
-crbug.com/1298194 virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line.html [ Failure Pass ]
-crbug.com/1298194 virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap.html [ Failure Pass ]
-
 # --- END Skia roll test suppresions
 
 
@@ -3337,6 +3330,9 @@
 crbug.com/626703 [ Linux ] external/wpt/url/failure.html [ Failure ]
 crbug.com/626703 virtual/prerender/external/wpt/speculation-rules/prerender/workers.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-animations/KeyframeEffect-getKeyframes.tentative.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/preload/avoid-delaying-onload-link-modulepreload.html [ Failure ]
+crbug.com/626703 [ Win ] virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https.html [ Failure ]
+crbug.com/626703 [ Win ] virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
 crbug.com/626703 [ Mac ] external/wpt/css/css-overflow/webkit-line-clamp-036.html [ Failure ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 819f39a..275d1a8 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -81,24 +81,15 @@
 # Dawn implements validation of the limit at createShaderModule time, while the CTS checks at createRenderPipeline time.
 crbug.com/dawn/986 wpt_internal/webgpu/cts.https.html?q=webgpu:api,validation,vertex_state:vertex_shader_input_location_limit:* [ Failure ]
 
-# These tests aren't working on CQ, unclear whether the test or harness (or Chrome) is broken.
-# Mac: mostly works
-# Linux: Crashes
-crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_copy.https.html [ Skip ]
-crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_draw.https.html [ Skip ]
-crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_opaque.https.html [ Skip ]
-crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_premultiplied.https.html [ Skip ]
-crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_size_different_with_back_buffer_size.https.html [ Skip ]
+# VVL failure
+crbug.com/1083478 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_size_different_with_back_buffer_size.https.html [ Crash ]
 
-# Mac/Win: Shifted by about half a pixel
-crbug.com/1083478 [ Win ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_opaque.https.html [ Failure ]
-crbug.com/1083478 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_opaque.https.html [ Failure ]
-crbug.com/1083478 [ Win ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_copy.https.html [ Failure ]
-crbug.com/1083478 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_copy.https.html [ Failure ]
-crbug.com/1083478 [ Win ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_draw.https.html [ Failure ]
-crbug.com/1083478 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_draw.https.html [ Failure ]
-crbug.com/1083478 [ Win ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_premultiplied.https.html [ Failure ]
-crbug.com/1083478 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_premultiplied.https.html [ Failure ]
+# These tests aren't working on CQ, unclear whether the test or harness (or Chrome) is broken.
+# Shifted by about half a pixel
+crbug.com/1083478 wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_opaque.https.html [ Failure ]
+crbug.com/1083478 wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_copy.https.html [ Failure ]
+crbug.com/1083478 wpt_internal/webgpu/web_platform/reftests/canvas_complex_bgra8unorm_draw.https.html [ Failure ]
+crbug.com/1083478 wpt_internal/webgpu/web_platform/reftests/canvas_composite_alpha_bgra8unorm_premultiplied.https.html [ Failure ]
 
 # Spec was changed so BGLs should eagerly apply per-pipeline limits. Tests need fixing, then Dawn
 # needs to pass them. https://github.com/gpuweb/cts/issues/230
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 049af1ac..87aed0b 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 73aae07a9513ef44a75d75d02a84630d3d653909
+Version: a723fd8ceb7615a4fd30b984c9928116200b1de6
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 1d7b08f..8a725ba 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
@@ -306073,7 +306073,7 @@
     ]
    },
    "lint.ignore": [
-    "d78f4a39c4c21c564d95055bba937c35f0a7d79f",
+    "d273dcf68418a3b8ad5bf7dac07f25dbdab7f043",
     []
    ],
    "loading": {
@@ -310430,6 +310430,10 @@
       "1c7c1a2750e7bc8dbd24ec16f4037183a4a9e91d",
       []
      ],
+     "slow-exec.js": [
+      "3b37da4ef4b903d85beabc0e6bdbcca0a726abcd",
+      []
+     ],
      "sound_5.oga": [
       "239ad2bd08c21ca783b83cd0245951fecdab6a64",
       []
@@ -316848,11 +316852,11 @@
     "prerender": {
      "resources": {
       "about-blank-iframes.html": [
-       "4cac6d6fee63b189cccfe34e40f32ff502b95641",
+       "db99d586e2b8b96da36344653a9e6be80d6d2247",
        []
       ],
       "activation-start.html": [
-       "262a1ae4d52973f650e34a2924c48598f657edff",
+       "456f15ff699eb36b230b5079f918cbd57ea2705a",
        []
       ],
       "cache.txt": [
@@ -316868,7 +316872,7 @@
        []
       ],
       "cross-origin-isolated.https.html": [
-       "1118b99454c5c45823dda24a9545f7eec52015ee",
+       "4fdc6c954180e8d8eecd01383be84789a27b2a7e",
        []
       ],
       "cross-origin-isolated.https.html.headers": [
@@ -316876,7 +316880,11 @@
        []
       ],
       "deferred-promise-utils.js": [
-       "02bd7ec5873a512c0fdd84ac5fa18071d1fd9357",
+       "9a5cfb1bf11d5d784b466f7820059dd44aa5803b",
+       []
+      ],
+      "deprecated-broadcast-channel.py": [
+       "62631d922fab5ffbacb9f6379dacc3464e552a64",
        []
       ],
       "empty.html": [
@@ -316888,7 +316896,7 @@
        []
       ],
       "iframe-added-post-activation.html": [
-       "fb7aaa8e95cea564e03e0e9a35b767cd7ac4b24e",
+       "10a48df58cf9dd9f13eca87e69c54098af1b64b0",
        []
       ],
       "indexedb-utils.js": [
@@ -316900,7 +316908,7 @@
        []
       ],
       "notification.html": [
-       "f9502dd5f21d6f93fd97f3553f4fc01f0cf57809",
+       "03e36f06c134e1ee0ced6c703d1b8e20f1c7511b",
        []
       ],
       "prerender-state.html": [
@@ -316908,7 +316916,7 @@
        []
       ],
       "presentation-request.html": [
-       "5dc8d6aca1ae35e8b6f2447f75ec72d786b87f1f",
+       "62829556bb438c1396a0f17c7e3b45456f2bdad9",
        []
       ],
       "service-worker.js": [
@@ -316920,11 +316928,11 @@
        []
       ],
       "storage-foundation-access.https.html": [
-       "4ab8b062c93f845a57e70650d248cf8bbb71ce48",
+       "db965fa928f2ad01285e362d9c8fb7d374acd29c",
        []
       ],
       "utils.js": [
-       "38aaff7de733211a1a220b521d1c20d4c5339a5f",
+       "bbb9448190c5e1978eeebe42c008dcfc075fd3d0",
        []
       ],
       "visibility-state-check.html": [
@@ -316932,23 +316940,23 @@
        []
       ],
       "web-database-access.html": [
-       "99741906ed3b708fdbed36397f62d7cb4a9e2801",
+       "fb00852caa9d6eaa0134662b7fe01f085641509e",
        []
       ],
       "window-move.html": [
-       "fdb12942b7488c917d1d428c9a4c96fa30303f51",
+       "0c5888c957e28e49452134ae37b3239821d3b040",
        []
       ],
       "window-open-during-prerendering.html": [
-       "3a6ece5fe898f0f95e82af3665a596f7ef5ed805",
+       "a314d5005b3bcad7df3ceac115830d814140a7c5",
        []
       ],
       "window-open-in-prerenderingchange.html": [
-       "485b878f16b1e90c604877c464e8edeba373551b",
+       "f32126f93d5da7dcc3873ce0f7ae4a66c3d56cd6",
        []
       ],
       "window-resize.html": [
-       "d8ae493cbb240068a7cd1847090557a2cb326768",
+       "8b6172ee16ccf8d6a9832e2e416a2395e611e983",
        []
       ],
       "worker-post-timeOrigin.js": [
@@ -341027,7 +341035,7 @@
       ]
      ],
      "currentchange-app-history-back-forward-same-doc.html": [
-      "9b1b777614b9d263ff7efcaa3f8e2fc8731840e9",
+      "8cfe2dca1ffa4914da8503bf6bfda5b6dca087df",
       [
        null,
        {}
@@ -341055,7 +341063,7 @@
       ]
      ],
      "currentchange-app-history-navigate-replace-same-doc.html": [
-      "2967f21d973aa6e6dd8b38ad0b1d0b7e09917d93",
+      "fa7006b4ebb8b09017806b71073e38147e106b87",
       [
        null,
        {}
@@ -341069,7 +341077,7 @@
       ]
      ],
      "currentchange-app-history-navigate-same-doc.html": [
-      "d112e2592806e8fe9830de8fbb2e8b2a8f1a97c7",
+      "d74048a0121fbc5d226c6cc35d0562c5d4c5eae8",
       [
        null,
        {}
@@ -341104,7 +341112,7 @@
       ]
      ],
      "currentchange-history-back-same-doc.html": [
-      "7cfecb15b8ef9e4dc4e2cf678262e889b8d02550",
+      "a90ee1eb5ddf86ef06393eef32f651a575088c5a",
       [
        null,
        {}
@@ -342305,14 +342313,14 @@
       ]
      ],
      "navigate-history-back-after-fragment.html": [
-      "f0a423a47b43b5452ca9535171945e7d12345c09",
+      "307fe5761bc04d39ad4e7393033e58c5d10704dc",
       [
        null,
        {}
       ]
      ],
      "navigate-history-back-after-pushState.html": [
-      "40c41ee3b7098ce58c2c97a83b082096e106cecf",
+      "74f353d50513403b05eb75d48c2e96344e6e871c",
       [
        null,
        {}
@@ -356182,6 +356190,15 @@
       ]
      ]
     },
+    "parsing": {
+     "invalid-directive.html": [
+      "d96141ee1a629d9d7ab384ca4c64b4181ab5e83a",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "plugin-types": {
      "plugin-types-ignored.html": [
       "cf27cdfc54b31aa12c0f5718a85ab6e956e70093",
@@ -460105,7 +460122,7 @@
         ]
        ],
        "selectmenu-form-state-restore.tentative.html": [
-        "65a685986791be499dbc19209da836fc293a7b2e",
+        "1002355a5ab1ea4718ad2194826dac743ea7ec85",
         [
          null,
          {}
@@ -486293,6 +486310,20 @@
     }
    },
    "preload": {
+    "avoid-delaying-onload-link-modulepreload-exec.html": [
+     "160aef6b5fda43f91605335123dc2b5f611f8e30",
+     [
+      null,
+      {}
+     ]
+    ],
+    "avoid-delaying-onload-link-modulepreload.html": [
+     "df1ac72eb38b8d07c969acd0c9f8eab848ac3dfe",
+     [
+      null,
+      {}
+     ]
+    ],
     "avoid-delaying-onload-link-preload-style.html": [
      "299713834094fb9661c2ea46aa7022c1af2cdc22",
      [
@@ -507943,7 +507974,7 @@
    "speculation-rules": {
     "prerender": {
      "about-blank-iframes.html": [
-      "a35d1886ef5e27154d65f797a455f1f31e87a1ae",
+      "9cc0ab3792714828be6235a4405db9cd77eec906",
       [
        null,
        {
@@ -507952,7 +507983,7 @@
       ]
      ],
      "activation-start.html": [
-      "773a99b087cf7ef2104747bd3688e941176171ae",
+      "fda6de15df0a3f7a071c6dea5388ad0b4ca77298",
       [
        null,
        {
@@ -507979,7 +508010,7 @@
       ]
      ],
      "cross-origin-isolated.https.html": [
-      "ba41ced98bb32c95e7121f39c73e766fb9e03ad0",
+      "01dafe00bf090ccfc41c5e86c22aa7f7841f014d",
       [
        null,
        {
@@ -507997,7 +508028,7 @@
       ]
      ],
      "iframe-added-post-activation.html": [
-      "aeec5fc99bbd6519c7c0a8345a0beeafb58a0c2d",
+      "c4263db87d90b4d9d8610be7d4708c85b42bbd95",
       [
        null,
        {
@@ -508033,7 +508064,7 @@
       ]
      ],
      "restriction-focus.html": [
-      "10df5b8dd2aedcbdfda5ecffe9d095c509fb7760",
+      "1149b8bd0981e8cf0109f2bca9dd974b9cd9a604",
       [
        null,
        {
@@ -508042,7 +508073,7 @@
       ]
      ],
      "restriction-notification.https.html": [
-      "964c408f111d92c9477fc2f14d21d87c768d6f92",
+      "e9d3ba22a5908912302674ba7373a8aa3d1f623b",
       [
        null,
        {
@@ -508052,7 +508083,7 @@
       ]
      ],
      "restriction-presentation-request.https.html": [
-      "75e55e27fe028f8eaa22a708e0696d52c4d6747f",
+      "5f7742204e2b399b6ee2b2aaad656dfc5a09765c",
       [
        null,
        {
@@ -508061,7 +508092,7 @@
       ]
      ],
      "restriction-window-move.html": [
-      "fca3b537db4e6d1c8a85516e2727c57951ad7bef",
+      "e8011311f61ac1c39192ea193b395493a82fddbd",
       [
        null,
        {
@@ -508070,7 +508101,7 @@
       ]
      ],
      "restriction-window-open.html": [
-      "33ec5b1530de8159c6ee5e2db06ac8b7df984bdf",
+      "5de23efc778cfd279d009bec0595b7653e83698d",
       [
        null,
        {
@@ -508079,7 +508110,7 @@
       ]
      ],
      "restriction-window-resize.html": [
-      "5f02ac6b8e7ba81d8059b4dd11ca4ecfe2b525a7",
+      "20a71b4bdb5945d6217b1a4055a2ee06f8507ceb",
       [
        null,
        {
@@ -508115,7 +508146,7 @@
       ]
      ],
      "storage-foundation.https.html": [
-      "ca1b9363f4b243eada40b59ff60ee2ada9dc2a5b",
+      "6be77bad8fd4cf7d93b282463549323b3a918baf",
       [
        null,
        {
@@ -508124,7 +508155,7 @@
       ]
      ],
      "visibility-state.html": [
-      "1affe546a97f288f0e67426fedda7f22bbdbbab2",
+      "f8d3ccd9a1e8fb21a3b2d6cd3fc2d396cf8d48a0",
       [
        null,
        {
@@ -508133,7 +508164,7 @@
       ]
      ],
      "web-database.html": [
-      "3e59c3c2959777682ce7279ea6110a034f7623e3",
+      "3ef1141e2063e9eda6bbf5b7a4b62760b5c52c03",
       [
        null,
        {
@@ -529023,10 +529054,10 @@
         {}
        ]
       ],
-      "pannernode-basic.html": [
-       "32402f5e068e8da1f985470b10bcd793cb8bd662",
+      "pannernode-basic.window.js": [
+       "298fce0f208cf28cc1872e6a1b8701203df9c19e",
        [
-        null,
+        "webaudio/the-audio-api/the-pannernode-interface/pannernode-basic.window.html",
         {}
        ]
       ],
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.json b/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.json
index 0967ef4..a20b909 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.json
+++ b/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.json
@@ -1 +1,3 @@
-{}
+{
+  "privacy_policy_url": "https://privacypolicy.com"
+}
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index d78f4a3..d273dcf6 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -191,6 +191,7 @@
 SET TIMEOUT: paint-timing/resources/subframe-painting.html
 SET TIMEOUT: portals/resources/portals-adopt-predecessor-portal.html
 SET TIMEOUT: preload/single-download-preload.html
+SET TIMEOUT: preload/resources/slow-exec.js
 SET TIMEOUT: resize-observer/resources/iframe.html
 SET TIMEOUT: resource-timing/resources/nested-contexts.js
 SET TIMEOUT: reporting/resources/first-csp-report.https.sub.html
diff --git a/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload-exec.html b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload-exec.html
new file mode 100644
index 0000000..160aef6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload-exec.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<link rel=modulepreload href="resources/slow-exec.js">
+<script>
+    setup(() => {
+        const link = window.document.createElement("link");
+        assert_implements(
+            'relList' in link,
+            'HTMLLinkElement.relList is not supported');
+
+        assert_implements(
+            link.relList.supports("modulepreload"),
+            'modulepreload is not supported');
+    });
+
+    promise_test(async t => {
+        await new Promise(r => window.addEventListener("load", r));
+
+        assert_false(!!window.didLoadModule);
+    }, "Executing modulepreload should not block the window's load event");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload.html b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload.html
new file mode 100644
index 0000000..df1ac72
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-modulepreload.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<link rel=modulepreload href="resources/dummy.js?pipe=trickle(d5)">
+<script>
+    setup(() => {
+        const link = window.document.createElement("link");
+        assert_implements(
+            'relList' in link,
+            'HTMLLinkElement.relList is not supported');
+
+        assert_implements(
+            link.relList.supports("modulepreload"),
+            'modulepreload is not supported');
+    });
+
+    promise_test(async t => {
+        await new Promise(r => window.addEventListener("load", r));
+        verifyNumberOfResourceTimingEntries("resources/dummy.js?pipe=trickle(d5)", 0);
+    }, "Fetching modulepreload should not block the window's load event");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/slow-exec.js b/third_party/blink/web_tests/external/wpt/preload/resources/slow-exec.js
new file mode 100644
index 0000000..3b37da4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/slow-exec.js
@@ -0,0 +1,3 @@
+window.didLoadModule = false;
+await new Promise(r => setTimeout(t, 5000));
+window.didLoadModule = true;
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/about-blank-iframes.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/about-blank-iframes.html
index a35d1886e..9cc0ab37 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/about-blank-iframes.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/about-blank-iframes.html
@@ -7,6 +7,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -14,7 +15,8 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const channel = new BroadcastChannel('test-channel');
+  const uid = token();
+  const channel = new PrerenderChannel('test-channel', uid);
 
   const gotMessage = new Promise(resolve => {
     channel.addEventListener('message', e => {
@@ -23,7 +25,7 @@
   });
 
   // Make the window to start the prerender.
-  const url = `resources/about-blank-iframes.html`;
+  const url = `resources/about-blank-iframes.html?uid=${uid}`;
   window.open(url, '_blank', 'noopener');
 
   const msg = await gotMessage;
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/activation-start.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/activation-start.html
index 773a99b0..fda6de1 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/activation-start.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/activation-start.html
@@ -3,21 +3,22 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
 
 setup(() => assertSpeculationRulesIsSupported());
-
 promise_test(async t => {
-  const testChannel = new BroadcastChannel('test-channel');
+  const uid = token();
+  const testChannel = new PrerenderChannel('test-channel', uid);
   t.add_cleanup(() => {
     testChannel.close();
   });
   const gotMessage = new Promise(resolve => {
     testChannel.addEventListener('message', e => resolve(e.data), {once: true});
   });
-  window.open('resources/activation-start.html', '_blank', 'noopener');
+  window.open(`resources/activation-start.html?uid=${uid}`, '_blank', 'noopener');
   assert_equals(await gotMessage, 'Done');
 }, 'PerformanceNavigationTiming.activationStart in prerendered page');
 
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/cross-origin-isolated.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/cross-origin-isolated.https.html
index ba41ced..01dafe0 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/cross-origin-isolated.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/cross-origin-isolated.https.html
@@ -3,6 +3,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -10,7 +11,8 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const testChannel = new BroadcastChannel('test-channel');
+  const uid = token();
+  const testChannel = new PrerenderChannel('test-channel', uid);
   t.add_cleanup(() => {
     testChannel.close();
   });
@@ -18,7 +20,7 @@
     testChannel.addEventListener('message', e => resolve(e.data), {once: true});
   });
 
-  startPrerendering('resources/cross-origin-isolated.https.html');
+  startPrerendering(`resources/cross-origin-isolated.https.html?uid=${uid}`);
   assert_true(await gotMessage);
 }, 'Allow crossOriginIsolated in prerendered page');
 
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/iframe-added-post-activation.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/iframe-added-post-activation.html
index aeec5fc9..c4263db 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/iframe-added-post-activation.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/iframe-added-post-activation.html
@@ -7,6 +7,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -14,12 +15,13 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const channel = new BroadcastChannel('test-channel');
+  const uid = token();
+  const channel = new PrerenderChannel('test-channel', uid);
   const messageQueue = new BroadcastMessageQueue(channel);
   t.add_cleanup(_ => channel.close());
 
   // Make the window to start the prerender.
-  const url = `resources/iframe-added-post-activation.html`;
+  const url = `resources/iframe-added-post-activation.html?uid=${uid}`;
   window.open(url, '_blank', 'noopener');
 
   // Wait for done.
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/about-blank-iframes.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/about-blank-iframes.html
index 4cac6d6f..db99d586 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/about-blank-iframes.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/about-blank-iframes.html
@@ -85,7 +85,7 @@
   });
 
   // Ask to activate.
-  const prerenderChannel = new BroadcastChannel('prerender-channel');
+  const prerenderChannel = new PrerenderChannel('prerender-channel');
   prerenderChannel.postMessage('readyToActivate');
 
   // Test document.prerendering post-activation for each document.
@@ -104,7 +104,7 @@
 } else {
   // For the prerendering page, run main() then message the test page with the
   // result.
-  const testChannel = new BroadcastChannel('test-channel');
+  const testChannel = new PrerenderChannel('test-channel');
   main().then(() => {
     testChannel.postMessage('PASS');
   }).catch((e) => {
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/activation-start.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/activation-start.html
index 262a1ae..456f15f 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/activation-start.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/activation-start.html
@@ -20,7 +20,7 @@
 }
 
 (async () => {
-  const testChannel = new BroadcastChannel('test-channel');
+  const testChannel = new PrerenderChannel('test-channel');
   const params = new URLSearchParams(location.search);
   const isPrerenderingMainFrame = params.has('prerendering') &&
                                   !params.has('iframe');
@@ -36,9 +36,9 @@
   // 3. The prerendering document opens the iframe document (isIframe).
   try {
     if (isInitiator) {
-      // We use a BroadcastChannel to receive a message from the prerendering
+      // We use a PrerenderChannel to receive a message from the prerendering
       // iframe.
-      const prerenderChannel = new BroadcastChannel('prerender-channel');
+      const prerenderChannel = new PrerenderChannel('prerender-channel');
       const prerendering_url = new URL(document.URL);
       prerendering_url.searchParams.set('prerendering', '');
       startPrerendering(prerendering_url);
@@ -80,7 +80,7 @@
       // Finishes the test.
       testChannel.postMessage('Done');
     } else if (isIframe) {
-      const prerenderChannel = new BroadcastChannel('prerender-channel');
+      const prerenderChannel = new PrerenderChannel('prerender-channel');
       const initial_value = getActivationStart();
       const activation_start_promise = new Promise(resolve => {
         document.addEventListener('prerenderingchange', () => {
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/cross-origin-isolated.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/cross-origin-isolated.https.html
index 1118b994..4fdc6c95 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/cross-origin-isolated.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/cross-origin-isolated.https.html
@@ -8,7 +8,7 @@
   assert_equals(e.data.name, 'crossOriginIsolated');
   assert_true(e.data.value);
 
-  const testChannel = new BroadcastChannel('test-channel');
+  const testChannel = new PrerenderChannel('test-channel');
   testChannel.postMessage(self.crossOriginIsolated);
   testChannel.close();
 };
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deferred-promise-utils.js b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deferred-promise-utils.js
index 02bd7ec..9a5cfb1 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deferred-promise-utils.js
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deferred-promise-utils.js
@@ -23,6 +23,9 @@
 class PrerenderEventCollector {
   constructor() {
     this.eventsSeen_ = [];
+    new PrerenderChannel('close').addEventListener('message', () => {
+      window.close();
+    });
   }
 
   // Adds an event to `eventsSeen_` along with the prerendering state of the
@@ -49,11 +52,9 @@
             })
         .finally(() => {
           // Used to communicate with the main test page.
-          const testChannel = new BroadcastChannel('test-channel');
+          const testChannel = new PrerenderChannel('test-channel');
           // Send the observed events back to the main test page.
           testChannel.postMessage(this.eventsSeen_);
-          testChannel.close();
-          window.close();
         });
     document.addEventListener('prerenderingchange', () => {
       this.addEvent('prerendering change');
@@ -63,10 +64,9 @@
     // resolves a promise without waiting for activation.
     setTimeout(() => {
       // Used to communicate with the initiator page.
-      const prerenderChannel = new BroadcastChannel('prerender-channel');
+      const prerenderChannel = new PrerenderChannel('prerender-channel');
       // Inform the initiator page that this page is ready to be activated.
       prerenderChannel.postMessage('readyToActivate');
-      prerenderChannel.close();
     }, 0);
   }
 }
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deprecated-broadcast-channel.py b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deprecated-broadcast-channel.py
new file mode 100644
index 0000000..62631d92
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/deprecated-broadcast-channel.py
@@ -0,0 +1,28 @@
+import json
+import time
+def main(request, response):
+    uid = request.GET.first(b"uid")
+    name = request.GET.first(b"name")
+    time.sleep(0.1)
+
+    messagesByName = []
+    if request.method == 'POST':
+        with request.server.stash.lock:
+            messages = request.server.stash.take(uid) or {}
+            if name in messages:
+                messagesByName = messages[name]
+
+            messagesByName.append(json.loads(request.body))
+            messages[name] = messagesByName
+            request.server.stash.put(uid, messages)
+        response.status = 204
+    else:
+        with request.server.stash.lock:
+            messages = request.server.stash.take(uid) or {}
+            if name in messages:
+                messagesByName = messages[name]
+
+            request.server.stash.put(uid, messages)
+            response.status = 200
+            response.headers['Content-Type'] = 'application/json'
+            response.content = json.dumps(messagesByName)
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/iframe-added-post-activation.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/iframe-added-post-activation.html
index fb7aaa8..10a48df5 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/iframe-added-post-activation.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/iframe-added-post-activation.html
@@ -29,7 +29,7 @@
   });
 
   // Ask to activate.
-  const prerenderChannel = new BroadcastChannel('prerender-channel');
+  const prerenderChannel = new PrerenderChannel('prerender-channel');
   prerenderChannel.postMessage('readyToActivate');
 
   // Check that document.prerendering is false in the iframe.
@@ -46,7 +46,7 @@
 } else {
   // For the prerendering page, run main() then message the test page with the
   // result.
-  const testChannel = new BroadcastChannel('test-channel');
+  const testChannel = new PrerenderChannel('test-channel');
   main().then(() => {
     testChannel.postMessage('PASS');
   }).catch((e) => {
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification.html
index f9502dd..03e36f0 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification.html
@@ -13,9 +13,9 @@
   loadInitiatorPage();
 } else {
   // Used to communicate with the initiator page.
-  const prerenderChannel = new BroadcastChannel('prerender-channel');
+  const prerenderChannel = new PrerenderChannel('prerender-channel');
   // Used to communicate with the main test page.
-  const testChannel = new BroadcastChannel('test-channel');
+  const testChannel = new PrerenderChannel('test-channel');
 
   window.addEventListener('load', () => {
     // Inform the initiator page that this page is ready to be activated.
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/presentation-request.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/presentation-request.html
index 5dc8d6a..62829556 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/presentation-request.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/presentation-request.html
@@ -6,7 +6,7 @@
 assert_true(document.prerendering);
 
 async function startPresentationRequest() {
-  const bc = new BroadcastChannel('prerender-channel');
+  const bc = new PrerenderChannel('prerender-channel');
   const presentationRequest = new PresentationRequest(
       'https://example.com/presentation.html');
 
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/storage-foundation-access.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/storage-foundation-access.https.html
index 4ab8b062..db965fa9 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/storage-foundation-access.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/storage-foundation-access.https.html
@@ -20,7 +20,7 @@
 }
 
 async function readWriteTest() {
-  const bc = new BroadcastChannel('prerender-channel');
+  const bc = new PrerenderChannel('prerender-channel');
   assert_true(document.prerendering);
 
   let f = await storageFoundation.open('test_file');
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
index 38aaff7d..bbb9448 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
@@ -22,6 +22,41 @@
   document.head.appendChild(script);
 }
 
+class PrerenderChannel extends EventTarget {
+  #ids = new Set();
+  #url;
+  #active = true;
+
+  constructor(name, uid = new URLSearchParams(location.search).get('uid')) {
+    super();
+    this.#url = `/speculation-rules/prerender/resources/deprecated-broadcast-channel.py?name=${name}&uid=${uid}`;
+    (async() => {
+      while (this.#active) {
+        const messages = await (await fetch(this.#url)).json();
+        for (const {data, id} of messages) {
+          if (!this.#ids.has(id))
+            this.dispatchEvent(new MessageEvent('message', {data}));
+          this.#ids.add(id);
+        }
+      }
+    })();
+  }
+
+  close() {
+    this.#active = false;
+  }
+
+  set onmessage(m) {
+    this.addEventListener('message', m)
+  }
+
+  async postMessage(data) {
+    const id = new Date().valueOf();
+    this.#ids.add(id);
+    await fetch(this.#url, {method: 'POST', body: JSON.stringify({data, id})});
+  }
+}
+
 // Reads the value specified by `key` from the key-value store on the server.
 async function readValueFromServer(key) {
   const serverUrl = `${STORE_URL}?key=${key}`;
@@ -63,7 +98,7 @@
 // receives the 'readyToActivate' message.
 function loadInitiatorPage() {
   // Used to communicate with the prerendering page.
-  const prerenderChannel = new BroadcastChannel('prerender-channel');
+  const prerenderChannel = new PrerenderChannel('prerender-channel');
   window.addEventListener('unload', () => {
     prerenderChannel.close();
   });
@@ -90,7 +125,7 @@
   readyToActivate.then(() => {
     window.location = url.toString();
   }).catch(e => {
-    const testChannel = new BroadcastChannel('test-channel');
+    const testChannel = new PrerenderChannel('test-channel');
     testChannel.postMessage(
         `Failed to navigate the prerendered page: ${e.toString()}`);
     testChannel.close();
@@ -98,21 +133,21 @@
   });
 }
 
-// Returns messages received from the given BroadcastChannel
+// Returns messages received from the given PrerenderChannel
 // so that callers do not need to add their own event listeners.
 // nextMessage() returns a promise which resolves with the next message.
 //
 // Usage:
-//   const channel = new BroadcastChannel('channel-name');
+//   const channel = new PrerenderChannel('channel-name');
 //   const messageQueue = new BroadcastMessageQueue(channel);
 //   const message1 = await messageQueue.nextMessage();
 //   const message2 = await messageQueue.nextMessage();
 //   message1 and message2 are the messages received.
 class BroadcastMessageQueue {
-  constructor(broadcastChannel) {
+  constructor(c) {
     this.messages = [];
     this.resolveFunctions = [];
-    this.channel = broadcastChannel;
+    this.channel = c;
     this.channel.addEventListener('message', e => {
       if (this.resolveFunctions.length > 0) {
         const fn = this.resolveFunctions.shift();
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/web-database-access.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/web-database-access.html
index 9974190..fb00852 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/web-database-access.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/web-database-access.html
@@ -1,9 +1,10 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
 <script>
 
-const bc = new BroadcastChannel('prerender-channel');
+const bc = new PrerenderChannel('prerender-channel');
 assert_true(document.prerendering);
 
 let result = "Success";
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-move.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-move.html
index fdb1294..0c5888c9 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-move.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-move.html
@@ -15,7 +15,7 @@
   try {
     func();
   } catch (e) {
-    const testChannel = new BroadcastChannel('test-channel');
+    const testChannel = new PrerenderChannel('test-channel');
     testChannel.postMessage({status: 'FAIL: ' + e});
   }
 }
@@ -50,7 +50,7 @@
       }
   );
 
-  const bc = new BroadcastChannel('test-channel');
+  const bc = new PrerenderChannel('test-channel');
   bc.postMessage({
     'status': 'PASS',
     'prevPosition': prevPosition,
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-during-prerendering.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-during-prerendering.html
index 3a6ece5..a314d500 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-during-prerendering.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-during-prerendering.html
@@ -9,10 +9,10 @@
 
 function runAsTriggerPage() {
   assert_false(document.prerendering);
-  startPrerendering(location.href + '?prerendering');
+  startPrerendering(location.href + '&prerendering=true');
 
   // Close this window for cleanup after the prerendering page runs the test.
-  const bc = new BroadcastChannel('result');
+  const bc = new PrerenderChannel('result');
   bc.onmessage = e => window.close();
 }
 
@@ -23,7 +23,7 @@
   const win = window.open('empty.html', '_blank');
 
   // Send the result to the test runner page.
-  const bc = new BroadcastChannel('result');
+  const bc = new PrerenderChannel('result');
   if (win) {
     bc.postMessage('opened');
     win.close();
@@ -32,7 +32,7 @@
   }
 }
 
-if (location.search === '?prerendering') {
+if (new URLSearchParams(location.search).has('prerendering')) {
   runAsPrerenderingPage();
 } else {
   runAsTriggerPage();
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-in-prerenderingchange.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-in-prerenderingchange.html
index 485b878f..f32126f9 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-in-prerenderingchange.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-open-in-prerenderingchange.html
@@ -14,12 +14,12 @@
   assert_false(document.prerendering);
 
   // Start prerendering.
-  const prerendering_url = location.href + '?prerendering';
+  const prerendering_url = location.href + '&prerendering=true';
   startPrerendering(prerendering_url);
 
   // Activate the prerendering page once it gets ready.
-  const bc = new BroadcastChannel('activation-ready');
-  bc.onmessage = () => window.location = prerendering_url;
+  const bc = new PrerenderChannel('activation-ready');
+  bc.onmessage = () => { window.location = prerendering_url };
 }
 
 // Runs as prerendeirng page. First this page waits for the load event and
@@ -32,10 +32,13 @@
     assert_true(document.prerendering);
 
     // Notify the trigger page of activation ready.
-    const bc = new BroadcastChannel('activation-ready');
+    const bc = new PrerenderChannel('activation-ready');
     bc.postMessage('ready for activation');
   }
 
+  new PrerenderChannel('close').addEventListener('message', () => {
+    window.close();
+  });
   document.onprerenderingchange = () => {
     assert_false(document.prerendering);
 
@@ -43,20 +46,17 @@
     const win = window.open('empty.html', '_blank');
 
     // Send the result to the test runner page.
-    const bc = new BroadcastChannel('result');
+    const bc = new PrerenderChannel('result');
     if (win) {
       bc.postMessage('opened');
       win.close();
     } else {
       bc.postMessage('failed to open');
     }
-
-    // Close this window for cleanup.
-    window.close();
   };
 }
 
-if (location.search === '?prerendering') {
+if (new URLSearchParams(location.search).has('prerendering')) {
   runAsPrerenderingPage();
 } else {
   runAsTriggerPage();
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-resize.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-resize.html
index d8ae493..8b6172e 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-resize.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/window-resize.html
@@ -8,7 +8,7 @@
   try {
     func();
   } catch (e) {
-    const testChannel = new BroadcastChannel('test-channel');
+    const testChannel = new PrerenderChannel('test-channel');
     testChannel.postMessage({status: 'FAIL: ' + e});
   }
 }
@@ -52,7 +52,7 @@
     }
   });
 
-  const bc = new BroadcastChannel('test-channel');
+  const bc = new PrerenderChannel('test-channel');
   bc.postMessage({
     'status': 'PASS',
     'prevRect': prevRect,
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-focus.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-focus.html
index 10df5b8..1149b8bd 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-focus.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-focus.html
@@ -13,8 +13,6 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('prerender-channel');
-
   document.getElementById('prerenderTextField').focus();
   assert_true(
       document.hasFocus(),
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-notification.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-notification.https.html
index 964c408..e9d3ba2 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-notification.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-notification.https.html
@@ -11,6 +11,7 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -18,7 +19,8 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('test-channel');
+  const uid = token();
+  const bc = new PrerenderChannel('test-channel', uid);
   t.add_cleanup(_ => bc.close());
 
   await test_driver.set_permission({name: 'notifications'}, 'granted', true);
@@ -29,7 +31,7 @@
       once: true
     });
   });
-  const url = `resources/notification.html`;
+  const url = `resources/notification.html?uid=${uid}`;
   window.open(url, '_blank', 'noopener');
 
   const result = await gotMessage;
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-presentation-request.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-presentation-request.https.html
index 75e55e27..5f77422 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-presentation-request.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-presentation-request.https.html
@@ -3,6 +3,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -10,7 +11,8 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('prerender-channel');
+  const uid = token();
+  const bc = new PrerenderChannel('prerender-channel', uid);
 
   const gotMessage = new Promise(resolve => {
     bc.addEventListener('message', e => {
@@ -21,7 +23,7 @@
   });
 
   // Start prerendering a page that attempts to start presentation.
-  startPrerendering(`resources/presentation-request.html`);
+  startPrerendering(`resources/presentation-request.html?uid=${uid}`);
 
   const result = await gotMessage;
   assert_equals(result, 'request failed');
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-move.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-move.html
index fca3b537..e801131 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-move.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-move.html
@@ -2,6 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -12,7 +13,8 @@
 // See https://github.com/jeremyroman/alternate-loading-modes/issues/73.
 ['moveTo', 'moveBy'].forEach(moveFunc => {
   promise_test(async t => {
-    const bc = new BroadcastChannel('test-channel');
+    const uid = token();
+    const bc = new PrerenderChannel('test-channel', uid);
     t.add_cleanup(_ => bc.close());
 
     const gotMessage = new Promise(resolve => {
@@ -21,7 +23,7 @@
       }, {once: true});
     });
 
-    const url = `resources/window-move.html?move=${moveFunc}`;
+    const url = `resources/window-move.html?move=${moveFunc}&uid=${uid}`;
 
     // We have to open a new window to run the test, since a window that was
     // not created by window.open() cannot be moved.
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-open.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-open.html
index 33ec5b15..5de23ef 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-open.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-open.html
@@ -2,6 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -10,15 +11,17 @@
 
 function runTest(test_file, expectation, description) {
   promise_test(async t => {
+    const uid = token();
     // Run test in a new window for test isolation.
-    window.open(test_file, '_blank', 'noopener');
+    window.open(`${test_file}?uid=${uid}`, '_blank', 'noopener');
 
     // Wait until the prerendered page sends the result.
-    const bc = new BroadcastChannel('result');
-    const gotMessage = new Promise(r => bc.onmessage = e => r(e.data));
-    const result = await gotMessage;
-
-    assert_equals(await gotMessage, expectation);
+    const bc = new PrerenderChannel('result', uid);
+    t.add_cleanup(() => {
+      new PrerenderChannel('close', uid).postMessage('close');
+    })
+    const result = await new Promise(r => bc.addEventListener('message', e => r(e.data)));
+    assert_equals(result, expectation);
   }, description);
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-resize.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-resize.html
index 5f02ac6..20a71b4 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-resize.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/restriction-window-resize.html
@@ -2,6 +2,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -13,7 +14,8 @@
 ['resizeTo', 'resizeBy'].forEach(resizeFunc => {
   promise_test(
       async t => {
-        const bc = new BroadcastChannel('test-channel');
+        const uid = token();
+        const bc = new PrerenderChannel('test-channel', uid);
         t.add_cleanup(_ => bc.close());
 
         const gotMessage = new Promise(resolve => {
@@ -22,7 +24,7 @@
           }, {once: true});
         });
 
-        const url = `resources/window-resize.html?resize=${resizeFunc}`;
+        const url = `resources/window-resize.html?resize=${resizeFunc}&uid=${uid}`;
 
         // We have to open a new window to run the test, since a window that was
         // not created by window.open() cannot be resized.
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/storage-foundation.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/storage-foundation.https.html
index ca1b936..6be77ba 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/storage-foundation.https.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/storage-foundation.https.html
@@ -3,6 +3,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -32,7 +33,8 @@
 }
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('prerender-channel');
+  const uid = token();
+  const bc = new PrerenderChannel('prerender-channel', uid);
 
   const gotMessage = new Promise(resolve => {
     bc.addEventListener('message', e => {
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/visibility-state.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/visibility-state.html
index 1affe54..f8d3ccd9 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/visibility-state.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/visibility-state.html
@@ -3,6 +3,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -10,7 +11,8 @@
 setup(() => assertSpeculationRulesIsSupported());
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('test-channel');
+  const uid = token();
+  const bc = new PrerenderChannel('test-channel', uid);
 
   const gotMessage = new Promise(resolve => {
     bc.addEventListener('message', e => {
@@ -19,7 +21,7 @@
       once: true
     });
   });
-  const url = `resources/visibility-state-check.html`;
+  const url = `resources/visibility-state-check.html?uid=${uid}`;
   window.open(url, '_blank', 'noopener');
 
   const result = await gotMessage;
@@ -47,6 +49,8 @@
     assert_equals(result[i].prerendering, expected[i].prerendering,
       `prerendering${i}`);
   }
+
+  new PrerenderChannel('close', uid).postMessage('')
 }, 'The visibilityState must be updated after prerendering.');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/web-database.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/web-database.html
index 3e59c3c..3ef1141 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/web-database.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/web-database.html
@@ -3,6 +3,7 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="resources/utils.js"></script>
 <body>
 <script>
@@ -38,7 +39,8 @@
 }
 
 promise_test(async t => {
-  const bc = new BroadcastChannel('prerender-channel');
+  const uid = token();
+  const bc = new PrerenderChannel('prerender-channel', uid);
 
   const gotMessage = new Promise(resolve => {
     bc.addEventListener('message', e => {
@@ -53,7 +55,7 @@
         'primary page should be able to execute statements from Web Database.');
 
   // Start prerendering a page that attempts to access Web Database.
-  startPrerendering('resources/web-database-access.html');
+  startPrerendering(`resources/web-database-access.html?uid=${uid}`);
   const result = await gotMessage;
 
   assert_equals(
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
new file mode 100644
index 0000000..6e471cab
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Fetching modulepreload should not block the window's load event assert_equals: resources/dummy.js?pipe=trickle(d5) expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
new file mode 100644
index 0000000..4d9c5ba
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
new file mode 100644
index 0000000..4d9c5ba
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
new file mode 100644
index 0000000..6e471cab
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Fetching modulepreload should not block the window's load event assert_equals: resources/dummy.js?pipe=trickle(d5) expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
new file mode 100644
index 0000000..6e471cab
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win10/external/wpt/preload/avoid-delaying-onload-link-modulepreload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Fetching modulepreload should not block the window's load event assert_equals: resources/dummy.js?pipe=trickle(d5) expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt b/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-presentation-request.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt b/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win10/virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
index 242ff5ea..4d9c5ba 100644
--- a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
+++ b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-arc-circumference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-circumference-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-circumference-expected.png
index 552c2d6..6b204fb 100644
--- a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-circumference-expected.png
+++ b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-circumference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
index 3693990..b2fec4c2 100644
--- a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
+++ b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
index 0b30d9b5..0b31a0d 100644
--- a/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
+++ b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index b687001..3e2001e 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2671,6 +2671,7 @@
     getter protocol
     getter provider
     method constructor
+    method logout
 interface File : Blob
     attribute @@toStringTag
     getter lastModified
diff --git a/third_party/dav1d/dav1d_generated.gni b/third_party/dav1d/dav1d_generated.gni
index 8a11cf1..9e48e42 100644
--- a/third_party/dav1d/dav1d_generated.gni
+++ b/third_party/dav1d/dav1d_generated.gni
@@ -11,10 +11,11 @@
   "libdav1d/src/x86/cdef_avx512.asm",
   "libdav1d/src/x86/cdef_sse.asm",
   "libdav1d/src/x86/cpuid.asm",
-  "libdav1d/src/x86/film_grain16_avx2.asm",
-  "libdav1d/src/x86/film_grain16_sse.asm",
-  "libdav1d/src/x86/film_grain_avx2.asm",
-  "libdav1d/src/x86/film_grain_sse.asm",
+  "libdav1d/src/x86/filmgrain16_avx2.asm",
+  "libdav1d/src/x86/filmgrain16_sse.asm",
+  "libdav1d/src/x86/filmgrain_avx2.asm",
+  "libdav1d/src/x86/filmgrain_avx512.asm",
+  "libdav1d/src/x86/filmgrain_sse.asm",
   "libdav1d/src/x86/ipred16_avx2.asm",
   "libdav1d/src/x86/ipred16_avx512.asm",
   "libdav1d/src/x86/ipred16_sse.asm",
@@ -49,7 +50,7 @@
 
 x86_template_sources = [
   "libdav1d/src/x86/cdef_init_tmpl.c",
-  "libdav1d/src/x86/film_grain_init_tmpl.c",
+  "libdav1d/src/x86/filmgrain_init_tmpl.c",
   "libdav1d/src/x86/ipred_init_tmpl.c",
   "libdav1d/src/x86/itx_init_tmpl.c",
   "libdav1d/src/x86/loopfilter_init_tmpl.c",
@@ -60,8 +61,8 @@
 arm32_asm_sources = [
   "libdav1d/src/arm/32/cdef.S",
   "libdav1d/src/arm/32/cdef16.S",
-  "libdav1d/src/arm/32/film_grain.S",
-  "libdav1d/src/arm/32/film_grain16.S",
+  "libdav1d/src/arm/32/filmgrain.S",
+  "libdav1d/src/arm/32/filmgrain16.S",
   "libdav1d/src/arm/32/ipred.S",
   "libdav1d/src/arm/32/ipred16.S",
   "libdav1d/src/arm/32/itx.S",
@@ -80,8 +81,8 @@
 arm64_asm_sources = [
   "libdav1d/src/arm/64/cdef.S",
   "libdav1d/src/arm/64/cdef16.S",
-  "libdav1d/src/arm/64/film_grain.S",
-  "libdav1d/src/arm/64/film_grain16.S",
+  "libdav1d/src/arm/64/filmgrain.S",
+  "libdav1d/src/arm/64/filmgrain16.S",
   "libdav1d/src/arm/64/ipred.S",
   "libdav1d/src/arm/64/ipred16.S",
   "libdav1d/src/arm/64/itx.S",
@@ -99,7 +100,7 @@
 
 arm_template_sources = [
   "libdav1d/src/arm/cdef_init_tmpl.c",
-  "libdav1d/src/arm/film_grain_init_tmpl.c",
+  "libdav1d/src/arm/filmgrain_init_tmpl.c",
   "libdav1d/src/arm/ipred_init_tmpl.c",
   "libdav1d/src/arm/itx_init_tmpl.c",
   "libdav1d/src/arm/loopfilter_init_tmpl.c",
@@ -111,7 +112,7 @@
   "libdav1d/src/cdef_apply_tmpl.c",
   "libdav1d/src/cdef_tmpl.c",
   "libdav1d/src/fg_apply_tmpl.c",
-  "libdav1d/src/film_grain_tmpl.c",
+  "libdav1d/src/filmgrain_tmpl.c",
   "libdav1d/src/ipred_prepare_tmpl.c",
   "libdav1d/src/ipred_tmpl.c",
   "libdav1d/src/itx_tmpl.c",
@@ -134,7 +135,7 @@
   "libdav1d/src/dequant_tables.h",
   "libdav1d/src/env.h",
   "libdav1d/src/fg_apply.h",
-  "libdav1d/src/film_grain.h",
+  "libdav1d/src/filmgrain.h",
   "libdav1d/src/getbits.h",
   "libdav1d/src/internal.h",
   "libdav1d/src/intra_edge.h",
diff --git a/third_party/dav1d/generate_source.py b/third_party/dav1d/generate_source.py
index 5c319fa9..e0f817d 100755
--- a/third_party/dav1d/generate_source.py
+++ b/third_party/dav1d/generate_source.py
@@ -49,7 +49,8 @@
 
 def _WriteGn(fd):
   fd.write(_COPYRIGHT)
-  _WriteArray(fd, "x86_asm_sources", _Glob("libdav1d/src/x86/*.asm"))
+  _WriteArray(fd, "x86_asm_sources", _Glob("libdav1d/src/x86/*.asm"),
+              ["libdav1d/src/x86/filmgrain_common.asm"])
   _WriteArray(fd, "x86_template_sources", _Glob("libdav1d/src/x86/*_tmpl.c"))
 
   _WriteArray(fd, "arm32_asm_sources", _Glob("libdav1d/src/arm/32/*.S"),
diff --git a/third_party/dav1d/version/vcs_version.h b/third_party/dav1d/version/vcs_version.h
index b8cfe53e..d11c4c2 100644
--- a/third_party/dav1d/version/vcs_version.h
+++ b/third_party/dav1d/version/vcs_version.h
@@ -1,2 +1,2 @@
 /* auto-generated, do not edit */
-#define DAV1D_VERSION "0.9.2-151-gb010080"
+#define DAV1D_VERSION "0.9.2-157-g3262e3d"
diff --git a/third_party/google_trust_services/OWNERS b/third_party/google_trust_services/OWNERS
index 0faa4b5f..d1d87db 100644
--- a/third_party/google_trust_services/OWNERS
+++ b/third_party/google_trust_services/OWNERS
@@ -1,2 +1,3 @@
 asymmetric@chromium.org
 awhalley@chromium.org
+hchao@chromium.org
diff --git a/third_party/iaccessible2/OWNERS b/third_party/iaccessible2/OWNERS
index 809a9b0..895c10f 100644
--- a/third_party/iaccessible2/OWNERS
+++ b/third_party/iaccessible2/OWNERS
@@ -1,4 +1,3 @@
-aboxhall@chromium.org
 dtseng@chromium.org
 nektar@chromium.org
 aleventhal@chromium.org
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 399c8ca..6e450f7d 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -671,6 +671,8 @@
       '-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
       # Don't use curl.
       '-DLLVM_ENABLE_CURL=OFF',
+      # Build libclang.a as well as libclang.so
+      '-DLIBCLANG_BUILD_STATIC=ON',
   ]
 
   if args.gcc_toolchain:
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 7b3fded..f7f718c5 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -36,7 +36,7 @@
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
 CLANG_REVISION = 'llvmorg-15-init-1995-g5bec1ea7'
-CLANG_SUB_REVISION = 3
+CLANG_SUB_REVISION = 4
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '15.0.0'
diff --git a/tools/media_engagement_preload/OWNERS b/tools/media_engagement_preload/OWNERS
index 3e01155..95da7298 100644
--- a/tools/media_engagement_preload/OWNERS
+++ b/tools/media_engagement_preload/OWNERS
@@ -1 +1,2 @@
 liberato@chromium.org
+evliu@google.com
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6844c5d8..b6b322a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -37504,6 +37504,7 @@
   <int value="16" label="IdTokenNoResponse"/>
   <int value="17" label="IdTokenInvalidResponse"/>
   <int value="18" label="IdTokenInvalidRequest"/>
+  <int value="19" label="ClientMetadataMissingPrivacyPolicyUrl"/>
 </enum>
 
 <enum name="FedCmRevokeStatus">
@@ -52233,7 +52234,6 @@
   <int value="-1565683260" label="EnableSwitchAccessPointScanning:disabled"/>
   <int value="-1564232131"
       label="OmniboxOnDeviceHeadProviderNonIncognito:enabled"/>
-  <int value="-1563539313" label="PhoneHubRecentApps:enabled"/>
   <int value="-1563280385" label="Floss:enabled"/>
   <int value="-1562065709" label="ForceEnableDevicesPage:enabled"/>
   <int value="-1561252720" label="AutofillCreditCardUploadFeedback:disabled"/>
@@ -54010,6 +54010,7 @@
   <int value="-344357771"
       label="WebAuthenticationCrosPlatformAuthenticator:enabled"/>
   <int value="-344343842" label="disable-experimental-app-list"/>
+  <int value="-343971624" label="BorealisPermitted:disabled"/>
   <int value="-343769596" label="ServiceWorkerScriptStreaming:disabled"/>
   <int value="-343314036" label="WebBluetooth:enabled"/>
   <int value="-342421456" label="EnableOverviewRoundedCorners:disabled"/>
@@ -54316,7 +54317,6 @@
   <int value="-118993417" label="TLS13HardeningForLocalAnchors:disabled"/>
   <int value="-118897826" label="IntensiveWakeUpThrottling:enabled"/>
   <int value="-116692797" label="HTTPAuthCommittedInterstitials:enabled"/>
-  <int value="-116172457" label="PhoneHubRecentApps:disabled"/>
   <int value="-115834377" label="EnableUnifiedMultiDeviceSetup:disabled"/>
   <int value="-115261987" label="DesksTemplates:disabled"/>
   <int value="-114807608" label="TerminalSystemApp:disabled"/>
@@ -54944,6 +54944,7 @@
   <int value="327045548" label="SafeSearchUrlReporting:enabled"/>
   <int value="328722396" label="NTPCondensedLayout:disabled"/>
   <int value="330138076" label="enable-clear-browsing-data-counters"/>
+  <int value="330319312" label="EnableVariableRefreshRate:enabled"/>
   <int value="330439654" label="SyncPseudoUSSExtensions:enabled"/>
   <int value="330627288" label="ArcTouchModeMouse:disabled"/>
   <int value="330653520" label="ChromeShareHighlightsAndroid:enabled"/>
@@ -57195,6 +57196,7 @@
   <int value="1900529524" label="disable-touch-drag-drop"/>
   <int value="1901640438" label="EnableLauncherSearchNormalization:disabled"/>
   <int value="1902274960" label="WebViewLogFirstPartyPageTimeSpent:enabled"/>
+  <int value="1902894451" label="BorealisPermitted:enabled"/>
   <int value="1903206193" label="NewWindowAppMenu:disabled"/>
   <int value="1903444879" label="VirtualKeyboardApi:disabled"/>
   <int value="1905465678" label="ContextualSearchSingleActions:enabled"/>
@@ -57383,6 +57385,7 @@
   <int value="2015335629" label="shelf-hover-previews"/>
   <int value="2015547864" label="NetworkServiceInProcess:enabled"/>
   <int value="2018956925" label="UseAAudioDriver:disabled"/>
+  <int value="2019319052" label="EnableVariableRefreshRate:disabled"/>
   <int value="2020107447" label="AndroidPayIntegrationV1:enabled"/>
   <int value="2020898714" label="HudDisplayForPerformanceMetrics:enabled"/>
   <int value="2021142866" label="FeedbackReportQuestionnaire:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index dc043d8..193fe6f 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1699,9 +1699,8 @@
 
 <histogram name="Android.Messages.Dismissed{MessageIdentifier}"
     enum="MessageDismissReason" expires_after="2022-06-01">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Recorded once per message when the message is dismissed to capture the
     dismissal reason (e.g. primary button tap, timeout). The histogram can be
@@ -1712,9 +1711,8 @@
 
 <histogram name="Android.Messages.Enqueued" enum="MessageIdentifier"
     expires_after="2022-07-31">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Records the message identifier each time a message is enqueued through
     MessageDispatcher (the entry point to Chrome for Android's messages system).
@@ -1725,9 +1723,8 @@
 
 <histogram name="Android.Messages.Enqueued.Hidden" enum="MessageIdentifier"
     expires_after="2022-07-31">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Records the message identifier when an enqueued message is not immediately
     displayed either because there is another message on the secreen or because
@@ -1739,9 +1736,8 @@
 
 <histogram name="Android.Messages.Enqueued.Hiding" enum="MessageIdentifier"
     expires_after="2022-06-01">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Recorded when an enqueued message is not immediately displayed. This metric
     records message identifier of a message currently displayed on the screen
@@ -1753,9 +1749,8 @@
 
 <histogram name="Android.Messages.Enqueued.Visible" enum="MessageIdentifier"
     expires_after="2022-07-31">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Records the message identifier when a message is displayed immediately when
     it is enqueued. This includes cases when there are no other messages on the
@@ -1766,9 +1761,8 @@
 
 <histogram name="Android.Messages.TimeToAction.Dismiss{MessageIdentifier}"
     units="ms" expires_after="2022-06-01">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Records the time interval the message was displayed on the screen before the
     user dismissed it with a gesture. The metric is NOT recorded when the user
@@ -1782,9 +1776,8 @@
 
 <histogram name="Android.Messages.TimeToAction{MessageIdentifier}" units="ms"
     expires_after="2022-06-01">
-  <owner>pavely@chromium.org</owner>
   <owner>lazzzis@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
+  <owner>src/components/messages/OWNERS</owner>
   <summary>
     Records the time interval the message was displayed on the screen before the
     user explicitly dismissed it. The metric is recorded when the user presses
diff --git a/tools/metrics/histograms/metadata/assistant/histograms.xml b/tools/metrics/histograms/metadata/assistant/histograms.xml
index 6fbdf27..6def44a1 100644
--- a/tools/metrics/histograms/metadata/assistant/histograms.xml
+++ b/tools/metrics/histograms/metadata/assistant/histograms.xml
@@ -161,7 +161,7 @@
 </histogram>
 
 <histogram name="Assistant.OptInFlowStatus" enum="AssistantOptInFlowStatus"
-    expires_after="2022-04-10">
+    expires_after="2023-04-10">
   <owner>updowndota@chromium.org</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>Record the status of the Assistant opt-in flow.</summary>
@@ -379,7 +379,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Intent" enum="QuickAnswersIntentType"
-    expires_after="2022-04-10">
+    expires_after="2023-04-10">
   <owner>updowndota@chromium.org</owner>
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
@@ -462,7 +462,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.SelectedContent.Length" units="characters"
-    expires_after="2022-04-10">
+    expires_after="2023-04-10">
   <owner>updowndota@chromium.org</owner>
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index b70d765..8d2328c 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -786,7 +786,7 @@
   </summary>
 </histogram>
 
-<histogram base="true"
+<histogram
     name="CompositorLatency.CompositorOnlyFrame{InteractionType}.{StageType}"
     units="microseconds" expires_after="2023-03-01">
   <owner>sadrul@chromium.org</owner>
@@ -848,8 +848,7 @@
   </summary>
 </histogram>
 
-<histogram base="true"
-    name="CompositorLatency{ReportType}{InteractionType}.{StageType}"
+<histogram name="CompositorLatency{ReportType}{InteractionType}.{StageType}"
     units="microseconds" expires_after="2022-07-24">
   <owner>sadrul@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
diff --git a/tools/perf/README.md b/tools/perf/README.md
index 28f98d5..5483b125 100644
--- a/tools/perf/README.md
+++ b/tools/perf/README.md
@@ -125,13 +125,6 @@
 
 [wpr]: https://github.com/catapult-project/catapult/tree/master/web_page_replay_go
 
-## pinpoint_cli
-
-A command line interface to the [pinpoint][] service. Allows to create new jobs,
-check the status of jobs, and fetch their measurements as csv files.
-
-[pinpoint]: https://pinpoint-dot-chromeperf.appspot.com
-
 ## flakiness_cli
 
 A command line interface to the [flakiness dashboard][].
diff --git a/tools/perf/cli_tools/pinpoint_cli/__init__.py b/tools/perf/cli_tools/pinpoint_cli/__init__.py
deleted file mode 100644
index 1adf20d2..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Copyright 2018 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.
diff --git a/tools/perf/cli_tools/pinpoint_cli/commands.py b/tools/perf/cli_tools/pinpoint_cli/commands.py
deleted file mode 100644
index 0667257..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/commands.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2018 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.
-
-from __future__ import print_function
-
-import csv
-import json
-import logging
-import ntpath
-import posixpath
-import sys
-
-from cli_tools.pinpoint_cli import histograms_df
-from cli_tools.pinpoint_cli import job_results
-from core.services import isolate_service
-from core.services import pinpoint_service
-
-
-def StartJobFromConfig(config_path):
-  """Start a pinpoint job based on a config file."""
-  src = sys.stdin if config_path == '-' else open(config_path)
-  with src as f:
-    config = json.load(f)
-
-  if not isinstance(config, dict):
-    raise ValueError('Invalid job config')
-
-  # An absent comparison_mode denotes a tryjob configuration.
-  if 'comparison_mode' not in config:
-    config['comparison_mode'] = 'try'
-
-  # As of crrev.com/c/1965875 try jobs must specify a base git hash.
-  if config['comparison_mode'] == 'try' and 'base_git_hash' not in config:
-    config['base_git_hash'] = config['start_git_hash']
-    del config['start_git_hash']
-    del config['end_git_hash']
-
-  response = pinpoint_service.NewJob(**config)
-  print('Started:', response['jobUrl'])
-
-
-def CheckJobStatus(job_ids):
-  for job_id in job_ids:
-    job = pinpoint_service.Job(job_id)
-    print('%s: %s' % (job_id, job['status'].lower()))
-
-
-def DownloadJobResultsAsCsv(job_ids, only_differences, output_file):
-  """Download the perf results of a job as a csv file."""
-  with open(output_file, 'wb') as f:
-    writer = csv.writer(f)
-    writer.writerow(('job_id', 'change', 'isolate') + histograms_df.COLUMNS)
-    num_rows = 0
-    for job_id in job_ids:
-      job = pinpoint_service.Job(job_id, with_state=True)
-      os_path = _OsPathFromJob(job)
-      results_file = os_path.join(
-          job['arguments']['benchmark'], 'perf_results.json')
-      print('Fetching results for %s job %s:' % (job['status'].lower(), job_id))
-      for change_id, isolate_hash in job_results.IterTestOutputIsolates(
-          job, only_differences):
-        print('- isolate: %s ...' % isolate_hash)
-        try:
-          histograms = isolate_service.RetrieveFile(isolate_hash, results_file)
-        except KeyError:
-          logging.warning('Skipping over isolate, results not found.')
-          continue
-        for row in histograms_df.IterRows(json.loads(histograms)):
-          writer.writerow((job_id, change_id, isolate_hash) + row)
-          num_rows += 1
-  print('Wrote data from %d histograms in %s.' % (num_rows, output_file))
-
-
-def _OsPathFromJob(job):
-  if job['arguments']['configuration'].lower().startswith('win'):
-    return ntpath
-  else:
-    return posixpath
diff --git a/tools/perf/cli_tools/pinpoint_cli/histograms_df.py b/tools/perf/cli_tools/pinpoint_cli/histograms_df.py
deleted file mode 100644
index 3887515..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/histograms_df.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2018 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.
-
-from core.external_modules import pandas
-
-from tracing.value import histogram_set
-from tracing.value.diagnostics import generic_set
-
-
-_PROPERTIES = (
-    ('name', 'name'),
-    ('unit', 'unit'),
-    ('mean', 'average'),
-    ('stdev', 'standard_deviation'),
-    ('count', 'num_values')
-)
-_DIAGNOSTICS = (
-    ('run_label', 'labels'),
-    ('benchmark', 'benchmarks'),
-    ('story', 'stories'),
-    ('benchmark_start', 'benchmarkStart'),
-    ('device_id', 'deviceIds'),
-    ('trace_url', 'traceUrls')
-)
-COLUMNS = tuple(key for key, _ in _PROPERTIES) + tuple(
-    key for key, _ in _DIAGNOSTICS)
-
-
-def _DiagnosticValueToStr(value):
-  if value is None:
-    return ''
-  elif isinstance(value, generic_set.GenericSet):
-    return ','.join(str(v) for v in value)
-  else:
-    return str(value)
-
-
-def IterRows(histogram_dicts):
-  """Iterate over histogram dicts yielding rows for a DataFrame or csv."""
-  histograms = histogram_set.HistogramSet()
-  histograms.ImportDicts(histogram_dicts)
-  for hist in histograms:
-    row = [getattr(hist, name) for _, name in _PROPERTIES]
-    row.extend(
-        _DiagnosticValueToStr(hist.diagnostics.get(name))
-        for _, name in _DIAGNOSTICS)
-    yield tuple(row)
-
-
-def DataFrame(histogram_dicts):
-  """Turn a list of histogram dicts into a pandas DataFrame."""
-  df = pandas.DataFrame.from_records(
-      IterRows(histogram_dicts), columns=COLUMNS)
-  df['benchmark_start'] = pandas.to_datetime(df['benchmark_start'])
-  return df
diff --git a/tools/perf/cli_tools/pinpoint_cli/histograms_df_test.py b/tools/perf/cli_tools/pinpoint_cli/histograms_df_test.py
deleted file mode 100644
index 6335a250..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/histograms_df_test.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright 2018 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 unittest
-
-import six
-
-from cli_tools.pinpoint_cli import histograms_df
-from core.external_modules import pandas
-
-from tracing.value import histogram
-from tracing.value import histogram_set
-from tracing.value.diagnostics import date_range
-from tracing.value.diagnostics import generic_set
-
-
-def TestHistogram(name, units, values, **kwargs):
-  def DiagnosticValue(value):
-    if isinstance(value, six.integer_types):
-      return date_range.DateRange(value)
-    elif isinstance(value, list):
-      return generic_set.GenericSet(value)
-    else:
-      raise NotImplementedError(type(value))
-
-  hist = histogram.Histogram(name, units)
-  hist.diagnostics.update(
-      (key, DiagnosticValue(value)) for key, value in kwargs.items())
-  for value in values:
-    hist.AddSample(value)
-  return hist
-
-
-@unittest.skipIf(pandas is None, 'pandas not available')
-class TestHistogramsDf(unittest.TestCase):
-  def testIterRows(self):
-    run1 = {'benchmarkStart': 1234567890000, 'labels': ['run1'],
-            'benchmarks': ['system_health'], 'deviceIds': ['device1']}
-    # Second run on same device ten minutes later.
-    run2 = {'benchmarkStart': 1234567890000 + 600000, 'labels': ['run2'],
-            'benchmarks': ['system_health'], 'deviceIds': ['device1']}
-    hists = histogram_set.HistogramSet([
-        TestHistogram('startup', 'ms', [8, 10, 12], stories=['story1'],
-                      traceUrls=['http://url/to/trace1'], **run1),
-        TestHistogram('memory', 'sizeInBytes', [256], stories=['story2'],
-                      traceUrls=['http://url/to/trace2'], **run1),
-        TestHistogram('memory', 'sizeInBytes', [512], stories=['story2'],
-                      traceUrls=['http://url/to/trace3'], **run2),
-    ])
-
-    expected = [
-        ('startup', 'ms', 10.0, 2.0, 3, 'run1', 'system_health',
-         'story1', '2009-02-13 23:31:30', 'device1', 'http://url/to/trace1'),
-        ('memory', 'sizeInBytes', 256.0, 0.0, 1, 'run1', 'system_health',
-         'story2', '2009-02-13 23:31:30', 'device1', 'http://url/to/trace2'),
-        ('memory', 'sizeInBytes', 512.0, 0.0, 1, 'run2', 'system_health',
-         'story2', '2009-02-13 23:41:30', 'device1', 'http://url/to/trace3'),
-    ]
-    six.assertCountEqual(self, histograms_df.IterRows(hists.AsDicts()),
-                         expected)
-
-  def testDataFrame(self):
-    run1 = {'benchmarkStart': 1234567890000, 'labels': ['run1'],
-            'benchmarks': ['system_health'], 'deviceIds': ['device1']}
-    # Second run on same device ten minutes later.
-    run2 = {'benchmarkStart': 1234567890000 + 600000, 'labels': ['run2'],
-            'benchmarks': ['system_health'], 'deviceIds': ['device1']}
-    hists = histogram_set.HistogramSet([
-        TestHistogram('startup', 'ms', [8, 10, 12], stories=['story1'],
-                      traceUrls=['http://url/to/trace1'], **run1),
-        TestHistogram('memory', 'sizeInBytes', [256], stories=['story2'],
-                      traceUrls=['http://url/to/trace2'], **run1),
-        TestHistogram('memory', 'sizeInBytes', [384], stories=['story2'],
-                      traceUrls=['http://url/to/trace3'], **run2),
-    ])
-    df = histograms_df.DataFrame(hists.AsDicts())
-
-    # Poke at the data frame and check a few known facts about our fake data:
-    # It has 3 histograms.
-    self.assertEqual(len(df), 3)
-    # The benchmark has two stories.
-    six.assertCountEqual(self, df['story'].unique(), ['story1', 'story2'])
-    # We recorded three traces.
-    self.assertEqual(len(df['trace_url'].unique()), 3)
-    # All benchmarks ran on the same device.
-    self.assertEqual(len(df['device_id'].unique()), 1)
-    # There is a memory regression between runs 1 and 2.
-    memory = df.set_index(['name', 'run_label']).loc['memory']['mean']
-    self.assertEqual(memory['run2'] - memory['run1'], 128.0)
-    # Ten minutes passed between the two benchmark runs.
-    self.assertEqual(df['benchmark_start'].max() - df['benchmark_start'].min(),
-                     pandas.Timedelta('10 minutes'))
diff --git a/tools/perf/cli_tools/pinpoint_cli/job_results.py b/tools/perf/cli_tools/pinpoint_cli/job_results.py
deleted file mode 100644
index 29cb5143..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/job_results.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2018 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.
-
-
-def ChangeToStr(change):
-  """Turn a pinpoint change dict into a string id."""
-  change_id = ','.join(
-      '{repository}@{git_hash}'.format(**commit)
-      for commit in change['commits'])
-  if 'patch' in change:
-    change_id += '+' + change['patch']['url']
-  return change_id
-
-
-def IterTestOutputIsolates(job, only_differences=False):
-  """Iterate over test execution results for all changes tested in the job.
-
-  Args:
-    job: A pinpoint job dict with state.
-
-  Yields:
-    (change_id, isolate_hash) pairs for each completed test execution found in
-    the job.
-  """
-  quests = job['quests']
-  for change_state in job['state']:
-    if only_differences and not any(
-        v == 'different' for v in change_state['comparisons'].values()):
-      continue
-    change_id = ChangeToStr(change_state['change'])
-    for attempt in change_state['attempts']:
-      executions = dict(zip(quests, attempt['executions']))
-      if 'Test' not in executions:
-        continue
-      test_run = executions['Test']
-      if not test_run['completed']:
-        continue
-      try:
-        isolate_hash = next(
-            d['value'] for d in test_run['details'] if d['key'] == 'isolate')
-      except StopIteration:
-        continue
-      yield change_id, isolate_hash
diff --git a/tools/perf/cli_tools/pinpoint_cli/job_results_test.py b/tools/perf/cli_tools/pinpoint_cli/job_results_test.py
deleted file mode 100644
index 885de37..0000000
--- a/tools/perf/cli_tools/pinpoint_cli/job_results_test.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright 2018 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 unittest
-
-from cli_tools.pinpoint_cli import job_results
-
-
-def Change(*args, **kwargs):
-  commits = []
-  for arg in args:
-    repository, git_hash = arg.split('@')
-    commits.append({'repository': repository, 'git_hash': git_hash})
-  change = {'commits': commits}
-  patch = kwargs.pop('patch', None)
-  if patch is not None:
-    change['patch'] = {'url': patch}
-  return change
-
-
-def Execution(**kwargs):
-  execution = {'completed': kwargs.pop('completed', True)}
-  execution['details'] = [
-      {'key': k, 'value': v} for k, v in kwargs.items()]
-  return execution
-
-
-class TestJobResults(unittest.TestCase):
-  def testChangeToStr(self):
-    self.assertEqual(
-        job_results.ChangeToStr(Change('src@1234')),
-        'src@1234')
-    self.assertEqual(
-        job_results.ChangeToStr(
-            Change('src@1234', 'v8@4567', patch='crrev.com/c/123')),
-        'src@1234,v8@4567+crrev.com/c/123')
-
-  def testIterTestOutputIsolates(self):
-    job = {
-        'quests': ['Build', 'Test', 'Get results'],
-        'state': [
-            {
-                'change': Change('src@1234'),
-                'attempts': [
-                    {
-                        'executions': [
-                            Execution(),  # Build
-                            Execution(isolate='results1'),  # Test
-                            Execution()  # Get results
-                        ]
-                    },
-                    {
-                        'executions': [
-                            Execution(),  # Build
-                            Execution(),  # Test (completed but failed)
-                        ]
-                    },
-                    {
-                        'executions': [
-                            Execution(),  # Build
-                            Execution(isolate='results3'),  # Test
-                            Execution(completed=False)  # Get results
-                        ]
-                    }
-                ]
-            },
-            {
-                'change': Change('src@1234', patch='crrev.com/c/123'),
-                'attempts': [
-                    {
-                        'executions': [
-                            Execution(),  # Build
-                            Execution(isolate='results4'),  # Test
-                            Execution()  # Get results
-                        ]
-                    },
-                    {
-                        'executions': [
-                            Execution(),  # Build
-                            Execution(completed=False)  # Test
-                        ]
-                    },
-                    {
-                        'executions': [
-                            Execution(completed=False)  # Build
-                        ]
-                    }
-                ]
-            }
-        ]
-    }
-    self.assertSequenceEqual(
-        list(job_results.IterTestOutputIsolates(job)),
-        [
-            ('src@1234', 'results1'),
-            ('src@1234', 'results3'),
-            ('src@1234+crrev.com/c/123', 'results4')
-        ])
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 33d31061..01af32a 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 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "602e7e88bc8db05ca82760aa5a63db3465334418",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/b6557e37c41e01d2cb0508b4c6583e23e2838a17/trace_processor_shell.exe"
+            "hash": "eee52f9d61b42294a69b5ff6de70cdd9d2921ad6",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/9d1b2dd68697e0b5d912c18674cadd1c16ef5790/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "943dfeca2624f9764f118626aa22c8d1b086e6fd",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/11de3dcf8b4e4c727906a0fb055b5493e9d2de82/trace_processor_shell"
+            "hash": "9ee7a7bf94fd8abc723a9a69100f7206e271b783",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/9d1b2dd68697e0b5d912c18674cadd1c16ef5790/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac_arm64/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
         },
         "linux": {
-            "hash": "791e1b35991b872ca7b35da945a4ee293b50a5c4",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/b6557e37c41e01d2cb0508b4c6583e23e2838a17/trace_processor_shell"
+            "hash": "8b0bd9456834b4db83fb71e612e5e1cb12bdee21",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/9d1b2dd68697e0b5d912c18674cadd1c16ef5790/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/services/isolate_service.py b/tools/perf/core/services/isolate_service.py
deleted file mode 100644
index 28a44ad..0000000
--- a/tools/perf/core/services/isolate_service.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright 2018 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 base64
-import json
-import os
-import zlib
-
-from core.services import request
-
-
-SERVICE_URL = 'https://chrome-isolated.appspot.com/_ah/api/isolateservice/v1'
-CACHE_DIR = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '..', '..', '_cached_data', 'isolates'))
-
-
-def Request(endpoint, **kwargs):
-  """Send a request to some isolate service endpoint."""
-  kwargs.setdefault('use_auth', True)
-  kwargs.setdefault('accept', 'json')
-  return request.Request(SERVICE_URL + endpoint, **kwargs)
-
-
-def Retrieve(digest):
-  """Retrieve the content stored at some isolate digest."""
-  return zlib.decompress(RetrieveCompressed(digest))
-
-
-def RetrieveFile(digest, filename):
-  """Retrieve a particular filename from an isolate container."""
-  container = json.loads(Retrieve(digest))
-  return Retrieve(container['files'][filename]['h'])
-
-
-def RetrieveCompressed(digest):
-  """Retrieve the compressed content stored at some isolate digest.
-
-  Responses are cached locally to speed up retrieving content multiple times
-  for the same digest.
-  """
-  cache_file = os.path.join(CACHE_DIR, digest)
-  if os.path.exists(cache_file):
-    with open(cache_file, 'rb') as f:
-      return f.read()
-  else:
-    if not os.path.isdir(CACHE_DIR):
-      os.makedirs(CACHE_DIR)
-    content = _RetrieveCompressed(digest)
-    with open(cache_file, 'wb') as f:
-      f.write(content)
-    return content
-
-
-def _RetrieveCompressed(digest):
-  """Retrieve the compressed content stored at some isolate digest."""
-  data = Request(
-      '/retrieve', method='POST', content_type='json',
-      data={'namespace': {'namespace': 'default-gzip'}, 'digest': digest})
-
-  if 'url' in data:
-    return request.Request(data['url'])
-  if 'content' in data:
-    return base64.b64decode(data['content'])
-  else:
-    raise NotImplementedError(
-        'Isolate %s in unknown format %s' % (digest, json.dumps(data)))
diff --git a/tools/perf/core/services/isolate_service_test.py b/tools/perf/core/services/isolate_service_test.py
deleted file mode 100644
index 79994b44..0000000
--- a/tools/perf/core/services/isolate_service_test.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright 2018 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 base64
-import json
-import os
-import shutil
-import tempfile
-import unittest
-import zlib
-
-import mock
-
-from core.services import isolate_service
-
-
-def ContentResponse(content):
-  return [{'content': base64.b64encode(zlib.compress(content.encode('utf-8')))}]
-
-
-def UrlResponse(url, content):
-  return [{'url': url}, zlib.compress(content.encode('utf-8'))]
-
-
-class TestIsolateApi(unittest.TestCase):
-  def setUp(self):
-    self.temp_dir = tempfile.mkdtemp()
-    mock.patch('core.services.isolate_service.CACHE_DIR', os.path.join(
-        self.temp_dir, 'isolate_cache')).start()
-    self.mock_request = mock.patch('core.services.request.Request').start()
-
-  def tearDown(self):
-    shutil.rmtree(self.temp_dir)
-    mock.patch.stopall()
-
-  def testRetrieve_content(self):
-    self.mock_request.side_effect = ContentResponse('OK!')
-    self.assertEqual(isolate_service.Retrieve('hash'), b'OK!')
-
-  def testRetrieve_fromUrl(self):
-    self.mock_request.side_effect = UrlResponse('http://get/response', 'OK!')
-    self.assertEqual(isolate_service.Retrieve('hash'), b'OK!')
-
-  def testRetrieveCompressed_content(self):
-    self.mock_request.side_effect = ContentResponse('OK!')
-    self.assertEqual(isolate_service.RetrieveCompressed('hash'),
-                     zlib.compress(b'OK!'))
-
-  def testRetrieveCompressed_fromUrl(self):
-    self.mock_request.side_effect = UrlResponse('http://get/response', 'OK!')
-    self.assertEqual(isolate_service.RetrieveCompressed('hash'),
-                     zlib.compress(b'OK!'))
-
-  def testRetrieveCompressed_usesCache(self):
-    self.mock_request.side_effect = ContentResponse('OK!')
-    self.assertEqual(isolate_service.RetrieveCompressed('hash'),
-                     zlib.compress(b'OK!'))
-    self.assertEqual(isolate_service.RetrieveCompressed('hash'),
-                     zlib.compress(b'OK!'))
-    # We retrieve the same hash twice, but the request is only made once.
-    self.assertEqual(self.mock_request.call_count, 1)
-
-  def testRetrieveFile_succeeds(self):
-    self.mock_request.side_effect = (
-        ContentResponse(json.dumps({'files': {'foo': {'h': 'hash2'}}})) +
-        UrlResponse('http://get/file/contents', 'nice!'))
-
-    self.assertEqual(isolate_service.RetrieveFile('hash1', 'foo'), b'nice!')
-
-  def testRetrieveFile_fails(self):
-    self.mock_request.side_effect = (
-        ContentResponse(json.dumps({'files': {'foo': {'h': 'hash2'}}})) +
-        UrlResponse('http://get/file/contents', 'nice!'))
-
-    with self.assertRaises(KeyError):
-      isolate_service.RetrieveFile('hash1', 'bar')  # File not in isolate.
diff --git a/tools/perf/examples/pinpoint_cli/bisect_job.json b/tools/perf/examples/pinpoint_cli/bisect_job.json
deleted file mode 100644
index 5c2be88..0000000
--- a/tools/perf/examples/pinpoint_cli/bisect_job.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "name": "Example bisect job",
-  "bug_id": "1022349",
-  "target": "performance_webview_test_suite",
-  "configuration": "android-go_webview-perf",
-  "comparison_mode": "performance",
-  "benchmark": "system_health.memory_mobile",
-  "story": "browse:media:facebook_photos",
-  "chart": "memory:webview:all_processes:reported_by_os:system_memory:private_dirty_size",
-  "statistic": "avg",
-  "repository": "chromium",
-  "start_git_hash": "9bb441de89f7d031992ec67585ac0732ef622222",
-  "end_git_hash": "f402331ff436d3ac12cacbd1c959641eb3524923"
-}
diff --git a/tools/perf/examples/pinpoint_cli/try_job.json b/tools/perf/examples/pinpoint_cli/try_job.json
deleted file mode 100644
index a51757ac..0000000
--- a/tools/perf/examples/pinpoint_cli/try_job.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name": "Try job to test my change",
-  "target": "performance_test_suite",
-  "configuration": "android-go-perf",
-  "benchmark": "system_health.memory_mobile",
-  "extra_test_args": "--story-tag-filter emerging_market",
-  "patch": "https://chromium-review.googlesource.com/c/v8/v8/+/1278496",
-  "repository": "chromium",
-  "start_git_hash": "HEAD",
-  "end_git_hash": "HEAD"
-}
diff --git a/tools/perf/pinpoint_cli b/tools/perf/pinpoint_cli
deleted file mode 100755
index 6222893..0000000
--- a/tools/perf/pinpoint_cli
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env vpython
-# Copyright 2018 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 argparse
-import json
-import os
-import sys
-
-from core import path_util
-path_util.AddPyUtilsToPath()
-path_util.AddTracingToPath()
-
-from core import cli_utils
-from core import external_modules
-from cli_tools.pinpoint_cli import commands
-from core.services import luci_auth
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '-v', '--verbose', action='count', default=0,
-      help='Increase verbosity level')
-  subparsers = parser.add_subparsers(dest='action')
-  subparsers.required = True
-
-  subparser = subparsers.add_parser(
-      'status', help='check the status of some pinpoint jobs')
-  subparser.add_argument(
-      'job_ids', metavar='JOB_ID', nargs='+',
-      help='one or more pinpoint job ids')
-
-  subparser = subparsers.add_parser(
-      'get-csv', help='download the perf results from jobs as a csv file')
-  subparser.add_argument(
-      '--only-differences', action='store_true',
-      help='on bisect jobs, only get data for changes immediately before/after'
-           ' differences in the comparison metric.')
-  subparser.add_argument(
-      '--output', metavar='OUTPUT_CSV', default='job_results.csv',
-      help='path to a file where to store perf results as a csv file'
-           ' (default: %(default)s)')
-  subparser.add_argument(
-      'job_ids', metavar='JOB_ID', nargs='+',
-      help='one or more pinpoint job ids')
-
-  subparser = subparsers.add_parser(
-      'start-job', help='start a new pinpoint job')
-  subparser.add_argument(
-      'config_path', metavar='CONFIG_PATH',
-      help='path to a json file with a pinpoint job configuration (see'
-           " examples/pinpoint_cli directory) or '-' to read it from stdin")
-  args = parser.parse_args()
-  cli_utils.ConfigureLogging(args.verbose)
-
-  luci_auth.CheckLoggedIn()
-  if args.action == 'status':
-    return commands.CheckJobStatus(args.job_ids)
-  elif args.action == 'get-csv':
-    return commands.DownloadJobResultsAsCsv(
-        args.job_ids, args.only_differences, args.output)
-  elif args.action == 'start-job':
-    return commands.StartJobFromConfig(args.config_path)
-  else:
-    raise NotImplementedError(args.action)
-
-
-if __name__ == '__main__':
-  external_modules.RequireModules()
-  sys.exit(main())
diff --git a/tools/rust/build_rust.py b/tools/rust/build_rust.py
index 5116640..5173a27c 100755
--- a/tools/rust/build_rust.py
+++ b/tools/rust/build_rust.py
@@ -35,12 +35,13 @@
 
 import argparse
 import collections
+import hashlib
 import os
-import sys
 import pipes
 import shutil
 import string
 import subprocess
+import sys
 
 from pathlib import Path
 
@@ -53,10 +54,16 @@
                     LLVM_BUILD_DIR, GetDefaultHostOs, RmTree, UpdatePackage)
 import build
 
-# Trunk on 2/24/2022
-RUST_REVISION = '4b043fa'
+# Trunk on 2/28/2022
+RUST_REVISION = '6343ed'
 RUST_SUB_REVISION = 1
 
+# Hash of src/stage0.json, which itself contains the stage0 toolchain hashes.
+# We trust the Rust build system checks, but to ensure it is not tampered with
+# itself check the hash.
+STAGE0_JSON_SHA256 = (
+    'a38b7ea8b8cbdb592b1a7ae8b97fa31746a2bda309597de111be4893a035070d')
+
 PACKAGE_VERSION = '%s-%s-%s-%s' % (RUST_REVISION, RUST_SUB_REVISION,
                                    CLANG_REVISION, CLANG_SUB_REVISION)
 
@@ -64,6 +71,7 @@
 
 THIRD_PARTY_DIR = os.path.join(CHROMIUM_DIR, 'third_party')
 RUST_SRC_DIR = os.path.join(THIRD_PARTY_DIR, 'rust-src')
+STAGE0_JSON_PATH = os.path.join(RUST_SRC_DIR, 'src', 'stage0.json')
 # Download crates.io dependencies to rust-src subdir (rather than $HOME/.cargo)
 CARGO_HOME_DIR = os.path.join(RUST_SRC_DIR, 'cargo-home')
 RUST_SRC_VERSION_FILE_PATH = os.path.join(RUST_SRC_DIR, 'src', 'version')
@@ -131,6 +139,21 @@
   sys.exit(1)
 
 
+def VerifyStage0JsonHash():
+  hasher = hashlib.sha256()
+  with open(STAGE0_JSON_PATH, 'rb') as input:
+    hasher.update(input.read())
+  actual_hash = hasher.hexdigest()
+
+  if actual_hash == STAGE0_JSON_SHA256:
+    return
+
+  print('src/stage0.json hash is different than expected!')
+  print('Expected hash: ' + STAGE0_JSON_SHA256)
+  print('Actual hash:   ' + actual_hash)
+  sys.exit(1)
+
+
 def Configure(llvm_libs_root):
   # Read the config.toml template file...
   with open(RUST_CONFIG_TEMPLATE_PATH, 'r') as input:
@@ -192,6 +215,11 @@
                       action='count',
                       help='run subcommands with verbosity')
   parser.add_argument(
+      '--verify-stage0-hash',
+      action='store_true',
+      help='checkout Rust, verify the stage0 hash, then immediately quit without '
+      'building. Will print the actual hash if different than expected.')
+  parser.add_argument(
       '--skip-checkout',
       action='store_true',
       help='skip Rust git checkout. Useful for trying local changes')
@@ -216,9 +244,6 @@
       'with --fetch-llvm-libs for local builds.')
   args = parser.parse_args()
 
-  if args.fetch_llvm_libs:
-    UpdatePackage('clang-libs', GetDefaultHostOs())
-
   # Get the LLVM root for libs. We use LLVM_BUILD_DIR tools either way.
   #
   # TODO(https://crbug.com/1245714): use LTO libs from LLVM_BUILD_DIR for
@@ -228,14 +253,23 @@
   else:
     llvm_libs_root = build.LLVM_BOOTSTRAP_DIR
 
+  if not args.skip_checkout:
+    CheckoutRust(RUST_REVISION, RUST_SRC_DIR)
+
+  VerifyStage0JsonHash()
+  if args.verify_stage0_hash:
+    # The above function exits and prints the actual hash if verification failed
+    # so we just quit here; if we reach this point, the hash is valid.
+    return 0
+
+  if args.fetch_llvm_libs:
+    UpdatePackage('clang-libs', GetDefaultHostOs())
+
   # Fetch GCC package to build against same libstdc++ as Clang. This function
   # will only download it if necessary.
   args.gcc_toolchain = None
   build.MaybeDownloadHostGcc(args)
 
-  if not args.skip_checkout:
-    CheckoutRust(RUST_REVISION, RUST_SRC_DIR)
-
   # Delete vendored sources and .cargo subdir. Otherwise when updating an
   # existing checkout, vendored sources will not be re-fetched leaving deps out
   # of date.
@@ -282,6 +316,8 @@
       stamp.write('rustc %s-dev (%s chromium)\n' %
                   (rust_version, PACKAGE_VERSION))
 
+  return 0
+
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index e9a2b37..7035876 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -6,6 +6,7 @@
 
 #include <stdlib.h>
 
+#include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -373,4 +374,10 @@
 const base::Feature kUiCompositorRequiredTilesOnly{
     "UiCompositorRequiredTilesOnly", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kEnableVariableRefreshRate = {
+    "EnableVariableRefreshRate", base::FEATURE_DISABLED_BY_DEFAULT};
+bool IsVariableRefreshRateEnabled() {
+  return base::FeatureList::IsEnabled(kEnableVariableRefreshRate);
+}
+
 }  // namespace features
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index d094dcbf..e14f050 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -142,7 +142,7 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 bool IsShortcutCustomizationAppEnabled();
 
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 COMPONENT_EXPORT(UI_BASE_FEATURES)
@@ -212,6 +212,11 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kUiCompositorRequiredTilesOnly;
 
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const base::Feature kEnableVariableRefreshRate;
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+bool IsVariableRefreshRateEnabled();
+
 }  // namespace features
 
 #endif  // UI_BASE_UI_BASE_FEATURES_H_
diff --git a/ui/chromeos/ui_chromeos_strings_grd/OWNERS b/ui/chromeos/ui_chromeos_strings_grd/OWNERS
index eef4eb7e..4f88613 100644
--- a/ui/chromeos/ui_chromeos_strings_grd/OWNERS
+++ b/ui/chromeos/ui_chromeos_strings_grd/OWNERS
@@ -1,3 +1,2 @@
 # For input-related naming change
-myy@chromium.org
 shend@chromium.org
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 55916ebe..9bc3ecf 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -25,6 +25,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/types/display_mode.h"
 #include "ui/display/util/display_util.h"
 #include "ui/display/util/edid_parser.h"
@@ -136,12 +137,13 @@
   }
 }
 
+template <typename T>
 int GetDrmProperty(int fd,
-                   drmModeConnector* connector,
+                   T* object,
                    const std::string& name,
                    ScopedDrmPropertyPtr* property) {
-  for (int i = 0; i < connector->count_props; ++i) {
-    ScopedDrmPropertyPtr tmp(drmModeGetProperty(fd, connector->props[i]));
+  for (uint32_t i = 0; i < static_cast<uint32_t>(object->count_props); ++i) {
+    ScopedDrmPropertyPtr tmp(drmModeGetProperty(fd, object->props[i]));
     if (!tmp)
       continue;
 
@@ -341,6 +343,30 @@
   return gfx::Size(width, height);
 }
 
+bool IsVrrCapable(int fd, drmModeConnector* connector) {
+  if (!features::IsVariableRefreshRateEnabled()) {
+    return false;
+  }
+
+  ScopedDrmPropertyPtr vrr_capable_property;
+  const int vrr_capable_index = GetDrmProperty(
+      fd, connector, kVrrCapablePropertyName, &vrr_capable_property);
+  return vrr_capable_index >= 0 && connector->prop_values[vrr_capable_index];
+}
+
+bool IsVrrEnabled(int fd, drmModeCrtc* crtc) {
+  if (!features::IsVariableRefreshRateEnabled()) {
+    return false;
+  }
+
+  ScopedDrmObjectPropertyPtr crtc_props(
+      drmModeObjectGetProperties(fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC));
+  ScopedDrmPropertyPtr vrr_enabled_property;
+  const int vrr_enabled_index = GetDrmProperty(
+      fd, crtc_props.get(), kVrrEnabledPropertyName, &vrr_enabled_property);
+  return vrr_enabled_index >= 0 && crtc_props->prop_values[vrr_enabled_index];
+}
+
 HardwareDisplayControllerInfo::HardwareDisplayControllerInfo(
     ScopedDrmConnectorPtr connector,
     ScopedDrmCrtcPtr crtc,
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index f6baf97..90e749c 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -49,6 +49,9 @@
 constexpr char kPrivacyScreenHwStatePropertyName[] = "privacy-screen hw-state";
 constexpr char kPrivacyScreenSwStatePropertyName[] = "privacy-screen sw-state";
 
+constexpr char kVrrCapablePropertyName[] = "vrr_capable";
+constexpr char kVrrEnabledPropertyName[] = "VRR_ENABLED";
+
 // DRM property enum to internal type mappings.
 template <typename InternalType>
 struct DrmPropertyEnumToInternalTypeMapping {
@@ -156,6 +159,10 @@
 
 bool ModeIsInterlaced(const drmModeModeInfo& mode);
 
+bool IsVrrCapable(int fd, drmModeConnector* connector);
+
+bool IsVrrEnabled(int fd, drmModeCrtc* crtc);
+
 uint64_t GetEnumValueForName(int fd, int property_id, const char* str);
 
 std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob);
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index cc88b00..ced5ff3 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -160,6 +160,7 @@
     "controls/prefix_delegate.h",
     "controls/prefix_selector.h",
     "controls/progress_bar.h",
+    "controls/progress_ring_utils.h",
     "controls/resize_area.h",
     "controls/resize_area_delegate.h",
     "controls/scroll_view.h",
@@ -380,6 +381,7 @@
     "controls/native/native_view_host.cc",
     "controls/prefix_selector.cc",
     "controls/progress_bar.cc",
+    "controls/progress_ring_utils.cc",
     "controls/resize_area.cc",
     "controls/scroll_view.cc",
     "controls/scrollbar/base_scroll_bar_thumb.cc",
diff --git a/ui/views/controls/progress_ring_utils.cc b/ui/views/controls/progress_ring_utils.cc
new file mode 100644
index 0000000..0faf83d
--- /dev/null
+++ b/ui/views/controls/progress_ring_utils.cc
@@ -0,0 +1,43 @@
+// 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 "ui/views/controls/progress_ring_utils.h"
+
+#include <utility>
+
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/skia_conversions.h"
+
+namespace views {
+
+void DrawProgressRing(gfx::Canvas* canvas,
+                      const SkRect& bounds,
+                      SkColor background_color,
+                      SkColor progress_color,
+                      float stroke_width,
+                      SkScalar start_angle,
+                      SkScalar sweep_angle) {
+  // Draw the background ring that gets progressively filled.
+  SkPath background_path;
+  background_path.addArc(bounds, /*startAngle=*/-90,
+                         /*SweepAngle=*/360);
+  cc::PaintFlags background_flags;
+  background_flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
+  background_flags.setAntiAlias(true);
+  background_flags.setColor(background_color);
+  background_flags.setStrokeWidth(stroke_width);
+  canvas->DrawPath(std::move(background_path), std::move(background_flags));
+
+  // Draw the filled portion of the ring.
+  SkPath path;
+  path.addArc(bounds, start_angle, sweep_angle);
+  cc::PaintFlags flags;
+  flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
+  flags.setAntiAlias(true);
+  flags.setColor(progress_color);
+  flags.setStrokeWidth(stroke_width);
+  canvas->DrawPath(std::move(path), std::move(flags));
+}
+
+}  // namespace views
diff --git a/ui/views/controls/progress_ring_utils.h b/ui/views/controls/progress_ring_utils.h
new file mode 100644
index 0000000..64336ad
--- /dev/null
+++ b/ui/views/controls/progress_ring_utils.h
@@ -0,0 +1,27 @@
+// 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 UI_VIEWS_CONTROLS_PROGRESS_RING_UTILS_H_
+#define UI_VIEWS_CONTROLS_PROGRESS_RING_UTILS_H_
+
+#include "ui/views/view.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+// Helper function that draws a progress ring on `canvas`. The progress ring
+// consists a background full ring and a partial ring that indicates the
+// progress. `start_angle` and `sweep_angle` are used to indicate the current
+// progress of the ring.
+VIEWS_EXPORT void DrawProgressRing(gfx::Canvas* canvas,
+                                   const SkRect& bounds,
+                                   SkColor background_color,
+                                   SkColor progress_color,
+                                   float stroke_width,
+                                   SkScalar start_angle,
+                                   SkScalar sweep_angle);
+
+}  // namespace views
+
+#endif  // UI_VIEWS_CONTROLS_PROGRESS_RING_UTILS_H_
diff --git a/ui/webui/resources/cr_components/app_management/BUILD.gn b/ui/webui/resources/cr_components/app_management/BUILD.gn
index 47f82302..d80739bd 100644
--- a/ui/webui/resources/cr_components/app_management/BUILD.gn
+++ b/ui/webui/resources/cr_components/app_management/BUILD.gn
@@ -8,6 +8,8 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
+assert(!is_android && !is_ios)
+
 preprocess_folder_tmp = "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/app_management_tmp"
 preprocess_folder =
     "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/app_management"
diff --git a/ui/webui/resources/cr_components/app_management/constants.ts b/ui/webui/resources/cr_components/app_management/constants.ts
index ae16d108..686a784 100644
--- a/ui/webui/resources/cr_components/app_management/constants.ts
+++ b/ui/webui/resources/cr_components/app_management/constants.ts
@@ -76,4 +76,5 @@
   RunOnOsLoginModeTurnedOff = 28,
   FileHandlingTurnedOn = 29,
   FileHandlingTurnedOff = 30,
+  FileHandlingOverflowShown = 31,
 }
diff --git a/ui/webui/resources/cr_components/app_management/file_handling_item.html b/ui/webui/resources/cr_components/app_management/file_handling_item.html
index a4dac01..55446f89 100644
--- a/ui/webui/resources/cr_components/app_management/file_handling_item.html
+++ b/ui/webui/resources/cr_components/app_management/file_handling_item.html
@@ -11,19 +11,40 @@
   #toggle-row:not([disabled_]) {
     cursor: pointer;
   }
+
+  #dialog-body {
+    user-select: text;
+  }
 </style>
 
 <div id="file-handling-item">
   <app-management-toggle-row
      id="toggle-row"
-     label="[[fileHandlingHeader]]"
+     label="[[i18n('appManagementFileHandlingHeader')]]"
      managed="[[isManaged_(app)]]"
      value="[[getValue_(app)]]">
   </app-management-toggle-row>
   <p>
-    <localized-link localized-string="[[userVisibleTypesLabel_(app)]]">
+    <localized-link id="type-list"
+      localized-string="[[userVisibleTypesLabel_(app)]]"
+      on-link-clicked="launchDialog_">
     </localized-link>
   </p>
-  <localized-link localized-string="[[fileHandlingSetDefaults]]" link-url="[[getLearnMoreLinkUrl_(app)]]">
+  <localized-link localized-string="[[i18nAdvanced('fileHandlingSetDefaults')]]"
+    link-url="[[getLearnMoreLinkUrl_(app)]]">
   </localized-link>
 </div>
+<template is="dom-if" if="[[showOverflowDialog]]" restamp>
+  <cr-dialog id="dialog" show-on-attach
+      on-close="onDialogClose_">
+    <div slot="title">[[i18n('fileHandlingOverflowDialogTitle')]]</div>
+    <div id="dialog-body" slot="body">
+      [[userVisibleTypes_(app)]]
+    </div>
+    <div slot="button-container">
+      <cr-button class="action-button" on-click="onCloseButtonClicked_">
+        [[i18n('close')]]
+      </cr-button>
+    </div>
+  </cr-dialog>
+</template>
diff --git a/ui/webui/resources/cr_components/app_management/file_handling_item.ts b/ui/webui/resources/cr_components/app_management/file_handling_item.ts
index 1b004a5..5db5ac7 100644
--- a/ui/webui/resources/cr_components/app_management/file_handling_item.ts
+++ b/ui/webui/resources/cr_components/app_management/file_handling_item.ts
@@ -6,15 +6,22 @@
 import './toggle_row.js';
 
 import {assert} from '//resources/js/assert.m.js';
+import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {CrDialogElement} from '../../cr_elements/cr_dialog/cr_dialog.m.js';
+import {I18nMixin} from '../../js/i18n_mixin.js';
+
 import {App} from './app_management.mojom-webui.js';
 import {BrowserProxy} from './browser_proxy.js';
 import {AppManagementUserAction} from './constants.js';
 import {AppManagementToggleRowElement} from './toggle_row.js';
 import {recordAppManagementUserAction} from './util.js';
 
-export class AppManagementFileHandlingItemElement extends PolymerElement {
+const AppManagementFileHandlingItemBase = I18nMixin(PolymerElement);
+
+export class AppManagementFileHandlingItemElement extends
+    AppManagementFileHandlingItemBase {
   static get is() {
     return 'app-management-file-handling-item';
   }
@@ -25,14 +32,19 @@
 
   static get properties() {
     return {
-      fileHandlingHeader: String,
-      fileHandlingSetDefaults: String,
-
       app: Object,
 
       /**
        * @type {boolean}
        */
+      showOverflowDialog: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * @type {boolean}
+       */
       hidden: {
         type: Boolean,
         computed: 'isHidden_(app)',
@@ -42,8 +54,7 @@
   }
 
   app: App;
-  fileHandlingHeader: String;
-  fileHandlingSetDefaults: String;
+  showOverflowDialog: boolean;
 
   ready() {
     super.ready();
@@ -64,6 +75,13 @@
     return false;
   }
 
+  private userVisibleTypes_(app: App): string {
+    if (app && app.fileHandlingState) {
+      return app.fileHandlingState.userVisibleTypes;
+    }
+    return '';
+  }
+
   private userVisibleTypesLabel_(app: App): string {
     if (app && app.fileHandlingState) {
       return app.fileHandlingState.userVisibleTypesLabel;
@@ -78,6 +96,26 @@
     return '';
   }
 
+  private launchDialog_(e: CustomEvent): void {
+    // A place holder href with the value "#" is used to have a compliant link.
+    // This prevents the browser from navigating the window to "#"
+    e.detail.event.preventDefault();
+    e.stopPropagation();
+    this.showOverflowDialog = true;
+
+    recordAppManagementUserAction(
+        this.app.type, AppManagementUserAction.FileHandlingOverflowShown);
+  }
+
+  private onCloseButtonClicked_() {
+    this.shadowRoot!.querySelector<CrDialogElement>('#dialog')!.close();
+  }
+
+  private onDialogClose_(): void {
+    this.showOverflowDialog = false;
+    focusWithoutInk(assert(this.shadowRoot!.querySelector('#type-list')!));
+  }
+
   private getValue_(app: App): boolean {
     if (app && app.fileHandlingState) {
       return app.fileHandlingState.enabled;
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
index 77f8a9e..b0b87a4 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
@@ -1,6 +1,7 @@
 <link rel="import" href="../../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
@@ -38,7 +39,7 @@
       #esimQrCodeDetection {
         background-color: var(--cros-bg-color-dropped-elevation-1);
         border-radius: 4px;
-        margin: 20px 0 20px 0;
+        margin: 20px 0 15px 0;
         overflow: hidden;
         position: relative;
       }
@@ -171,7 +172,7 @@
         filter: brightness(2.1);
       }
 
-      #loadingMessage {
+      #inputSubtitle {
         bottom: 0;
         color: var(--cros-text-color-secondary);
         font-size: var(--cr-form-field-label-font-size);
@@ -284,8 +285,10 @@
               value="{{activationCode}}"
               disabled="[[showBusy]]"
               on-keydown="onKeyDown_"
-              invalid="[[shouldActivationCodeInputBeInvalid_(state_)]]"
-              error-message="[[i18n('scanQrCodeInvalid')]]">
+              invalid="[[shouldActivationCodeInputBeInvalid_(state_,
+                  isActivationCodeInvalidFormat_)]]"
+              error-message="[[getInputErrorMessage_()]]"
+              aria-description="[[getInputSubtitle_(showBusy)]]">
             <template is="dom-if" if="[[showBusy]]">
               <div slot="suffix">
                 <paper-spinner-lite active>
@@ -293,8 +296,10 @@
               </div>
             </template>
           </cr-input>
-          <div id="loadingMessage" hidden$="[[!showBusy]]">
-              [[i18n('scanQrCodeLoading')]]
+          <div id="inputSubtitle"
+              hidden$="[[shouldActivationCodeInputBeInvalid_(state_,
+                  isActivationCodeInvalidFormat_)]]">
+            [[getInputSubtitle_(showBusy)]]
           </div>
         </div>
       </div>
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
index 22e6c60b3d..b25f32ec 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
@@ -40,6 +40,12 @@
 const QR_CODE_FORMAT = 'qr_code';
 
 /**
+ * The prefix for valid activation codes.
+ * @private {string}
+ */
+const ACTIVATION_CODE_PREFIX = 'LPA:1$';
+
+/**
  * Page in eSIM Setup flow that accepts activation code. User has option for
  * manual entry or scan a QR code.
  */
@@ -126,6 +132,17 @@
     qrCodeCameraA11yString_: {
       type: String,
       value: '',
+    },
+
+    /**
+     * Indicates whether or not |activationCode| matches the correct activation
+     * code format. If there is a partial match (i.e. the code is incomplete but
+     * matches the format so far), this will be false.
+     * @private
+     */
+    isActivationCodeInvalidFormat_: {
+      type: Boolean,
+      value: false,
     }
   },
 
@@ -363,7 +380,14 @@
               this.clearQrCodeDetectorTimer_();
               this.activationCode = activationCode;
               this.stopStream_(this.stream_);
-              this.state_ = PageState.SCANNING_SUCCESS;
+
+              if (this.validateActivationCode_(activationCode)) {
+                this.state_ = PageState.SCANNING_SUCCESS;
+              } else {
+                // If the scanned activation code is invalid or incomplete, show
+                // error.
+                this.state_ = PageState.SCANNING_INSTALL_FAILURE;
+              }
             }
           }).bind(this),
           QR_CODE_DETECTION_INTERVAL_MS);
@@ -394,8 +418,11 @@
 
   /** @private */
   onActivationCodeChanged_() {
-    const activationCode = this.validateActivationCode_(this.activationCode);
-    this.fire('activation-code-updated', {activationCode: activationCode});
+    this.fire('activation-code-updated', {
+      activationCode: this.validateActivationCode_(this.activationCode) ?
+          this.activationCode :
+          null
+    });
   },
 
   /** @private */
@@ -405,19 +432,38 @@
   },
 
   /**
+   * Checks if |activationCode| matches or partially matches the correct format.
+   * Sets |isActivationCodeInvalidFormat_| to true if the format is incorrect.
    * @param {string} activationCode
-   * @return {string|null} The validated activation code or null if it's
-   *     invalid.
+   * @return {boolean} Returns true if |activationCode| is valid and ready to be
+   *     submitted for installation.
    * @private
    */
   validateActivationCode_(activationCode) {
-    // TODO(crbug.com/1093185): Add better validation when we know the
-    // constraints.
-    activationCode = activationCode.trim();
-    if (activationCode.length > 3) {
-      return activationCode;
+    if (activationCode.length <= ACTIVATION_CODE_PREFIX.length) {
+      // If the currently entered activation code is shorter than
+      // |ACTIVATION_CODE_PREFIX|, check if the code matches the format thus
+      // far.
+      this.isActivationCodeInvalidFormat_ = activationCode !==
+          ACTIVATION_CODE_PREFIX.substring(0, activationCode.length);
+
+      // Because the entered activation code is shorter than
+      // |ACTIVATION_CODE_PREFIX| it cannot be submitted yet.
+      return false;
+    } else {
+      // |activationCode| is longer than |ACTIVATION_CODE_PREFIX|. Check if it
+      // begins with the prefix.
+      this.isActivationCodeInvalidFormat_ =
+          activationCode.substring(0, ACTIVATION_CODE_PREFIX.length) !==
+          ACTIVATION_CODE_PREFIX;
     }
-    return null;
+
+    if (this.isActivationCodeInvalidFormat_) {
+      // If the activation code does not match the format, it cannot be
+      // submitted.
+      return false;
+    }
+    return true;
   },
 
   /** @private */
@@ -570,6 +616,34 @@
    * @private
    */
   shouldActivationCodeInputBeInvalid_(state) {
+    if (this.isActivationCodeInvalidFormat_) {
+      return true;
+    }
     return state === PageState.MANUAL_ENTRY_INSTALL_FAILURE;
   },
+
+  /**
+   * @param {boolean} showBusy
+   * @return {string}
+   * @private
+   */
+  getInputSubtitle_(showBusy) {
+    if (showBusy) {
+      return this.i18n('scanQrCodeLoading');
+    }
+
+    // Because this string contains '<' and '>' characters, we cannot use i18n
+    // methods.
+    return loadTimeData.getString('scanQrCodeInputSubtitle');
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getInputErrorMessage_() {
+    // Because this string contains '<' and '>' characters, we cannot use i18n
+    // methods.
+    return loadTimeData.getString('scanQrCodeInputError');
+  }
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.html
index 6bdfa31..06b5a0b 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.html
@@ -3,6 +3,7 @@
 <link rel="import" href="base_page.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="../../../html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_lottie/cr_lottie.html">
 
 <dom-module id="activation-verification-page">
@@ -30,7 +31,11 @@
       <div slot="page-body" id="pageBody" class="layout vertical center-center">
         <span>[[i18n('verifyingActivationCode')]]</span>
         <div id="animationContainer">
-          <cr-lottie id="spinner" animation-url="spinner.json" autoplay>
+          <iron-media-query query="(prefers-color-scheme: dark)"
+              query-matches="{{isDarkModeActive_}}">
+          </iron-media-query>
+          <cr-lottie id="spinner"
+              animation-url="[[getAnimationUrl_(isDarkModeActive_)]]" autoplay>
           </cr-lottie>
         </div>
       </div>
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.js
index 5868da10..d54162fb 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_verification_page.js
@@ -10,4 +10,23 @@
   is: 'activation-verification-page',
 
   behaviors: [I18nBehavior],
+
+  properties: {
+    /**
+     * @type {boolean}
+     * @private
+     */
+    isDarkModeActive_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getAnimationUrl_() {
+    return this.isDarkModeActive_ ? 'spinner_dark.json' : 'spinner.json';
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.html
index bda918b..c2333f3c 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.html
@@ -7,6 +7,7 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_lottie/cr_lottie.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
 
 <dom-module id="setup-loading-page">
   <template>
@@ -48,9 +49,14 @@
     </style>
     <base-page title="[[loadingTitle]]" message="[[loadingMessage]]">
       <div slot="page-body" id="pageBody" class="layout vertical center-center">
+        <iron-media-query query="(prefers-color-scheme: dark)"
+            query-matches="{{isDarkModeActive_}}">
+        </iron-media-query>
         <template is="dom-if" if="[[!isSimDetectError]]" restamp>
           <div id="animationContainer">
-            <cr-lottie id="spinner" animation-url="spinner.json" autoplay>
+            <cr-lottie id="spinner"
+                animation-url="[[getAnimationUrl_(isDarkModeActive_)]]"
+                autoplay>
             </cr-lottie>
           </div>
         </template>
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.js
index c2c7772..9ca44e8 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/setup_loading_page.js
@@ -36,5 +36,22 @@
       type: Boolean,
       value: false,
     },
+
+    /**
+     * @type {boolean}
+     * @private
+     */
+    isDarkModeActive_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getAnimationUrl_() {
+    return this.isDarkModeActive_ ? 'spinner_dark.json' : 'spinner.json';
   },
 });
diff --git a/weblayer/browser/navigation_browsertest.cc b/weblayer/browser/navigation_browsertest.cc
index ee207e6..3f00c67b 100644
--- a/weblayer/browser/navigation_browsertest.cc
+++ b/weblayer/browser/navigation_browsertest.cc
@@ -218,7 +218,14 @@
   EXPECT_EQ(observer.navigation_state(), NavigationState::kFailed);
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, Download) {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+// https://crbug.com/1296643
+#define MAYBE_Download DISABLED_Download
+#else
+#define MAYBE_Download Download
+#endif
+
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, MAYBE_Download) {
   EXPECT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/content-disposition.html"));