diff --git a/AUTHORS b/AUTHORS
index bbfbd04..6f0b74b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -236,6 +236,7 @@
 Clemens Fruhwirth <clemens@endorphin.org>
 Clement Scheelfeldt Skau <clementskau@gmail.com>
 Clinton Staley <clintstaley@gmail.com>
+Cong Zuo <zckevinzc@gmail.com>
 Connor Pearson <cjp822@gmail.com>
 Conrad Irwin <conrad.irwin@gmail.com>
 Craig Schlenter <craig.schlenter@gmail.com>
diff --git a/BUILD.gn b/BUILD.gn
index 95505cf6..e472f3b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -223,7 +223,7 @@
     }
   }
 
-  if (!is_ios && !is_android) {
+  if (!is_ios && !is_android && !is_fuchsia) {
     deps += [ "//components/viz/demo:viz_demo" ]
     if (!use_libfuzzer) {
       # TODO(crbug.com/1241949): libcronet.$version.so fails to link in libfuzzer builds.
diff --git a/DEPS b/DEPS
index 3b96694..a978af6 100644
--- a/DEPS
+++ b/DEPS
@@ -275,11 +275,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '67a98573f48ed6402a58cea94687156fc0d62169',
+  'v8_revision': '9bb30425fe350d96fa3bdeb09c05987b03dd3c0e',
   # 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': 'cd48eedd9d9f689d6f78d59d7e9fee12f75deea7',
+  'angle_revision': '2f0b6429b41c9024f6eb9d7938e7de5e42ad61bb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -298,7 +298,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220523.2.1',
+  'fuchsia_version': 'version:8.20220524.1.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -318,7 +318,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': 'b2e11aa0c1f508e9d8d2b0f634946eb66d3704db',
+  'nacl_revision': '77f46a8ec552f316f64aaa82b7f06da9fceb036b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -342,7 +342,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': '53943c0ef6f4328c70a83a0ec300e1fed77d2e7f',
+  'catapult_revision': 'f2b13af9411351b29ed8317d6b4ad03f590ae6de',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -350,7 +350,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '235544c629c5871cd4aaa37dff1bfa8058500519',
+  'devtools_frontend_revision': '79a7fc9c426881822e8ffc6033059035449c74ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -386,7 +386,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': 'fb890c4122ab4cb9e9f4c5409460926a794d44e6',
+  'dawn_revision': '575b27512e71a13c17d6499da3a15eb8e31ff1a1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -837,7 +837,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': '9gr5_gdvHHkG07PyBCawvcbMaGg5yOWPN_n6wMrpw3IC',
+          'version': '6qRQsw5V-LKn2R7l2DS5syhrf_JK--u052yBbTIxnvkC',
         },
       ],
       'dep_type': 'cipd',
@@ -848,7 +848,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'Bkhj1POAeZHYQ7H9h-7DQELWCveT9675cyXoEqAcNOUC',
+          'version': 'FbzfFtRyn2kYrAc59Fob0mV8f1GIuf0RWy9z06dZBYkC',
         },
       ],
       'dep_type': 'cipd',
@@ -859,7 +859,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': '25rF0DWGyrN4FdgjxSCXO53rwY2EWEaMBYFwPERXDuQC',
+          'version': 'LzmB1RTlJMWpydEINdbLQ593_ZqembwCcisf5nLQEOUC',
         },
       ],
       'dep_type': 'cipd',
@@ -916,7 +916,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'BobEGaocUi64wlQHOALXRWncSh4kcaTyvSebOlg8yysC',
+          'version': 'MUuQOC3fgISvPAo4if3JoGIhAlrLH2U5qx8sUz4EZkkC',
       },
     ],
     'condition': 'checkout_android',
@@ -1130,7 +1130,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'a195de3cf32b5dad755741f3befd798175da62f6',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'a3c1ad164e3eb5ca0330350c35f01f2965c91bbb',
       'condition': 'checkout_linux',
   },
 
@@ -1532,7 +1532,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b146e432cba8ac3c886a671169f1677519389ab2',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd099a600a3410a602cee37a19e875f1a974d493a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1707,7 +1707,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9618103c4aa25ea342ddad65848ff8bb0c1cee9a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '794c54faf04fdc369329664f58460cbe9f1b3748',
+    Var('webrtc_git') + '/src.git' + '@' + '2a2f3ece157ef365ae6a8332efb91d044b98ad87',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1780,7 +1780,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@38f3838299391cccc87ce54c48b30245f694f0dd',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6d498a98bd042af9eee8d7a398c79f0a0f3d417',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index d71a086..85e858d 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -69,15 +69,18 @@
   }
 }
 
-# This version of the WebView APK doesn't include WebLayer java and resources.
+# This version of the WebView APK doesn't include WebLayer.
 # It's used to define the allowlist of resources to be pulled out of language
 # splits. See |shared_resources_allowlist_target|.
 standalone_system_webview_apk_tmpl("system_webview_no_weblayer_apk") {
   exclude_weblayer_java = true
   apk_name = "SystemWebViewNoWebLayer"
 
+  # Don't include any code to speed up compilation. This is used only for the
+  # resources allowlist.
   include_32_bit_webview = false
   include_64_bit_webview = false
+  omit_dex = true
 
   # Adding deps on recycler view in the base WebView APK will end up keeping the
   # Java in the base APK instead of the WebLayer DFM, even though it is not
diff --git a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
index d369f32..9a8a8a6 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
@@ -120,4 +120,11 @@
     public String getFirstPartyVariationsHeadersEnabledPackageId() {
         return "";
     }
+
+    /**
+     * Checks if app recovery mitigations are currently required and initializes SafeMode if needed.
+     * This should only be called from the ":webview_service" process. All other processes should
+     * query SafeModeController to receive mitigation steps.
+     */
+    public void checkForAppRecovery() {}
 }
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 363aa49b..54e7ab1 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -36,6 +36,7 @@
     "//android_webview:common_aidl_java",
     "//android_webview:common_crash_java",
     "//android_webview:common_java",
+    "//android_webview:common_platform_services_java",
     "//android_webview/glue:glue_java",
     "//android_webview/proto:metrics_bridge_records_proto_java",
     "//base:base_java",
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
index 0e7e3f09..ea86a7d 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/WebViewApkApplication.java
@@ -15,6 +15,7 @@
 import org.chromium.android_webview.AwLocaleConfig;
 import org.chromium.android_webview.ProductConfig;
 import org.chromium.android_webview.common.CommandLineUtil;
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.android_webview.common.SafeModeController;
 import org.chromium.android_webview.devui.util.WebViewPackageHelper;
 import org.chromium.base.ContextUtils;
@@ -73,9 +74,16 @@
     @Override
     public void onCreate() {
         super.onCreate();
+        checkForAppRecovery();
         FontPreloadingWorkaround.maybeInstallWorkaround(this);
     }
 
+    public static void checkForAppRecovery() {
+        if (ContextUtils.getProcessName().contains(":webview_service")) {
+            PlatformServiceBridge.getInstance().checkForAppRecovery();
+        }
+    }
+
     /**
      * Initializes globals needed for components that run in the "webview_apk" or "webview_service"
      * process.
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index 86f8504..8b3da5cb 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -45,6 +45,8 @@
                              "include_64_bit_webview",
                            ])
 
+    _omit_dex = defined(omit_dex) && omit_dex
+
     deps += [
       "//android_webview:locale_pak_assets",
       "//android_webview:pak_file_assets",
@@ -80,7 +82,9 @@
       }
     }
 
-    product_config_java_packages = [ webview_product_config_java_package ]
+    if (!_omit_dex) {
+      product_config_java_packages = [ webview_product_config_java_package ]
+    }
 
     if (webview_includes_weblayer) {
       if (_is_bundle_module) {
@@ -88,7 +92,9 @@
       } else {
         deps += [ "//weblayer:locale_pak_assets" ]
       }
-      product_config_java_packages += [ weblayer_product_config_java_package ]
+      if (!_omit_dex) {
+        product_config_java_packages += [ weblayer_product_config_java_package ]
+      }
     }
 
     if (!defined(alternative_android_sdk_dep)) {
@@ -106,14 +112,18 @@
         "If trichrome library is used, static_library_provider must be set " +
             "so that a dep can be added on the library APK.")
 
+    _include_32_bit_webview = !defined(invoker.include_32_bit_webview) ||
+                              invoker.include_32_bit_webview
+    if (android_64bit_target_cpu) {
+      _include_64_bit_webview = !defined(invoker.include_64_bit_webview) ||
+                                invoker.include_64_bit_webview
+    }
+
     # Pure 32-bit implies a 32-bit only Webview built on a 64-bit configuration.
-    _pure_32_bit =
-        android_64bit_target_cpu && defined(invoker.include_64_bit_webview) &&
-        !invoker.include_64_bit_webview
-    _pure_64_bit =
-        android_64bit_target_cpu && defined(invoker.include_32_bit_webview) &&
-        !invoker.include_32_bit_webview
+    _pure_32_bit = android_64bit_target_cpu && !_include_64_bit_webview
+    _pure_64_bit = android_64bit_target_cpu && !_include_32_bit_webview
     not_needed([
+                 "_include_32_bit_webview",
                  "_pure_32_bit",
                  "_pure_64_bit",
                ])
@@ -125,7 +135,8 @@
     if (!_use_trichrome_library) {
       shared_resources = true
 
-      if (!android_64bit_target_cpu || !_pure_32_bit) {
+      if ((!android_64bit_target_cpu && _include_32_bit_webview) ||
+          (android_64bit_target_cpu && !_pure_32_bit)) {
         shared_libraries = [ "//android_webview:libwebviewchromium" ]
         _include_primary_support = true
       }
@@ -143,16 +154,26 @@
       if (android_64bit_target_cpu) {
         if (invoker.is_64_bit_browser) {
           native_lib_placeholders = [ "libdummy.so" ]
-          if (invoker.include_32_bit_webview) {
+          if (_include_32_bit_webview) {
             secondary_abi_shared_libraries = [ "//android_webview:monochrome_64($android_secondary_abi_toolchain)" ]
             _include_secondary_support = true
           }
         } else {
-          if (invoker.include_64_bit_webview) {
+          if (_include_64_bit_webview) {
             shared_libraries = [ "//android_webview:monochrome" ]
             _include_primary_support = true
           }
           secondary_native_lib_placeholders = [ "libdummy.so" ]
+          static_library_provider_use_secondary_abi = true
+        }
+
+        # http://crbug.com/1042107.
+        if (is_component_build) {
+          if (invoker.is_64_bit_browser) {
+            main_component_library = "libmonochrome_64.cr.so"
+          } else {
+            main_component_library = "libmonochrome.cr.so"
+          }
         }
       } else {
         native_lib_placeholders = [ "libdummy.so" ]
@@ -246,7 +267,7 @@
       command_line_flags_file = "webview-command-line"
     }
 
-    if (!is_java_debug) {
+    if (!is_java_debug && !_omit_dex) {
       proguard_enabled = true
       if (!defined(proguard_configs)) {
         proguard_configs = []
@@ -263,13 +284,13 @@
       if (_use_trichrome_library) {
         if (android_64bit_target_cpu) {
           if (invoker.is_64_bit_browser) {
-            if (invoker.include_32_bit_webview) {
+            if (_include_32_bit_webview) {
               version_code = trichrome_64_32_version_code
             } else {
               version_code = trichrome_64_version_code
             }
           } else {
-            if (invoker.include_64_bit_webview) {
+            if (_include_64_bit_webview) {
               version_code = trichrome_32_64_version_code
             } else {
               version_code = trichrome_32_version_code
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index eadec40..7bc74fe 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1762,6 +1762,8 @@
     "wallpaper/wallpaper_constants.h",
     "wallpaper/wallpaper_controller_impl.cc",
     "wallpaper/wallpaper_controller_impl.h",
+    "wallpaper/wallpaper_pref_manager.cc",
+    "wallpaper/wallpaper_pref_manager.h",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator.cc",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator.h",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator_observer.h",
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 54c04a8..a1306d5e 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -44,6 +44,7 @@
 #include "base/guid.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -2501,6 +2502,10 @@
     return false;
   }
 
+  if (*is_new_folder)
+    base::RecordAction(base::UserMetricsAction("AppList_CreateFolder"));
+
+  MaybeRecordFolderDeleteUserAction(source_folder_id);
   RecordAppMovingTypeMetrics(move_type);
   return true;
 }
@@ -2533,6 +2538,19 @@
     if (moving_to_new_page)
       EnsurePageBreakBeforeItem(item_id);
   }
+
+  MaybeRecordFolderDeleteUserAction(source_folder_id);
+}
+
+void AppsGridView::MaybeRecordFolderDeleteUserAction(
+    const std::string& folder_id) {
+  // Ignore the top-level grid (which isn't a folder and can't be deleted).
+  if (folder_id.empty())
+    return;
+
+  // If the folder disappeared from the model, record a user action.
+  if (!model_->FindFolderItem(folder_id))
+    base::RecordAction(base::UserMetricsAction("AppList_DeleteFolder"));
 }
 
 void AppsGridView::CancelContextMenusOnCurrentPage() {
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index 30914fe..3e5c1c4 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -723,6 +723,11 @@
   // model changes.
   void ReparentItemForReorder(AppListItem* item, const GridIndex& target);
 
+  // Records the user metrics action for deleting a folder if `folder_id` has
+  // been removed from the data model. Called after an app item move which might
+  // or might not cause a folder to be deleted.
+  void MaybeRecordFolderDeleteUserAction(const std::string& folder_id);
+
   // Removes the AppListItemView at |index| in |view_model_|, removes it from
   // view structure as well and deletes it.
   void DeleteItemViewAtIndex(int index);
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index a782403..a3efd04 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -62,6 +62,7 @@
 #include "base/test/bind.h"
 #include "base/test/icu_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1832,6 +1833,69 @@
   EXPECT_TRUE(folder_apps_grid_view()->IsInFolder());
 }
 
+TEST_F(AppsGridViewTest, CreatingFolderRecordsUserAction) {
+  base::UserActionTester user_actions;
+
+  // Create two apps, then drag the second on top of the first to create a
+  // folder.
+  model_->PopulateApps(2);
+  UpdateLayout();
+  InitiateDragForItemAtCurrentPageAt(AppsGridView::MOUSE, /*row=*/0,
+                                     /*column=*/1, apps_grid_view_);
+  gfx::Point to = GetItemRectOnCurrentPageAt(0, 0).CenterPoint();
+  UpdateDrag(AppsGridView::MOUSE, to, apps_grid_view_, /*steps=*/10);
+  EndDrag(apps_grid_view_, /*cancel=*/false);
+
+  // Both items are in the folder.
+  AppListItem* item_0 = model_->FindItem("Item 0");
+  AppListItem* item_1 = model_->FindItem("Item 1");
+  EXPECT_TRUE(item_0->IsInFolder());
+  EXPECT_TRUE(item_1->IsInFolder());
+
+  // User action was recorded.
+  EXPECT_EQ(1, user_actions.GetActionCount("AppList_CreateFolder"));
+}
+
+TEST_F(AppsGridViewTest, DeletingFolderRecordsUserAction) {
+  base::UserActionTester user_actions;
+
+  // Create a single-item folder and open it.
+  AppListFolderItem* folder =
+      model_->CreateSingleItemFolder("folder_id", "Item 0");
+  std::string folder_id = folder->id();
+  test_api_->Update();
+  test_api_->PressItemAt(0);
+
+  // Drag the app out of the folder.
+  AppsGridViewTestApi folder_grid_test_api(folder_apps_grid_view());
+  AppListItemView* drag_view = InitiateDragForItemAtCurrentPageAt(
+      AppsGridView::MOUSE, 0, 0, folder_apps_grid_view());
+  gfx::Point empty_space =
+      app_list_folder_view()->GetLocalBounds().bottom_center() +
+      gfx::Vector2d(0, drag_view->height());
+  UpdateDrag(AppsGridView::MOUSE, empty_space, folder_apps_grid_view(),
+             /*steps=*/10);
+  // Fire the reparent timer that should be started when an item is dragged out
+  // of folder bounds.
+  ASSERT_TRUE(folder_apps_grid_view()->FireFolderItemReparentTimerForTest());
+
+  // Calculate the coordinates for the drop point. Note that we we are dropping
+  // into the app list view not the folder view. The (0,1) spot is empty.
+  gfx::Point drop_point = GetItemRectOnCurrentPageAt(0, 1).CenterPoint();
+  views::View::ConvertPointToTarget(apps_grid_view_, folder_apps_grid_view(),
+                                    &drop_point);
+  UpdateDrag(AppsGridView::MOUSE, drop_point, folder_apps_grid_view(),
+             /*steps=*/5);
+  EndDrag(folder_apps_grid_view(), /*cancel=*/false);
+
+  // Item is in top-level grid and folder is deleted.
+  EXPECT_EQ("Item 0", model_->GetModelContent());
+  EXPECT_FALSE(model_->FindFolderItem(folder_id));
+
+  // User action was recorded.
+  EXPECT_EQ(1, user_actions.GetActionCount("AppList_DeleteFolder"));
+}
+
 TEST_P(AppsGridViewDragTest, MouseDragItemOutOfFolder) {
   // Creates a folder item.
   const size_t kTotalItems = kMaxItemsPerFolderPage;
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index 7a9ed08..d66e1f82 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -50,7 +50,7 @@
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
 #include "ash/touch/touch_devices_controller.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_pref_manager.h"
 #include "ash/wm/desks/desks_restore_util.h"
 #include "ash/wm/desks/persistent_desks_bar_controller.h"
 #include "ash/wm/desks/templates/saved_desk_util.h"
@@ -112,7 +112,7 @@
   MediaTray::RegisterProfilePrefs(registry);
   UsbPeripheralNotificationController::RegisterProfilePrefs(registry);
   VPNListView::RegisterProfilePrefs(registry);
-  WallpaperControllerImpl::RegisterProfilePrefs(registry);
+  WallpaperPrefManager::RegisterProfilePrefs(registry);
   WindowCycleController::RegisterProfilePrefs(registry);
 
   // Provide prefs registered in the browser for ash_unittests.
@@ -137,7 +137,7 @@
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry, bool for_test) {
   PaletteTray::RegisterLocalStatePrefs(registry);
-  WallpaperControllerImpl::RegisterLocalStatePrefs(registry);
+  WallpaperPrefManager::RegisterLocalStatePrefs(registry);
   if (!ash::features::IsBluetoothRevampEnabled())
     BluetoothPowerController::RegisterLocalStatePrefs(registry);
   DetachableBaseHandler::RegisterPrefs(registry);
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 7636e29..8a5bc17 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2362,7 +2362,7 @@
       </message>
 
       <message name="IDS_AURA_OPEN_PERSONALIZATION_HUB" desc="The label used to open personalization hub in context menu">
-        Personalize
+        Set wallpaper &amp; style
       </message>
       <message name="IDS_AURA_SET_DESKTOP_WALLPAPER" desc="The label used for change wallpaper in context menu">
         Set wallpaper
diff --git a/ash/ash_strings_grd/IDS_AURA_OPEN_PERSONALIZATION_HUB.png.sha1 b/ash/ash_strings_grd/IDS_AURA_OPEN_PERSONALIZATION_HUB.png.sha1
index d7308fb..52ea1a93 100644
--- a/ash/ash_strings_grd/IDS_AURA_OPEN_PERSONALIZATION_HUB.png.sha1
+++ b/ash/ash_strings_grd/IDS_AURA_OPEN_PERSONALIZATION_HUB.png.sha1
@@ -1 +1 @@
-2c3124cc72901646664f8e7ce89c01924b4e4532
\ No newline at end of file
+cafe09cbae077570e284ca7dab8e6ab858000bc8
\ No newline at end of file
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS
index eb193752..0af496a 100644
--- a/ash/assistant/ui/DEPS
+++ b/ash/assistant/ui/DEPS
@@ -20,6 +20,7 @@
   "+chromeos/services/libassistant/public/cpp",
   "+mojo/public/cpp",
   "+net/base",
+  "+third_party/abseil-cpp/absl",
   "+third_party/skia/include/core",
   "+ui",
 ]
diff --git a/ash/capture_mode/capture_mode_bar_view.cc b/ash/capture_mode/capture_mode_bar_view.cc
index 35a8cf7..24cec02 100644
--- a/ash/capture_mode/capture_mode_bar_view.cc
+++ b/ash/capture_mode/capture_mode_bar_view.cc
@@ -108,9 +108,9 @@
   const SkColor separator_color = color_provider->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kSeparatorColor);
   separator_1_->SetColor(separator_color);
-  separator_1_->SetPreferredHeight(kSeparatorHeight);
+  separator_1_->SetPreferredLength(kSeparatorHeight);
   separator_2_->SetColor(separator_color);
-  separator_2_->SetPreferredHeight(kSeparatorHeight);
+  separator_2_->SetPreferredLength(kSeparatorHeight);
 
   close_button_->SetTooltipText(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 34d3eb3..7f1a715 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -68,6 +68,7 @@
 #include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
@@ -114,7 +115,6 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/window_modality_controller.h"
 #include "ui/wm/core/window_util.h"
@@ -414,27 +414,21 @@
   }
 };
 
-class CaptureSessionWidgetObserver : public views::WidgetObserver {
+class CaptureSessionWidgetClosed {
  public:
-  explicit CaptureSessionWidgetObserver(views::Widget* widget) {
+  explicit CaptureSessionWidgetClosed(views::Widget* widget) {
     DCHECK(widget);
-    observer_.Observe(widget);
+    widget_ = widget->GetWeakPtr();
   }
-  CaptureSessionWidgetObserver(const CaptureSessionWidgetObserver&) = delete;
-  CaptureSessionWidgetObserver& operator=(const CaptureSessionWidgetObserver&) =
+  CaptureSessionWidgetClosed(const CaptureSessionWidgetClosed&) = delete;
+  CaptureSessionWidgetClosed& operator=(const CaptureSessionWidgetClosed&) =
       delete;
-  ~CaptureSessionWidgetObserver() override = default;
+  ~CaptureSessionWidgetClosed() = default;
 
-  bool GetWidgetDestroyed() const { return !observer_.IsObserving(); }
-
-  // views::WidgetObserver
-  void OnWidgetClosing(views::Widget* widget) override {
-    DCHECK(observer_.IsObservingSource(widget));
-    observer_.Reset();
-  }
+  bool GetWidgetClosed() const { return !widget_ || widget_->IsClosed(); }
 
  private:
-  base::ScopedObservation<views::Widget, views::WidgetObserver> observer_{this};
+  base::WeakPtr<views::Widget> widget_;
 };
 
 class CaptureNotificationWaiter : public message_center::MessageCenterObserver {
@@ -531,13 +525,13 @@
   controller->Start(CaptureModeEntryType::kQuickSettings);
   EXPECT_TRUE(controller->IsActive());
   EXPECT_TRUE(GetCaptureModeBarWidget());
-  CaptureSessionWidgetObserver observer(GetCaptureModeBarWidget());
-  EXPECT_FALSE(observer.GetWidgetDestroyed());
+  CaptureSessionWidgetClosed observer(GetCaptureModeBarWidget());
+  EXPECT_FALSE(observer.GetWidgetClosed());
   controller->Stop();
   EXPECT_FALSE(controller->IsActive());
   EXPECT_FALSE(controller->capture_mode_session());
   // The Widget should have been destroyed by now.
-  EXPECT_TRUE(observer.GetWidgetDestroyed());
+  EXPECT_TRUE(observer.GetWidgetClosed());
 }
 
 TEST_F(CaptureModeTest, StartWithMostRecentTypeAndSource) {
diff --git a/ash/components/arc/test/fake_file_system_instance.cc b/ash/components/arc/test/fake_file_system_instance.cc
index 3fefc58..f7e7340 100644
--- a/ash/components/arc/test/fake_file_system_instance.cc
+++ b/ash/components/arc/test/fake_file_system_instance.cc
@@ -200,24 +200,6 @@
   recent_documents_[key].push_back(document);
 }
 
-void FakeFileSystemInstance::RemoveRecentDocument(const Document& document) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // Unfortunately we don't know the root_id when deleting a document, so
-  // here we need to loop through all available roots to find the document.
-  for (auto const& doc : recent_documents_) {
-    const auto iter = std::find_if(
-        doc.second.begin(), doc.second.end(),
-        [&document](const Document& recent_document) {
-          return document.authority == recent_document.authority &&
-                 document.document_id == recent_document.document_id;
-        });
-    if (iter != doc.second.end()) {
-      recent_documents_[doc.first].erase(iter);
-      return;
-    }
-  }
-}
-
 void FakeFileSystemInstance::AddRoot(const Root& root) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   roots_list_.push_back(root);
@@ -593,9 +575,6 @@
         FROM_HERE, base::BindOnce(std::move(callback), false));
     return;
   }
-  // We also need to remove the document from the recent_documents_ if it was
-  // being added there.
-  RemoveRecentDocument(iter->second);
   documents_.erase(iter);
   size_t erased = child_documents_.erase(key);
   DCHECK_NE(0u, erased);
diff --git a/ash/components/arc/test/fake_file_system_instance.h b/ash/components/arc/test/fake_file_system_instance.h
index 4fcb0a6..4b34a60f 100644
--- a/ash/components/arc/test/fake_file_system_instance.h
+++ b/ash/components/arc/test/fake_file_system_instance.h
@@ -231,9 +231,6 @@
   // Adds a recent document accessible by document provider based methods.
   void AddRecentDocument(const std::string& root_id, const Document& document);
 
-  // Removes a recent document accessible by document provider based methods.
-  void RemoveRecentDocument(const Document& document);
-
   // Adds a root accessible by document provider based methods.
   void AddRoot(const Root& root);
 
diff --git a/ash/components/attestation/DEPS b/ash/components/attestation/DEPS
index 516fd3af..cd9bcfe 100644
--- a/ash/components/attestation/DEPS
+++ b/ash/components/attestation/DEPS
@@ -7,6 +7,7 @@
   "+components/account_id",
   "+crypto",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
 ]
 
diff --git a/ash/components/audio/DEPS b/ash/components/audio/DEPS
index bed6c70..8636921 100644
--- a/ash/components/audio/DEPS
+++ b/ash/components/audio/DEPS
@@ -11,5 +11,6 @@
   "+services/media_session/public",
   "+ui/events/devices",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api/dbus",
 ]
diff --git a/ash/components/cryptohome/DEPS b/ash/components/cryptohome/DEPS
index ab959df6..410db217 100644
--- a/ash/components/cryptohome/DEPS
+++ b/ash/components/cryptohome/DEPS
@@ -7,6 +7,7 @@
   "+components/account_id",
   "+components/device_event_log",
   "+components/user_manager",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
 ]
 
diff --git a/ash/components/drivefs/drivefs_host.cc b/ash/components/drivefs/drivefs_host.cc
index dc89e90..b2719f4 100644
--- a/ash/components/drivefs/drivefs_host.cc
+++ b/ash/components/drivefs/drivefs_host.cc
@@ -56,8 +56,11 @@
         bool{host->account_token_delegate_->GetCachedAccessToken()};
     search_ = std::make_unique<DriveFsSearch>(
         drivefs_interface(), host_->network_connection_tracker_, host_->clock_);
-    http_client_ = std::make_unique<DriveFsHttpClient>(
-        host_->delegate_->GetURLLoaderFactory());
+    if (base::FeatureList::IsEnabled(
+            chromeos::features::kDriveFsChromeNetworking)) {
+      http_client_ = std::make_unique<DriveFsHttpClient>(
+          host_->delegate_->GetURLLoaderFactory());
+    }
   }
 
   MountState(const MountState&) = delete;
@@ -184,6 +187,12 @@
   void ExecuteHttpRequest(
       mojom::HttpRequestPtr request,
       mojo::PendingRemote<mojom::HttpDelegate> delegate) override {
+    if (!http_client_) {
+      // The Chrome Network Service <-> DriveFS bridge is not enabled. Ignore
+      // the request and allow the |delegate| to close itself. DriveFS will
+      // pick up on the |delegate| closure and fallback to cURL.
+      return;
+    }
     http_client_->ExecuteHttpRequest(std::move(request), std::move(delegate));
   }
 
diff --git a/ash/components/hid_detection/BUILD.gn b/ash/components/hid_detection/BUILD.gn
index 8092a1c..53425cf 100644
--- a/ash/components/hid_detection/BUILD.gn
+++ b/ash/components/hid_detection/BUILD.gn
@@ -59,6 +59,7 @@
 
   deps = [
     ":hid_detection",
+    ":test_support",
     "//ash/constants",
     "//base",
     "//base/test:test_support",
diff --git a/ash/components/hid_detection/bluetooth_hid_detector.cc b/ash/components/hid_detection/bluetooth_hid_detector.cc
index 6cb9fb9..a8531f5 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector.cc
+++ b/ash/components/hid_detection/bluetooth_hid_detector.cc
@@ -75,7 +75,10 @@
   DCHECK(ash::features::IsOobeHidDetectionRevampEnabled());
 }
 
-BluetoothHidDetector::~BluetoothHidDetector() = default;
+BluetoothHidDetector::~BluetoothHidDetector() {
+  DCHECK(!delegate_) << " Bluetooth HID detection must be stopped before "
+                     << "BluetoothHidDetector is destroyed.";
+}
 
 void BluetoothHidDetector::StartBluetoothHidDetection(
     Delegate* delegate,
diff --git a/ash/components/hid_detection/bluetooth_hid_detector_impl.cc b/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
index a625b3f..5317cfe0 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
+++ b/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
@@ -109,10 +109,6 @@
 
 void BluetoothHidDetectorImpl::PerformStartBluetoothHidDetection(
     InputDevicesStatus input_devices_status) {
-  DCHECK(input_devices_status.pointer_is_missing ||
-         input_devices_status.keyboard_is_missing)
-      << " StartBluetoothHidDetection() called when neither pointer or "
-      << "keyboard is missing";
   DCHECK_EQ(kNotStarted, state_);
   HID_LOG(EVENT) << "Starting Bluetooth HID detection, pointer missing: "
                  << input_devices_status.pointer_is_missing
diff --git a/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc b/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
index 6115700..6e58482 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
+++ b/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
@@ -31,7 +31,6 @@
 using chromeos::bluetooth_config::mojom::PairedBluetoothDevicePropertiesPtr;
 using BluetoothHidMetadata = BluetoothHidDetector::BluetoothHidMetadata;
 using BluetoothHidType = BluetoothHidDetector::BluetoothHidType;
-using InputDevicesStatus = BluetoothHidDetector::InputDevicesStatus;
 
 const char kTestPinCode[] = "123456";
 const uint32_t kTestPasskey = 123456;
@@ -94,7 +93,8 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void SetInputDevicesStatus(InputDevicesStatus input_devices_status) {
+  void SetInputDevicesStatus(
+      BluetoothHidDetector::InputDevicesStatus input_devices_status) {
     bluetooth_hid_detector()->SetInputDevicesStatus(input_devices_status);
     base::RunLoop().RunUntilIdle();
   }
@@ -402,6 +402,41 @@
       /*pairing_state=*/absl::nullopt);
 }
 
+TEST_F(BluetoothHidDetectorImplTest, AddDevices_NoTypeMissing) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  std::string device_id2;
+  AddUnpairedDevice(&device_id2, DeviceType::kKeyboard);
+
+  // Begin HID detection with no types missing. No device should be attempted to
+  // be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection(
+      /*is_pointer_missing=*/false, /*is_keyboard_missing=*/false);
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_TRUE(
+      GetDevicePairingHandlers()[0]->current_pairing_device_id().empty());
+  EXPECT_EQ(0u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+
+  // Mock the pointer disconnecting and add another device to trigger
+  // OnDiscoveredDevicesListChanged(). |device_id1| should be attempted to be
+  // paired with.
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
+  std::string device_id3;
+  AddUnpairedDevice(&device_id3, DeviceType::kMouse);
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+  EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      /*pairing_state=*/absl::nullopt);
+}
+
 TEST_F(BluetoothHidDetectorImplTest,
        AddDevices_SeriallyAfterStartingDetection) {
   FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection();
@@ -435,8 +470,8 @@
 
   // Mock |device_id1| being registered as connected. The next device in the
   // queue should now be processed.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = true});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = true});
   EXPECT_TRUE(
       GetDevicePairingHandlers()[0]->current_pairing_device_id().empty());
   EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -490,8 +525,8 @@
 
   // Mock |device_id1| being registered as connected. |device_id2| should be
   // attempted to be paired with.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = true});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = true});
   EXPECT_EQ(device_id2,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -534,8 +569,8 @@
 
   // Mock |device_id1| being registered as connected. |device_id3| should be
   // attempted to be paired with.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = true});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = true});
   EXPECT_EQ(device_id3,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -565,8 +600,8 @@
       /*pairing_state=*/absl::nullopt);
 
   // Set both devices to connected.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = false});
   EXPECT_EQ(0u, delegate->num_bluetooth_hid_status_changed_calls());
   AssertBluetoothHidDetectionStatus(
       /*current_pairing_device=*/absl::nullopt,
@@ -583,8 +618,8 @@
       /*pairing_state=*/absl::nullopt);
 
   // Mock the pointer no longer being connected.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
 
   // Add another device to trigger OnDiscoveredDevicesListChanged().
   std::string device_id2;
@@ -616,8 +651,8 @@
       /*pairing_state=*/absl::nullopt);
 
   // Mock a keyboard being connected. Nothing should happen.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
   EXPECT_EQ(device_id1,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -626,8 +661,8 @@
       /*pairing_state=*/absl::nullopt);
 
   // Mock keyboard being disconnected. Nothing should happen.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = true});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = true});
   EXPECT_EQ(device_id1,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -637,8 +672,8 @@
 
   // Mock a pointer being connected. This should cancel pairing with
   // |device_id1|.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = true});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = true});
   EXPECT_EQ(device_id2,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -668,8 +703,8 @@
 
   // Mock a keyboard being connected. This should not cancel pairing with
   // |device_id1|.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
   EXPECT_EQ(device_id1,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -679,8 +714,8 @@
 
   // Mock a pointer also being connected. This should cancel pairing with
   // |device_id1|.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = false,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = false});
   EXPECT_TRUE(
       GetDevicePairingHandlers()[0]->current_pairing_device_id().empty());
   EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -887,8 +922,8 @@
       /*pairing_state=*/absl::nullopt);
 
   // Mock the device being registered as connected.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
   EXPECT_TRUE(
       GetDevicePairingHandlers()[0]->current_pairing_device_id().empty());
   EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
@@ -951,8 +986,8 @@
 
   // Mock |device_id1| being registered as connected. |device_id2| should be
   // attempted to be paired with.
-  SetInputDevicesStatus(InputDevicesStatus{.pointer_is_missing = true,
-                                           .keyboard_is_missing = false});
+  SetInputDevicesStatus(
+      {.pointer_is_missing = true, .keyboard_is_missing = false});
   EXPECT_EQ(device_id2,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(10u, delegate->num_bluetooth_hid_status_changed_calls());
diff --git a/ash/components/hid_detection/hid_detection_manager.cc b/ash/components/hid_detection/hid_detection_manager.cc
index cbf8c820..30fc106 100644
--- a/ash/components/hid_detection/hid_detection_manager.cc
+++ b/ash/components/hid_detection/hid_detection_manager.cc
@@ -12,7 +12,10 @@
   DCHECK(ash::features::IsOobeHidDetectionRevampEnabled());
 }
 
-HidDetectionManager::~HidDetectionManager() = default;
+HidDetectionManager::~HidDetectionManager() {
+  DCHECK(!delegate_) << " HID detection must be stopped before "
+                     << "HidDetectionManager is destroyed";
+}
 
 void HidDetectionManager::StartHidDetection(Delegate* delegate) {
   DCHECK(!delegate_);
diff --git a/ash/components/hid_detection/hid_detection_manager_impl.cc b/ash/components/hid_detection/hid_detection_manager_impl.cc
index 6eb30ef..8106214 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl.cc
+++ b/ash/components/hid_detection/hid_detection_manager_impl.cc
@@ -24,8 +24,10 @@
 }
 
 HidDetectionManagerImpl::HidDetectionManagerImpl(
-    device::mojom::DeviceService* device_service)
-    : device_service_{device_service} {}
+    device::mojom::DeviceService* device_service,
+    BluetoothHidDetector* bluetooth_hid_detector)
+    : device_service_{device_service},
+      bluetooth_hid_detector_{bluetooth_hid_detector} {}
 
 HidDetectionManagerImpl::~HidDetectionManagerImpl() = default;
 
@@ -52,6 +54,7 @@
 void HidDetectionManagerImpl::PerformStopHidDetection() {
   HID_LOG(EVENT) << "Stopping HID detection.";
   input_device_manager_receiver_.reset();
+  bluetooth_hid_detector_->StopBluetoothHidDetection();
 }
 
 HidDetectionManager::HidDetectionStatus
@@ -69,8 +72,10 @@
   const std::string& device_id = info->id;
   device_id_to_device_map_[device_id] = std::move(info);
 
-  if (AttemptSetDeviceAsConnectedHid(*device_id_to_device_map_[device_id]))
+  if (AttemptSetDeviceAsConnectedHid(*device_id_to_device_map_[device_id])) {
     NotifyHidDetectionStatusChanged();
+    SetInputDevicesStatus();
+  }
 }
 
 void HidDetectionManagerImpl::InputDeviceRemoved(const std::string& id) {
@@ -101,9 +106,14 @@
   if (was_connected_hid_disconnected_) {
     SetConnectedHids();
     NotifyHidDetectionStatusChanged();
+    SetInputDevicesStatus();
   }
 }
 
+void HidDetectionManagerImpl::OnBluetoothHidStatusChanged() {
+  // TODO(gordonseto): Implement this.
+}
+
 void HidDetectionManagerImpl::BindToInputDeviceManagerIfNeeded() {
   if (input_device_manager_.is_bound())
     return;
@@ -153,6 +163,10 @@
   }
   SetConnectedHids();
   NotifyHidDetectionStatusChanged();
+
+  bluetooth_hid_detector_->StartBluetoothHidDetection(
+      this, {.pointer_is_missing = !connected_pointer_id_.has_value(),
+             .keyboard_is_missing = !connected_keyboard_id_.has_value()});
 }
 
 bool HidDetectionManagerImpl::SetConnectedHids() {
@@ -216,4 +230,10 @@
   return InputMetadata{state, device->name};
 }
 
+void HidDetectionManagerImpl::SetInputDevicesStatus() {
+  bluetooth_hid_detector_->SetInputDevicesStatus(
+      {.pointer_is_missing = !connected_pointer_id_.has_value(),
+       .keyboard_is_missing = !connected_keyboard_id_.has_value()});
+}
+
 }  // namespace ash::hid_detection
diff --git a/ash/components/hid_detection/hid_detection_manager_impl.h b/ash/components/hid_detection/hid_detection_manager_impl.h
index d0a136e3..d97bf7b 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl.h
+++ b/ash/components/hid_detection/hid_detection_manager_impl.h
@@ -7,6 +7,7 @@
 
 #include "ash/components/hid_detection/hid_detection_manager.h"
 
+#include "ash/components/hid_detection/bluetooth_hid_detector.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
@@ -20,7 +21,8 @@
 // Concrete HidDetectionManager implementation that uses InputDeviceManager and
 // BluetoothHidDetectorImpl to detect and connect with devices.
 class HidDetectionManagerImpl : public HidDetectionManager,
-                                public device::mojom::InputDeviceManagerClient {
+                                public device::mojom::InputDeviceManagerClient,
+                                public BluetoothHidDetector::Delegate {
  public:
   using InputDeviceManagerBinder = base::RepeatingCallback<void(
       mojo::PendingReceiver<device::mojom::InputDeviceManager>)>;
@@ -29,8 +31,8 @@
   static void SetInputDeviceManagerBinderForTest(
       InputDeviceManagerBinder binder);
 
-  explicit HidDetectionManagerImpl(
-      device::mojom::DeviceService* device_service);
+  HidDetectionManagerImpl(device::mojom::DeviceService* device_service,
+                          BluetoothHidDetector* bluetooth_hid_detector);
   ~HidDetectionManagerImpl() override;
 
  private:
@@ -46,6 +48,9 @@
   void InputDeviceAdded(device::mojom::InputDeviceInfoPtr info) override;
   void InputDeviceRemoved(const std::string& id) override;
 
+  // BluetoothHidDetector::Delegate:
+  void OnBluetoothHidStatusChanged() override;
+
   void BindToInputDeviceManagerIfNeeded();
 
   // Processes the list of input devices fetched by GetIsHidDetectionRequired().
@@ -77,6 +82,9 @@
   InputMetadata GetInputMetadata(
       const absl::optional<std::string>& device_id) const;
 
+  // Informs |bluetooth_hid_detector_| what devices are missing.
+  void SetInputDevicesStatus();
+
   std::map<std::string, device::mojom::InputDeviceInfoPtr>
       device_id_to_device_map_;
   absl::optional<std::string> connected_touchscreen_id_;
@@ -84,6 +92,7 @@
   absl::optional<std::string> connected_keyboard_id_;
 
   device::mojom::DeviceService* device_service_ = nullptr;
+  BluetoothHidDetector* bluetooth_hid_detector_ = nullptr;
   mojo::Remote<device::mojom::InputDeviceManager> input_device_manager_;
   mojo::AssociatedReceiver<device::mojom::InputDeviceManagerClient>
       input_device_manager_receiver_{this};
diff --git a/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc b/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
index 600c596..51816f1 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
+++ b/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/components/hid_detection/hid_detection_manager_impl.h"
 
+#include "ash/components/hid_detection/bluetooth_hid_detector.h"
+#include "ash/components/hid_detection/fake_bluetooth_hid_detector.h"
 #include "ash/constants/ash_features.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
@@ -17,6 +19,7 @@
 using InputMetadata = HidDetectionManager::InputMetadata;
 using InputState = HidDetectionManager::InputState;
 using InputDeviceType = device::mojom::InputDeviceType;
+using InputDevicesStatus = BluetoothHidDetector::InputDevicesStatus;
 
 enum HidType {
   kMouse,
@@ -66,8 +69,9 @@
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(
         ash::features::kOobeHidDetectionRevamp);
-    hid_detection_manager_ =
-        std::make_unique<HidDetectionManagerImpl>(/*device_service=*/nullptr);
+    fake_bluetooth_hid_detector_ = std::make_unique<FakeBluetoothHidDetector>();
+    hid_detection_manager_ = std::make_unique<HidDetectionManagerImpl>(
+        /*device_service=*/nullptr, fake_bluetooth_hid_detector_.get());
 
     HidDetectionManagerImpl::SetInputDeviceManagerBinderForTest(
         base::BindRepeating(&device::FakeInputServiceLinux::Bind,
@@ -75,6 +79,9 @@
   }
 
   void TearDown() override {
+    if (fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active())
+      StopHidDetection();
+
     HidDetectionManagerImpl::SetInputDeviceManagerBinderForTest(
         base::NullCallback());
   }
@@ -89,13 +96,21 @@
   }
 
   void StartHidDetection() {
+    EXPECT_FALSE(
+        fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
     hid_detection_manager_->StartHidDetection(&delegate_);
     base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(
+        fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
   }
 
   void StopHidDetection() {
+    EXPECT_TRUE(
+        fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
     hid_detection_manager_->StopHidDetection();
     base::RunLoop().RunUntilIdle();
+    EXPECT_FALSE(
+        fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
   }
 
   size_t GetNumHidDetectionStatusChangedCalls() {
@@ -166,6 +181,12 @@
               GetLastHidDetectionStatus()->keyboard_metadata.detected_hid_name);
     EXPECT_EQ(touchscreen_detected,
               GetLastHidDetectionStatus()->touchscreen_detected);
+    EXPECT_EQ(pointer_metadata.state == InputState::kSearching,
+              fake_bluetooth_hid_detector_->input_devices_status()
+                  .pointer_is_missing);
+    EXPECT_EQ(keyboard_metadata.state == InputState::kSearching,
+              fake_bluetooth_hid_detector_->input_devices_status()
+                  .keyboard_is_missing);
   }
 
  private:
@@ -177,6 +198,7 @@
   size_t num_devices_created_ = 0;
 
   FakeHidDetectionManagerDelegate delegate_;
+  std::unique_ptr<FakeBluetoothHidDetector> fake_bluetooth_hid_detector_;
 
   std::unique_ptr<hid_detection::HidDetectionManager> hid_detection_manager_;
 };
diff --git a/ash/components/login/auth/DEPS b/ash/components/login/auth/DEPS
index b732d30..a14e5f7 100644
--- a/ash/components/login/auth/DEPS
+++ b/ash/components/login/auth/DEPS
@@ -21,6 +21,7 @@
   "+google_apis",
   "+net",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/boringssl/src/include",
   "+third_party/cros_system_api",
   "+url",
diff --git a/ash/components/tpm/DEPS b/ash/components/tpm/DEPS
index 73676ef..ee09834 100644
--- a/ash/components/tpm/DEPS
+++ b/ash/components/tpm/DEPS
@@ -11,5 +11,6 @@
   "+components/policy/proto",
   "+google_apis/gaia",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
 ]
diff --git a/ash/constants/ash_constants.h b/ash/constants/ash_constants.h
index f432785..81d1516 100644
--- a/ash/constants/ash_constants.h
+++ b/ash/constants/ash_constants.h
@@ -70,9 +70,6 @@
 // Whether dark mode is enabled by default.
 constexpr bool kDefaultDarkModeEnabled = false;
 
-// Whether color mode is themed by default.
-constexpr bool kDefaultColorModeThemed = true;
-
 // Maximum number of times that dark/light mode educational nudge can be shown.
 constexpr int kDarkLightModeNudgeMaxShownCount = 3;
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 39ab5ff..3cd08f1 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -499,6 +499,10 @@
 const base::Feature kDriveFsMirroring{"DriveFsMirroring",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables access to Chrome's Network Service for DriveFS.
+const base::Feature kDriveFsChromeNetworking{"DriveFsChromeNetworking",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables authenticating to Wi-Fi networks using EAP-GTC.
 const base::Feature kEapGtcWifiAuthentication{
     "EapGtcWifiAuthentication", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -1132,7 +1136,7 @@
 // Provides a UI for users to customize their wallpapers, screensaver and
 // avatars.
 const base::Feature kPersonalizationHub{"PersonalizationHub",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Provides a UI for users to view information about their Android phone
 // and perform phone-side actions within ChromeOS.
@@ -1448,7 +1452,7 @@
 // Uses new  AuthSession-based API in cryptohome to authenticate users during
 // sign-in.
 const base::Feature kUseAuthsessionAuthentication{
-    "UseAuthsessionAuthentication", base::FEATURE_DISABLED_BY_DEFAULT};
+    "UseAuthsessionAuthentication", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables using the BluetoothSystem Mojo interface for Bluetooth operations.
 const base::Feature kUseBluetoothSystemInAsh{"UseBluetoothSystemInAsh",
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 3be47e9..af3d5b1 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -204,6 +204,8 @@
 extern const base::Feature kDriveFsBidirectionalNativeMessaging;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDriveFsMirroring;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kDriveFsChromeNetworking;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEapGtcWifiAuthentication;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWADebugMode;
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index 6194b12..dfd1226 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -481,11 +481,6 @@
 // A boolean pref storing the enabled status of the ambient color feature.
 const char kAmbientColorEnabled[] = "ash.ambient_color.enabled";
 
-// A boolean pref used when dark light mode feature is enabled to indicate
-// whether the color mode is themed. If true, the background color will be
-// calculated based on extracted wallpaper color.
-const char kColorModeThemed[] = "ash.dark_mode.color_mode_themed";
-
 // A boolean pref that indicates whether dark mode is enabled.
 const char kDarkModeEnabled[] = "ash.dark_mode.enabled";
 
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 1b79262..29c888c2 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -239,7 +239,6 @@
 extern const char kMessageCenterLockScreenModeHideSensitive[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kAmbientColorEnabled[];
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kColorModeThemed[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kDarkModeEnabled[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kDarkLightModeNudge[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kDarkModeScheduleType[];
diff --git a/ash/projector/projector_annotation_tray.cc b/ash/projector/projector_annotation_tray.cc
index d66eb35..0ef9a0c 100644
--- a/ash/projector/projector_annotation_tray.cc
+++ b/ash/projector/projector_annotation_tray.cc
@@ -261,8 +261,12 @@
   ResetTray();
 }
 
-void ProjectorAnnotationTray::OnCanvasInitializationFailed() {
-  // Set icon color to kIconColorPrimary with 30% opacity.
+void ProjectorAnnotationTray::SetTrayEnabled(bool enabled) {
+  SetEnabled(enabled);
+  if (enabled)
+    return;
+
+  // For disabled state, set icon color to kIconColorPrimary with 30% opacity.
   SkColor disabled_icon_color =
       SkColorSetA(AshColorProvider::Get()->GetContentLayerColor(
                       AshColorProvider::ContentLayerType::kIconColorPrimary),
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h
index 2d38ac3..2a38f69 100644
--- a/ash/projector/projector_annotation_tray.h
+++ b/ash/projector/projector_annotation_tray.h
@@ -52,7 +52,7 @@
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
 
   void HideAnnotationTray();
-  void OnCanvasInitializationFailed();
+  void SetTrayEnabled(bool enabled);
 
  private:
   void ToggleAnnotator();
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc
index 8dbf09d..7f5ae71 100644
--- a/ash/projector/projector_ui_controller.cc
+++ b/ash/projector/projector_ui_controller.cc
@@ -162,7 +162,7 @@
     projector_annotation_tray->HideAnnotationTray();
   }
 
-  should_enable_annotation_tray_button_.reset();
+  canvas_initialized_state_.reset();
   current_root_ = nullptr;
 }
 
@@ -187,17 +187,9 @@
   }
 }
 
-// TODO(b/232419423): Rename this function.
 void ProjectorUiController::OnCanvasInitialized(bool success) {
-  should_enable_annotation_tray_button_ = success;
-
-  if (auto* projector_annotation_tray =
-          GetProjectorAnnotationTrayForRoot(current_root_)) {
-    projector_annotation_tray->SetEnabled(
-        *should_enable_annotation_tray_button_);
-    if (!*should_enable_annotation_tray_button_)
-      projector_annotation_tray->OnCanvasInitializationFailed();
-  }
+  canvas_initialized_state_ = success;
+  UpdateTrayEnabledState();
 }
 
 void ProjectorUiController::OnRecordedWindowChangingRoot(
@@ -207,8 +199,8 @@
   SetProjectorAnnotationTrayVisibility(current_root_, /*visible=*/false);
   SetProjectorAnnotationTrayVisibility(new_root, /*visible=*/true);
   current_root_ = new_root;
-  if (should_enable_annotation_tray_button_)
-    OnCanvasInitialized(*should_enable_annotation_tray_button_);
+  if (canvas_initialized_state_)
+    UpdateTrayEnabledState();
 }
 
 void ProjectorUiController::OnProjectorSessionActiveStateChanged(bool active) {
@@ -225,4 +217,11 @@
       {kProjectorYellowPenColor, ProjectorMarkerColor::kYellow}};
   return marker_colors_map[color];
 }
+
+void ProjectorUiController::UpdateTrayEnabledState() {
+  if (auto* projector_annotation_tray =
+          GetProjectorAnnotationTrayForRoot(current_root_)) {
+    projector_annotation_tray->SetTrayEnabled(*canvas_initialized_state_);
+  }
+}
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller.h b/ash/projector/projector_ui_controller.h
index 33e2889..cdcb3bd7 100644
--- a/ash/projector/projector_ui_controller.h
+++ b/ash/projector/projector_ui_controller.h
@@ -60,12 +60,17 @@
 
   ProjectorMarkerColor GetMarkerColorForMetrics(SkColor color);
 
+  void UpdateTrayEnabledState();
+
   bool annotator_enabled_ = false;
 
   // The current root window in which the video recording is happening.
   aura::Window* current_root_ = nullptr;
 
-  absl::optional<bool> should_enable_annotation_tray_button_;
+  // True if the canvas is initialized successfully, false if it failed to
+  // initialize. An absent value indicates that the initialization has not
+  // completed.
+  absl::optional<bool> canvas_initialized_state_;
 
   base::ScopedObservation<ProjectorSession, ProjectorSessionObserver>
       projector_session_observation_{this};
diff --git a/ash/services/recording/DEPS b/ash/services/recording/DEPS
index b60a9c8..426d679c 100644
--- a/ash/services/recording/DEPS
+++ b/ash/services/recording/DEPS
@@ -10,6 +10,7 @@
   "+mojo/public",
   "+services/audio/public",
   "+services/viz/privileged/mojom/compositing",
+  "+third_party/abseil-cpp/absl",
   "+ui/gfx",
 ]
 
diff --git a/ash/shelf/hotseat_widget_unittest.cc b/ash/shelf/hotseat_widget_unittest.cc
index bd04d02..f5d059e 100644
--- a/ash/shelf/hotseat_widget_unittest.cc
+++ b/ash/shelf/hotseat_widget_unittest.cc
@@ -2303,9 +2303,7 @@
 
 // Tests that hotseat bounds don't jump when the hotseat widget is translated
 // when a transitionj animation starts.
-// Flaky https://crbug.com/1292675
-TEST_P(HotseatWidgetTest,
-       DISABLED_InitialAnimationPositionWithNonIdentityTransform) {
+TEST_P(HotseatWidgetTest, InitialAnimationPositionWithNonIdentityTransform) {
   TabletModeControllerTestApi().EnterTabletMode();
   // Add an app to shelf - the app will be used to track the shelf view position
   // throughout the test.
@@ -2316,6 +2314,12 @@
       AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
   wm::ActivateWindow(window.get());
 
+  // Make sure that all shelf item views complete their bounds animations
+  // before starting the test (tests depend on the first item bounds within the
+  // shelf view).
+  auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting();
+  ShelfViewTestAPI(shelf_view).RunMessageLoopUntilAnimationsDone();
+
   HotseatWidget* const hotseat_widget = GetPrimaryShelf()->hotseat_widget();
   gfx::Point last_app_views_position =
       hotseat_widget->GetWindowBoundsInScreen().origin();
diff --git a/ash/shelf/shelf_shutdown_confirmation_bubble.cc b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
index 92ca28c..a524061 100644
--- a/ash/shelf/shelf_shutdown_confirmation_bubble.cc
+++ b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
@@ -200,6 +201,17 @@
   confirm_->SetEnabledTextColors(button_color);
 }
 
+void ShelfShutdownConfirmationBubble::GetAccessibleNodeData(
+    ui::AXNodeData* node_data) {
+  node_data->role = ax::mojom::Role::kDialog;
+  node_data->SetName(title_->GetText());
+}
+
+std::u16string ShelfShutdownConfirmationBubble::GetAccessibleWindowTitle()
+    const {
+  return title_->GetText();
+}
+
 void ShelfShutdownConfirmationBubble::OnCancelled() {
   dialog_result_ = DialogResult::kCancelled;
   GetWidget()->Close();
diff --git a/ash/shelf/shelf_shutdown_confirmation_bubble.h b/ash/shelf/shelf_shutdown_confirmation_bubble.h
index 81ba13f..126dcfda 100644
--- a/ash/shelf/shelf_shutdown_confirmation_bubble.h
+++ b/ash/shelf/shelf_shutdown_confirmation_bubble.h
@@ -57,6 +57,8 @@
 
   // views::View:
   void OnThemeChanged() override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  std::u16string GetAccessibleWindowTitle() const override;
 
  protected:
   // ShelfBubble:
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index ab95bc58..674c796 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -431,7 +431,7 @@
   separator_ = new views::Separator();
   separator_->SetColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kSeparatorColor));
-  separator_->SetPreferredHeight(kSeparatorSize);
+  separator_->SetPreferredLength(kSeparatorSize);
   separator_->SetVisible(false);
   ConfigureChildView(separator_, ui::LAYER_TEXTURED);
   AddChildView(separator_);
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index b2e28ee..8e0ebf3f 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -107,10 +107,10 @@
 }
 
 // Notify all the other components besides the System UI to update on the color
-// mode or theme changes. E.g, Chrome browser, WebUI. And since AshColorProvider
-// is kind of NativeTheme of ChromeOS. This will notify the View::OnThemeChanged
-// to live update the colors on color mode or theme changes as well.
-void NotifyColorModeAndThemeChanges(bool is_dark_mode_enabled) {
+// mode changes. E.g, Chrome browser, WebUI. And since AshColorProvider is kind
+// of NativeTheme of ChromeOS. This will notify the View::OnThemeChanged to live
+// update the colors on color mode or theme changes as well.
+void NotifyColorModeChanges(bool is_dark_mode_enabled) {
   auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
   native_theme->set_use_dark_colors(is_dark_mode_enabled);
   native_theme->NotifyOnNativeThemeUpdated();
@@ -184,8 +184,6 @@
 void AshColorProvider::RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(prefs::kDarkModeEnabled,
                                 kDefaultDarkModeEnabled);
-  registry->RegisterBooleanPref(prefs::kColorModeThemed,
-                                kDefaultColorModeThemed);
 }
 
 void AshColorProvider::OnActiveUserPrefServiceChanged(PrefService* prefs) {
@@ -200,14 +198,9 @@
       prefs::kDarkModeEnabled,
       base::BindRepeating(&AshColorProvider::NotifyDarkModeEnabledPrefChange,
                           base::Unretained(this)));
-  pref_change_registrar_->Add(
-      prefs::kColorModeThemed,
-      base::BindRepeating(&AshColorProvider::NotifyColorModeThemedPrefChange,
-                          base::Unretained(this)));
 
   // Immediately tell all the observers to load this user's saved preferences.
   NotifyDarkModeEnabledPrefChange();
-  NotifyColorModeThemedPrefChange();
 }
 
 void AshColorProvider::OnSessionStateChanged(
@@ -219,7 +212,6 @@
     force_oobe_light_mode_ = false;
   }
   NotifyDarkModeEnabledPrefChange();
-  NotifyColorModeThemedPrefChange();
 }
 
 SkColor AshColorProvider::GetShieldLayerColor(ShieldLayerType type) const {
@@ -291,12 +283,13 @@
 }
 
 SkColor AshColorProvider::GetBackgroundColor() const {
-  return IsThemed() ? GetBackgroundThemedColor() : GetBackgroundDefaultColor();
+  return GetBackgroundThemedColorImpl(GetBackgroundDefaultColor(),
+                                      IsDarkModeEnabled());
 }
 
 SkColor AshColorProvider::GetInvertedBackgroundColor() const {
-  return IsThemed() ? GetInvertedBackgroundThemedColor()
-                    : GetInvertedBackgroundDefaultColor();
+  return GetBackgroundThemedColorImpl(GetInvertedBackgroundDefaultColor(),
+                                      !IsDarkModeEnabled());
 }
 
 SkColor AshColorProvider::GetBackgroundColorInMode(bool use_dark_color) const {
@@ -381,12 +374,6 @@
           .FindBoolPath(account_id, prefs::kDarkModeEnabled);
 }
 
-bool AshColorProvider::IsThemed() const {
-  if (!active_user_pref_service_)
-    return kDefaultColorModeThemed;
-  return active_user_pref_service_->GetBoolean(prefs::kColorModeThemed);
-}
-
 void AshColorProvider::ToggleColorMode() {
   DCHECK(active_user_pref_service_);
   active_user_pref_service_->SetBoolean(prefs::kDarkModeEnabled,
@@ -397,16 +384,6 @@
   DarkModeController::Get()->ToggledByUser();
 }
 
-void AshColorProvider::UpdateColorModeThemed(bool is_themed) {
-  if (is_themed == IsThemed())
-    return;
-
-  DCHECK(active_user_pref_service_);
-  active_user_pref_service_->SetBoolean(prefs::kColorModeThemed, is_themed);
-  active_user_pref_service_->CommitPendingWrite();
-  NotifyColorModeThemedPrefChange();
-}
-
 SkColor AshColorProvider::GetShieldLayerColorImpl(ShieldLayerType type,
                                                   bool inverted) const {
   constexpr int kAlphas[] = {kAlpha20, kAlpha40, kAlpha60,
@@ -535,16 +512,6 @@
   return GetBackgroundColorInMode(!IsDarkModeEnabled());
 }
 
-SkColor AshColorProvider::GetBackgroundThemedColor() const {
-  return GetBackgroundThemedColorImpl(GetBackgroundDefaultColor(),
-                                      IsDarkModeEnabled());
-}
-
-SkColor AshColorProvider::GetInvertedBackgroundThemedColor() const {
-  return GetBackgroundThemedColorImpl(GetInvertedBackgroundDefaultColor(),
-                                      !IsDarkModeEnabled());
-}
-
 SkColor AshColorProvider::GetBackgroundThemedColorImpl(
     SkColor default_color,
     bool use_dark_color) const {
@@ -578,15 +545,7 @@
   for (auto& observer : observers_)
     observer.OnColorModeChanged(is_enabled);
 
-  NotifyColorModeAndThemeChanges(IsDarkModeEnabled());
-}
-
-void AshColorProvider::NotifyColorModeThemedPrefChange() {
-  const bool is_themed = IsThemed();
-  for (auto& observer : observers_)
-    observer.OnColorModeThemed(is_themed);
-
-  NotifyColorModeAndThemeChanges(IsDarkModeEnabled());
+  NotifyColorModeChanges(IsDarkModeEnabled());
 }
 
 base::ScopedClosureRunner AshColorProvider::GetNotifyOnDarkModeChangeClosure() {
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index bfc32ca..d4cd9b1 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -96,16 +96,9 @@
   // Gets the background color in the desired color mode dark/light.
   SkColor GetBackgroundColorInMode(bool use_dark_color) const;
 
-  // Whether the system color mode is themed, by default is true. If true, the
-  // background color will be calculated based on extracted wallpaper color.
-  bool IsThemed() const;
-
   // Toggles pref |kDarkModeEnabled|.
   void ToggleColorMode();
 
-  // Updates pref |kColorModeThemed| to |is_themed|.
-  void UpdateColorModeThemed(bool is_themed);
-
  private:
   friend class ScopedLightModeAsDefault;
   friend class ScopedAssistantLightModeAsDefault;
@@ -126,10 +119,6 @@
   // Gets the background default color based on the current inverted color mode.
   SkColor GetInvertedBackgroundDefaultColor() const;
 
-  // Gets the background themed color based on the current color mode.
-  SkColor GetBackgroundThemedColor() const;
-  // Gets the background themed color based on the current inverted color mode.
-  SkColor GetInvertedBackgroundThemedColor() const;
   // Gets the background themed color that's calculated based on the color
   // extracted from wallpaper. For dark mode, it will be dark muted wallpaper
   // prominent color + SK_ColorBLACK 50%. For light mode, it will be light
@@ -141,9 +130,6 @@
   // Notifies all the observers on |kDarkModeEnabled|'s change.
   void NotifyDarkModeEnabledPrefChange();
 
-  // Notifies all the observers on |kColorModeThemed|'s change.
-  void NotifyColorModeThemedPrefChange();
-
   // Returns a closure which calls `NotifyIfDarkModeChanged` if the dark mode
   // changed between creation and getting out of scope.
   base::ScopedClosureRunner GetNotifyOnDarkModeChangeClosure();
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc
index 0a5c94e8..0eea491 100644
--- a/ash/system/accessibility/autoclick_menu_view.cc
+++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -109,7 +109,7 @@
                                    base::Unretained(pause_button_)))),
           views::Builder<views::Separator>()
               .CopyAddressTo(&separator_)
-              .SetPreferredHeight(kSeparatorHeight)
+              .SetPreferredLength(kSeparatorHeight)
               .SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
                   separator_spacing - kUnifiedTopShortcutSpacing, 0,
                   separator_spacing, 0))),
diff --git a/ash/system/accessibility/floating_accessibility_view.cc b/ash/system/accessibility/floating_accessibility_view.cc
index e6daa47..37f412d 100644
--- a/ash/system/accessibility/floating_accessibility_view.cc
+++ b/ash/system/accessibility/floating_accessibility_view.cc
@@ -54,7 +54,7 @@
   auto separator = std::make_unique<views::Separator>();
   separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kSeparatorColor));
-  separator->SetPreferredHeight(kSeparatorHeight);
+  separator->SetPreferredLength(kSeparatorHeight);
   int total_height = kUnifiedTopShortcutSpacing * 2 + kTrayItemSize;
   int separator_spacing = (total_height - kSeparatorHeight) / 2;
   separator->SetBorder(views::CreateEmptyBorder(
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_view.cc b/ash/system/accessibility/select_to_speak/select_to_speak_menu_view.cc
index 1a43a15..661f70e0 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_view.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_view.cc
@@ -151,7 +151,7 @@
           views::Builder<views::Separator>()
               .SetColor(AshColorProvider::Get()->GetContentLayerColor(
                   AshColorProvider::ContentLayerType::kSeparatorColor))
-              .SetPreferredHeight(kSeparatorHeight)
+              .SetPreferredLength(kSeparatorHeight)
               .SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
                   separator_spacing - kUnifiedTopShortcutSpacing, 0,
                   separator_spacing, 0))),
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 972f35f..6ebfd405 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -165,7 +165,7 @@
       AddChildView(std::make_unique<BatteryView>());
 
       auto* separator = AddChildView(std::make_unique<views::Separator>());
-      separator->SetPreferredHeight(GetPreferredSize().height());
+      separator->SetPreferredLength(GetPreferredSize().height());
       separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
           AshColorProvider::ContentLayerType::kSeparatorColor));
     }
diff --git a/ash/system/phonehub/phone_status_view.cc b/ash/system/phonehub/phone_status_view.cc
index bbe235d..c3dca1e 100644
--- a/ash/system/phonehub/phone_status_view.cc
+++ b/ash/system/phonehub/phone_status_view.cc
@@ -140,7 +140,7 @@
   separator_ = new views::Separator();
   separator_->SetColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kSeparatorColor));
-  separator_->SetPreferredHeight(kSeparatorHeight);
+  separator_->SetPreferredLength(kSeparatorHeight);
   AddView(TriView::Container::CENTER, separator_);
 
   settings_button_ = new IconButton(
diff --git a/ash/system/time/calendar_event_list_view.cc b/ash/system/time/calendar_event_list_view.cc
index f19d181..bdc3cf55 100644
--- a/ash/system/time/calendar_event_list_view.cc
+++ b/ash/system/time/calendar_event_list_view.cc
@@ -108,14 +108,12 @@
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
 
-  // Set up background color and blur.
+  // Set up background color.
   auto* color_provider = AshColorProvider::Get();
   SkColor background_color = color_provider->GetBaseLayerColor(
       AshColorProvider::BaseLayerType::kOpaque);
   SetBackground(views::CreateSolidBackground(background_color));
   SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
 
   views::BoxLayout* button_layout = close_button_container_->SetLayoutManager(
       std::make_unique<views::BoxLayout>(
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index ce6da2e..c416a2b 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -1274,6 +1274,17 @@
   }
 
   if (!IsDateCellViewFocused()) {
+    if (is_tab_key_pressed && key_event->IsShiftDown()) {
+      // If this is reverse tab navigation (Shift+Tab) and current focused view
+      // is the last focusable view, then make an attempt to navigate to the
+      // previous widget (most likely to the message center). Stop the
+      // propagation of the event if the attempt was successful.
+      const auto* next_reverse_view = focus_manager->GetNextFocusableView(
+          focus_manager->GetFocusedView(), GetWidget(), /*reverse=*/true,
+          /*dont_loop=*/true);
+      if (!next_reverse_view && controller_->FocusOut(/*reverse=*/true))
+        event->StopPropagation();
+    }
     TrayDetailedView::OnEvent(event);
     return;
   }
@@ -1281,10 +1292,10 @@
   // When tab key is pressed, stops focusing on any `CalendarDateCellView` and
   // goes to the next focusable button in the header.
   if (is_tab_key_pressed) {
-    // Set focus on `up_button_`/`event_list_view_` or null
+    // Set focus on `down_button_`/`event_list_view_` or null
     // pointer to escape the focusing on the date cell.
     if (key_event->IsShiftDown()) {
-      up_button_->RequestFocus();
+      down_button_->RequestFocus();
     } else if (event_list_view_) {
       // Moves focusing ring to the close button of the event list.
       event_list_view_->RequestFocus();
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index 1b26b91..ad81a892 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -4,19 +4,26 @@
 
 #include "ash/system/time/calendar_view.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/close_button.h"
 #include "ash/style/icon_button.h"
+#include "ash/system/message_center/unified_message_center_bubble.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/time/calendar_event_list_view.h"
 #include "ash/system/time/calendar_month_view.h"
 #include "ash/system/time/calendar_unittest_utils.h"
 #include "ash/system/time/calendar_utils.h"
 #include "ash/system/time/calendar_view_controller.h"
 #include "ash/system/tray/detailed_view_delegate.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
+#include "ash/system/unified/unified_system_tray_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/time/time_override.h"
@@ -24,6 +31,8 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/events/event_utils.h"
+#include "ui/message_center/message_center.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/focus/focus_manager.h"
@@ -481,6 +490,28 @@
   // Moves to down button.
   PressTab();
   EXPECT_EQ(down_button(), focus_manager->GetFocusedView());
+
+  // Moves to today's cell.
+  PressTab();
+  EXPECT_EQ(u"7",
+            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
+                ->GetText());
+
+  // Moves to down button.
+  PressShiftTab();
+  EXPECT_EQ(down_button(), focus_manager->GetFocusedView());
+
+  // Moves to up button.
+  PressShiftTab();
+  EXPECT_EQ(up_button(), focus_manager->GetFocusedView());
+
+  // Moves to settings button.
+  PressShiftTab();
+  EXPECT_EQ(settings_button(), focus_manager->GetFocusedView());
+
+  // Moves to "Go back to today" button.
+  PressShiftTab();
+  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
 }
 
 // Tests the focus loop between the back button, today's button, settings
@@ -1330,4 +1361,129 @@
   EXPECT_EQ(1.0f, header()->layer()->opacity());
 }
 
+// Test class for testing the `CalendarView` together with the message center
+// bubble.
+class CalendarViewWithMessageCenterTest : public AshTestBase {
+ public:
+  CalendarViewWithMessageCenterTest() = default;
+  CalendarViewWithMessageCenterTest(const CalendarViewWithMessageCenterTest&) =
+      delete;
+  CalendarViewWithMessageCenterTest& operator=(
+      const CalendarViewWithMessageCenterTest&) = delete;
+  ~CalendarViewWithMessageCenterTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitWithFeatures(
+        {features::kCalendarView, features::kNotificationsRefresh}, {});
+    AshTestBase::SetUp();
+  }
+
+  views::FocusManager* message_center_focus_manager() {
+    return GetPrimaryUnifiedSystemTray()
+        ->message_center_bubble()
+        ->message_center_view()
+        ->GetFocusManager();
+  }
+
+  views::FocusManager* calendar_focus_manager() {
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_view()
+        ->detailed_view_for_testing()
+        ->GetFocusManager();
+  }
+
+  void AddNotification() {
+    message_center::MessageCenter::Get()->AddNotification(
+        std::make_unique<message_center::Notification>(
+            message_center::NOTIFICATION_TYPE_BASE_FORMAT,
+            "test_notification_id", u"test title", u"test message",
+            ui::ImageModel(), std::u16string(), GURL(),
+            message_center::NotifierId(),
+            message_center::RichNotificationData(),
+            new message_center::NotificationDelegate()));
+  }
+
+  void ShowCalendarView() {
+    ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
+    GetPrimaryUnifiedSystemTray()->OnDateTrayActionPerformed(event);
+  }
+
+  // Calculates the number of focusable views inside the message center bubble
+  // in order to avoid hardcoding that number / be independent from
+  // implementation details of another widget.
+  int GetNumberOfFocusableViewsInMessageCenter() {
+    int count = 0;
+    auto* widget = GetPrimaryUnifiedSystemTray()
+                       ->message_center_bubble()
+                       ->GetBubbleWidget();
+    views::View* current_focusable_view = nullptr;
+    while ((current_focusable_view =
+                message_center_focus_manager()->GetNextFocusableView(
+                    current_focusable_view, widget, /*reverse=*/false,
+                    /*dont_loop=*/true)))
+      count++;
+    return count;
+  }
+
+  void PressTab() {
+    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+    generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
+  }
+
+  void PressShiftTab() {
+    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+    generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_SHIFT_DOWN);
+  }
+
+ private:
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+};
+
+// Tests `Tab` / `Shift+Tab` navigation within two bubbles.
+TEST_F(CalendarViewWithMessageCenterTest,
+       CalendarViewFocusingWithMessageCenterOpened) {
+  EXPECT_FALSE(GetPrimaryUnifiedSystemTray()->IsShowingCalendarView());
+  EXPECT_FALSE(GetPrimaryUnifiedSystemTray()->IsMessageCenterBubbleShown());
+
+  AddNotification();
+  ShowCalendarView();
+
+  EXPECT_TRUE(GetPrimaryUnifiedSystemTray()->IsShowingCalendarView());
+  EXPECT_TRUE(GetPrimaryUnifiedSystemTray()->IsMessageCenterBubbleShown());
+
+  int number_of_focusable_views_in_message_center =
+      GetNumberOfFocusableViewsInMessageCenter();
+
+  // Today's date cell should be focused now.
+  PressTab();
+  auto* current_date_cell_view = calendar_focus_manager()->GetFocusedView();
+  EXPECT_STREQ(current_date_cell_view->GetClassName(), "CalendarDateCellView");
+
+  // Enter the message center.
+  PressTab();
+
+  // Keep tabbing until exiting the message center.
+  for (int i = 0; i < number_of_focusable_views_in_message_center; i++)
+    PressTab();
+
+  // "Previous menu" / exit from calendar button should be focused now.
+  EXPECT_EQ(u"Previous menu",
+            static_cast<IconButton*>(calendar_focus_manager()->GetFocusedView())
+                ->GetAccessibleName());
+
+  // Move back to the message center.
+  PressShiftTab();
+
+  // Keep tabbing backwards until exiting the message center.
+  for (int i = 0; i < number_of_focusable_views_in_message_center; i++)
+    PressShiftTab();
+
+  // Today's date cell should be focused now.
+  EXPECT_EQ(current_date_cell_view, calendar_focus_manager()->GetFocusedView());
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index 7c44c98c1..ab43fd4 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -267,7 +267,7 @@
 
 views::Separator* TrayPopupUtils::CreateVerticalSeparator() {
   views::Separator* separator = new views::Separator();
-  separator->SetPreferredHeight(24);
+  separator->SetPreferredLength(24);
   separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kSeparatorColor));
   return separator;
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index 3389ad9..126f9fd2 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -598,7 +598,7 @@
 
   if (PowerStatus::Get()->IsBatteryPresent()) {
     separator_ = AddChildView(std::make_unique<views::Separator>());
-    separator_->SetPreferredHeight(kUnifiedSystemInfoHeight);
+    separator_->SetPreferredLength(kUnifiedSystemInfoHeight);
 
     const bool use_smart_charging_ui = UseSmartChargingUI();
     if (use_smart_charging_ui)
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 222da27..c656c97 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -30,6 +30,7 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/style/ash_color_provider.h"
+#include "ash/wallpaper/wallpaper_pref_manager.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
 #include "ash/wallpaper/wallpaper_view.h"
@@ -581,171 +582,6 @@
   return base::PathExists(valid_path) ? valid_path : base::FilePath();
 }
 
-// Populates online wallpaper related info in |info|.
-void PopulateOnlineWallpaperInfo(WallpaperInfo* info,
-                                 const base::Value& info_dict) {
-  const std::string* asset_id_str = info_dict.FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperAssetIdNodeName);
-  const std::string* collection_id = info_dict.FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperCollectionIdNodeName);
-  const std::string* dedup_key = info_dict.FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperDedupKeyNodeName);
-  const std::string* unit_id_str = info_dict.FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperUnitIdNodeName);
-  const base::Value* variant_list = info_dict.FindListPath(
-      WallpaperControllerImpl::kNewWallpaperVariantListNodeName);
-
-  info->collection_id = collection_id ? *collection_id : std::string();
-  info->dedup_key = dedup_key ? absl::make_optional(*dedup_key) : absl::nullopt;
-
-  if (asset_id_str) {
-    uint64_t asset_id;
-    if (base::StringToUint64(*asset_id_str, &asset_id))
-      info->asset_id = absl::make_optional(asset_id);
-  }
-  if (unit_id_str) {
-    uint64_t unit_id;
-    if (base::StringToUint64(*unit_id_str, &unit_id))
-      info->unit_id = absl::make_optional(unit_id);
-  }
-  if (variant_list) {
-    std::vector<OnlineWallpaperVariant> variants;
-    for (const auto& variant_info : variant_list->GetListDeprecated()) {
-      const std::string* variant_asset_id_str = variant_info.FindStringPath(
-          WallpaperControllerImpl::kNewWallpaperAssetIdNodeName);
-      const std::string* url = variant_info.FindStringPath(
-          WallpaperControllerImpl::kOnlineWallpaperUrlNodeName);
-      absl::optional<int> type = variant_info.FindIntPath(
-          WallpaperControllerImpl::kOnlineWallpaperTypeNodeName);
-      if (variant_asset_id_str && url && type.has_value()) {
-        uint64_t variant_asset_id;
-        if (base::StringToUint64(*variant_asset_id_str, &variant_asset_id))
-          variants.emplace_back(
-              variant_asset_id, GURL(*url),
-              static_cast<backdrop::Image::ImageType>(type.value()));
-      }
-    }
-    info->variants = std::move(variants);
-  }
-}
-
-bool GetWallpaperInfo(const AccountId& account_id,
-                      const PrefService* const pref_service,
-                      const std::string& pref_name,
-                      WallpaperInfo* info) {
-  if (!pref_service)
-    return false;
-
-  const base::Value* info_dict =
-      pref_service->GetDictionary(pref_name)->FindDictKey(
-          account_id.GetUserEmail());
-  if (!info_dict)
-    return false;
-
-  // Use temporary variables to keep |info| untouched in the error case.
-  const std::string* location = info_dict->FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperLocationNodeName);
-  absl::optional<int> layout = info_dict->FindIntPath(
-      WallpaperControllerImpl::kNewWallpaperLayoutNodeName);
-  absl::optional<int> type = info_dict->FindIntPath(
-      WallpaperControllerImpl::kNewWallpaperTypeNodeName);
-  const std::string* date_string = info_dict->FindStringPath(
-      WallpaperControllerImpl::kNewWallpaperDateNodeName);
-
-  if (!location || !layout || !type || !date_string)
-    return false;
-
-  if (type.value() >= static_cast<int>(WallpaperType::kCount))
-    return false;
-
-  WallpaperType wallpaper_type = static_cast<WallpaperType>(type.value());
-  if (!features::IsWallpaperGooglePhotosIntegrationEnabled() &&
-      (wallpaper_type == WallpaperType::kOnceGooglePhotos ||
-       wallpaper_type == WallpaperType::kDailyGooglePhotos)) {
-    return false;
-  }
-
-  int64_t date_val;
-  if (!base::StringToInt64(*date_string, &date_val))
-    return false;
-
-  info->location = *location;
-  info->layout = static_cast<WallpaperLayout>(layout.value());
-  info->type = wallpaper_type;
-  info->date = base::Time::FromInternalValue(date_val);
-  PopulateOnlineWallpaperInfo(info, *info_dict);
-  return true;
-}
-
-bool SetWallpaperInfo(const AccountId& account_id,
-                      const WallpaperInfo& info,
-                      PrefService* const pref_service,
-                      const std::string& pref_name) {
-  if (!pref_service)
-    return false;
-
-  DictionaryPrefUpdate wallpaper_update(pref_service, pref_name);
-  base::Value wallpaper_info_dict(base::Value::Type::DICTIONARY);
-  if (info.asset_id.has_value()) {
-    wallpaper_info_dict.SetStringPath(
-        WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
-        base::NumberToString(info.asset_id.value()));
-  }
-  if (info.unit_id.has_value()) {
-    wallpaper_info_dict.SetStringPath(
-        WallpaperControllerImpl::kNewWallpaperUnitIdNodeName,
-        base::NumberToString(info.unit_id.value()));
-  }
-  base::Value online_wallpaper_variant_list(base::Value::Type::LIST);
-  for (const auto& variant : info.variants) {
-    base::Value online_wallpaper_variant_dict(base::Value::Type::DICTIONARY);
-    online_wallpaper_variant_dict.SetStringPath(
-        WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
-        base::NumberToString(variant.asset_id));
-    online_wallpaper_variant_dict.SetStringPath(
-        WallpaperControllerImpl::kOnlineWallpaperUrlNodeName,
-        variant.raw_url.spec());
-    online_wallpaper_variant_dict.SetIntPath(
-        WallpaperControllerImpl::kOnlineWallpaperTypeNodeName,
-        static_cast<int>(variant.type));
-    online_wallpaper_variant_list.Append(
-        std::move(online_wallpaper_variant_dict));
-  }
-  wallpaper_info_dict.SetKey(
-      WallpaperControllerImpl::kNewWallpaperVariantListNodeName,
-      std::move(online_wallpaper_variant_list));
-  wallpaper_info_dict.SetStringPath(
-      WallpaperControllerImpl::kNewWallpaperCollectionIdNodeName,
-      info.collection_id);
-  wallpaper_info_dict.SetStringPath(
-      WallpaperControllerImpl::kNewWallpaperDateNodeName,
-      base::NumberToString(info.date.ToInternalValue()));
-  if (info.dedup_key) {
-    wallpaper_info_dict.SetStringPath(
-        WallpaperControllerImpl::kNewWallpaperDedupKeyNodeName,
-        info.dedup_key.value());
-  }
-  wallpaper_info_dict.SetStringPath(
-      WallpaperControllerImpl::kNewWallpaperLocationNodeName, info.location);
-  wallpaper_info_dict.SetIntPath(
-      WallpaperControllerImpl::kNewWallpaperLayoutNodeName, info.layout);
-  wallpaper_info_dict.SetIntPath(
-      WallpaperControllerImpl::kNewWallpaperTypeNodeName,
-      static_cast<int>(info.type));
-  wallpaper_update->SetKey(account_id.GetUserEmail(),
-                           std::move(wallpaper_info_dict));
-  return true;
-}
-
-void RemoveWallpaperInfo(const AccountId& account_id,
-                         PrefService* const pref_service,
-                         const std::string& pref_name) {
-  if (!pref_service)
-    return;
-  DictionaryPrefUpdate prefs_wallpapers_info_update(pref_service, pref_name);
-  prefs_wallpapers_info_update->RemoveKey(account_id.GetUserEmail());
-}
-
 // Deletes the user-specific directory inside the Google Photos cache
 // directory. Only call this by posting it to `sequenced_task_runner_` with no
 // delay to ensure that file IO is called in a well defined order. This avoids
@@ -807,41 +643,26 @@
 const char WallpaperControllerImpl::kLargeWallpaperSubDir[] = "large";
 const char WallpaperControllerImpl::kOriginalWallpaperSubDir[] = "original";
 
-const char WallpaperControllerImpl::kNewWallpaperAssetIdNodeName[] = "asset_id";
-const char WallpaperControllerImpl::kNewWallpaperCollectionIdNodeName[] =
-    "collection_id";
-const char WallpaperControllerImpl::kNewWallpaperDateNodeName[] = "date";
-const char WallpaperControllerImpl::kNewWallpaperDedupKeyNodeName[] =
-    "dedup_key";
-const char WallpaperControllerImpl::kNewWallpaperLayoutNodeName[] = "layout";
-const char WallpaperControllerImpl::kNewWallpaperLocationNodeName[] = "file";
-const char WallpaperControllerImpl::kNewWallpaperTypeNodeName[] = "type";
-const char WallpaperControllerImpl::kNewWallpaperUnitIdNodeName[] = "unit_id";
-const char WallpaperControllerImpl::kNewWallpaperVariantListNodeName[] =
-    "variants";
-const char WallpaperControllerImpl::kOnlineWallpaperTypeNodeName[] =
-    "online_image_type";
-const char WallpaperControllerImpl::kOnlineWallpaperUrlNodeName[] = "url";
-
 // static
 std::unique_ptr<WallpaperControllerImpl> WallpaperControllerImpl::Create(
     PrefService* local_state) {
   auto online_wallpaper_variant_fetcher =
       std::make_unique<OnlineWallpaperVariantInfoFetcher>();
+  auto pref_manager = WallpaperPrefManager::Create(local_state);
   return std::make_unique<WallpaperControllerImpl>(
-      local_state, std::move(online_wallpaper_variant_fetcher));
+      std::move(pref_manager), std::move(online_wallpaper_variant_fetcher));
 }
 
 WallpaperControllerImpl::WallpaperControllerImpl(
-    PrefService* local_state,
+    std::unique_ptr<WallpaperPrefManager> pref_manager,
     std::unique_ptr<OnlineWallpaperVariantInfoFetcher> online_fetcher)
-    : variant_info_fetcher_(std::move(online_fetcher)),
+    : pref_manager_(std::move(pref_manager)),
+      variant_info_fetcher_(std::move(online_fetcher)),
       color_profiles_(GetProminentColorProfiles()),
       wallpaper_reload_delay_(kWallpaperReloadDelay),
       sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
-      local_state_(local_state) {
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {
   DCHECK(!color_profiles_.empty());
   prominent_colors_ =
       std::vector<SkColor>(color_profiles_.size(), kInvalidWallpaperColor);
@@ -860,23 +681,6 @@
 }
 
 // static
-void WallpaperControllerImpl::RegisterLocalStatePrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(prefs::kUserWallpaperInfo);
-  registry->RegisterDictionaryPref(prefs::kWallpaperColors);
-  registry->RegisterDictionaryPref(prefs::kRecentDailyGooglePhotosWallpapers);
-}
-
-// static
-void WallpaperControllerImpl::RegisterProfilePrefs(
-    PrefRegistrySimple* registry) {
-  using user_prefs::PrefRegistrySyncable;
-
-  registry->RegisterDictionaryPref(prefs::kSyncableWallpaperInfo,
-                                   PrefRegistrySyncable::SYNCABLE_OS_PREF);
-}
-
-// static
 gfx::Size WallpaperControllerImpl::GetMaxDisplaySizeInNative() {
   // Return an empty size for test environments where the screen is null.
   if (!display::Screen::GetScreen())
@@ -1112,20 +916,14 @@
     return true;
   }
 
-  WallpaperInfo old_info;
-  if (local_state_ && GetUserWallpaperInfo(account_id, &old_info)) {
-    // Remove the color cache of the previous wallpaper if it exists.
-    DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                                 prefs::kWallpaperColors);
-    wallpaper_colors_update->RemoveKey(old_info.location);
-  }
-  bool success = SetLocalWallpaperInfo(account_id, info);
+  pref_manager_->RemoveProminentColors(account_id);
+  bool success = pref_manager_->SetLocalWallpaperInfo(account_id, info);
   // Although `WallpaperType::kCustomized` typed wallpapers are syncable, we
   // don't set synced info until the image is stored in drivefs, so we know when
   // to retry saving it on failure.
   if (IsWallpaperTypeSyncable(info.type) &&
       info.type != WallpaperType::kCustomized) {
-    SetSyncedWallpaperInfo(account_id, info);
+    pref_manager_->SetSyncedWallpaperInfo(account_id, info);
   }
 
   return success;
@@ -1144,7 +942,7 @@
     return true;
   }
 
-  return GetLocalWallpaperInfo(account_id, info);
+  return pref_manager_->GetLocalWallpaperInfo(account_id, info);
 }
 
 bool WallpaperControllerImpl::GetWallpaperFromCache(const AccountId& account_id,
@@ -1201,6 +999,7 @@
 
 void WallpaperControllerImpl::SetClient(WallpaperControllerClient* client) {
   wallpaper_controller_client_ = client;
+  pref_manager_->SetClient(client);
   variant_info_fetcher_->SetClient(client);
 }
 
@@ -1425,42 +1224,14 @@
 bool WallpaperControllerImpl::SetDailyGooglePhotosWallpaperIdCache(
     const AccountId& account_id,
     const DailyGooglePhotosIdCache& ids) {
-  if (!local_state_)
-    return false;
-  DictionaryPrefUpdate daily_google_photos_ids_update(
-      local_state_, prefs::kRecentDailyGooglePhotosWallpapers);
-  base::Value id_list(base::Value::Type::LIST);
-  for (auto id = ids.rbegin(); id != ids.rend(); id++) {
-    id_list.Append(base::NumberToString(*id));
-  }
-  daily_google_photos_ids_update->SetKey(account_id.GetUserEmail(),
-                                         std::move(id_list));
-  return true;
+  return pref_manager_->SetDailyGooglePhotosWallpaperIdCache(account_id, ids);
 }
 
 bool WallpaperControllerImpl::GetDailyGooglePhotosWallpaperIdCache(
     const AccountId& account_id,
     DailyGooglePhotosIdCache& ids_out) const {
-  if (!local_state_)
-    return false;
-
-  const base::Value::Dict* dict =
-      local_state_->GetDictionary(prefs::kRecentDailyGooglePhotosWallpapers)
-          ->GetIfDict();
-  if (!dict)
-    return false;
-
-  const base::Value::List* id_list = dict->FindList(account_id.GetUserEmail());
-  if (!id_list)
-    return false;
-
-  for (auto& id_str : *id_list) {
-    uint32_t id;
-    if (base::StringToUint(id_str.GetString(), &id)) {
-      ids_out.Put(std::move(id));
-    }
-  }
-  return true;
+  return pref_manager_->GetDailyGooglePhotosWallpaperIdCache(account_id,
+                                                             ids_out);
 }
 
 void WallpaperControllerImpl::SetDefaultWallpaper(
@@ -1766,7 +1537,9 @@
 }
 
 void WallpaperControllerImpl::RemoveUserWallpaper(const AccountId& account_id) {
-  RemoveUserWallpaperInfo(account_id);
+  if (wallpaper_cache_map_.find(account_id) != wallpaper_cache_map_.end())
+    wallpaper_cache_map_.erase(account_id);
+  pref_manager_->RemoveUserWallpaperInfo(account_id);
   RemoveUserWallpaperImpl(account_id);
 }
 
@@ -1780,7 +1553,7 @@
       Shell::Get()->session_controller()->IsActiveUserSessionStarted();
   // Removes the wallpaper info so that the user is no longer policy controlled,
   // otherwise setting default wallpaper is not allowed.
-  RemoveUserWallpaperInfo(account_id);
+  pref_manager_->RemoveUserWallpaperInfo(account_id);
   SetDefaultWallpaper(account_id, show_wallpaper, base::DoNothing());
 }
 
@@ -1937,9 +1710,9 @@
   // the |kWallpaperColors| pref.
   // TODO(crbug.com/787134): The |prominent_colors_| of wallpapers with empty
   // location should be cached as well.
-  if (!current_wallpaper_->wallpaper_info().location.empty()) {
-    CacheProminentColors(colors, current_wallpaper_->wallpaper_info().location);
-  }
+  // TODO(skau): This does not guarantee that the current wallpaper is the same
+  // wallpaper for which the colors were calculated.
+  pref_manager_->CacheProminentColors(GetActiveAccountId(), colors);
   SetProminentColors(colors);
 }
 
@@ -1953,7 +1726,7 @@
   // here, otherwise switching back and forth between users constantly would
   // prevent us from ever checking.
   WallpaperInfo info;
-  GetLocalWallpaperInfo(account_id, &info);
+  pref_manager_->GetLocalWallpaperInfo(account_id, &info);
   if (info.type == WallpaperType::kOnceGooglePhotos)
     CheckGooglePhotosStaleness(account_id, info);
 }
@@ -1996,7 +1769,7 @@
     return;
   AccountId account_id = GetActiveAccountId();
   WallpaperInfo local_info;
-  if (!GetLocalWallpaperInfo(account_id, &local_info))
+  if (!pref_manager_->GetLocalWallpaperInfo(account_id, &local_info))
     return;
 
   switch (local_info.type) {
@@ -2051,15 +1824,15 @@
     WallpaperInfo synced_info;
 
     // Migrate wallpaper info to syncable prefs.
-    if (!GetSyncedWallpaperInfo(account_id, &synced_info) &&
-        GetLocalWallpaperInfo(account_id, &local_info) &&
+    if (!pref_manager_->GetSyncedWallpaperInfo(account_id, &synced_info) &&
+        pref_manager_->GetLocalWallpaperInfo(account_id, &local_info) &&
         IsWallpaperTypeSyncable(local_info.type)) {
       if (local_info.type == WallpaperType::kCustomized) {
         base::FilePath source = GetCustomWallpaperDir(kOriginalWallpaperSubDir)
                                     .Append(local_info.location);
         SaveWallpaperToDriveFsAndSyncInfo(account_id, source);
       } else {
-        SetSyncedWallpaperInfo(account_id, local_info);
+        pref_manager_->SetSyncedWallpaperInfo(account_id, local_info);
         wallpaper_controller_client_->MigrateCollectionIdFromChromeApp(
             account_id,
             base::BindOnce(&WallpaperController::SetDailyRefreshCollectionId,
@@ -2164,26 +1937,6 @@
                 : kShellWindowId_WallpaperContainer;
 }
 
-void WallpaperControllerImpl::RemoveUserWallpaperInfo(
-    const AccountId& account_id) {
-  if (wallpaper_cache_map_.find(account_id) != wallpaper_cache_map_.end())
-    wallpaper_cache_map_.erase(account_id);
-
-  WallpaperInfo info;
-  GetUserWallpaperInfo(account_id, &info);
-  RemoveWallpaperInfo(account_id, local_state_, prefs::kUserWallpaperInfo);
-  RemoveWallpaperInfo(account_id, GetUserPrefServiceSyncable(account_id),
-                      prefs::kSyncableWallpaperInfo);
-
-  // Can be null in tests.
-  if (!local_state_)
-    return;
-  // Remove the color cache of the previous wallpaper if it exists.
-  DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                               prefs::kWallpaperColors);
-  wallpaper_colors_update->RemoveKey(info.location);
-}
-
 void WallpaperControllerImpl::RemoveUserWallpaperImpl(
     const AccountId& account_id) {
   if (wallpaper_controller_client_) {
@@ -2917,10 +2670,11 @@
     color_calculator_.reset();
   }
 
-  // Fetch the color cache if it exists.
-  if (!current_wallpaper_->wallpaper_info().location.empty()) {
+  if (GetActiveUserSession()) {
+    // The cache is only available if we have an active session.
+    // Fetch the color cache if it exists.
     absl::optional<std::vector<SkColor>> cached_colors =
-        GetCachedColors(current_wallpaper_->wallpaper_info().location);
+        pref_manager_->GetCachedColors(GetActiveAccountId());
     if (cached_colors.has_value()) {
       SetProminentColors(cached_colors.value());
       return;
@@ -2950,42 +2704,6 @@
          !image.isNull();
 }
 
-void WallpaperControllerImpl::CacheProminentColors(
-    const std::vector<SkColor>& colors,
-    const std::string& current_location) {
-  if (!local_state_)
-    return;
-  DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                               prefs::kWallpaperColors);
-  base::Value wallpaper_colors(base::Value::Type::LIST);
-  for (SkColor color : colors)
-    wallpaper_colors.Append(static_cast<double>(color));
-  wallpaper_colors_update->SetKey(current_location,
-                                  std::move(wallpaper_colors));
-}
-
-absl::optional<std::vector<SkColor>> WallpaperControllerImpl::GetCachedColors(
-    const std::string& current_location) const {
-  if (!local_state_)
-    return absl::nullopt;
-
-  const base::Value* prominent_colors =
-      local_state_->GetDictionary(prefs::kWallpaperColors)
-          ->FindListKey(current_location);
-  if (!prominent_colors)
-    return absl::nullopt;
-
-  absl::optional<std::vector<SkColor>> cached_colors_out;
-  cached_colors_out = std::vector<SkColor>();
-  cached_colors_out.value().reserve(
-      prominent_colors->GetListDeprecated().size());
-  for (const auto& value : prominent_colors->GetListDeprecated()) {
-    cached_colors_out.value().push_back(
-        static_cast<SkColor>(value.GetDouble()));
-  }
-  return cached_colors_out;
-}
-
 void WallpaperControllerImpl::OnAlwaysOnTopWallpaperDecoded(
     const WallpaperInfo& info,
     const gfx::ImageSkia& image) {
@@ -3076,33 +2794,6 @@
   }
 }
 
-bool WallpaperControllerImpl::SetLocalWallpaperInfo(const AccountId& account_id,
-                                                    const WallpaperInfo& info) {
-  return SetWallpaperInfo(account_id, info, local_state_,
-                          prefs::kUserWallpaperInfo);
-}
-
-bool WallpaperControllerImpl::GetLocalWallpaperInfo(const AccountId& account_id,
-                                                    WallpaperInfo* info) const {
-  return GetWallpaperInfo(account_id, local_state_, prefs::kUserWallpaperInfo,
-                          info);
-}
-
-bool WallpaperControllerImpl::GetSyncedWallpaperInfo(
-    const AccountId& account_id,
-    WallpaperInfo* info) const {
-  return GetWallpaperInfo(account_id, GetUserPrefServiceSyncable(account_id),
-                          prefs::kSyncableWallpaperInfo, info);
-}
-
-bool WallpaperControllerImpl::SetSyncedWallpaperInfo(
-    const AccountId& account_id,
-    const WallpaperInfo& info) {
-  return SetWallpaperInfo(account_id, info,
-                          GetUserPrefServiceSyncable(account_id),
-                          prefs::kSyncableWallpaperInfo);
-}
-
 void WallpaperControllerImpl::HandleWallpaperInfoSyncedIn(
     const AccountId& account_id,
     WallpaperInfo info) {
@@ -3275,9 +2966,9 @@
   // handled it locally.
   WallpaperInfo synced_info;
   WallpaperInfo local_info;
-  if (!GetSyncedWallpaperInfo(account_id, &synced_info))
+  if (!pref_manager_->GetSyncedWallpaperInfo(account_id, &synced_info))
     return;
-  if (!GetLocalWallpaperInfo(account_id, &local_info)) {
+  if (!pref_manager_->GetLocalWallpaperInfo(account_id, &local_info)) {
     HandleWallpaperInfoSyncedIn(account_id, synced_info);
     return;
   }
@@ -3394,7 +3085,7 @@
 void WallpaperControllerImpl::OnUpdateWallpaperTimerExpired() {
   WallpaperInfo info;
   auto account_id = GetActiveAccountId();
-  if (!GetLocalWallpaperInfo(account_id, &info)) {
+  if (!pref_manager_->GetLocalWallpaperInfo(account_id, &info)) {
     LOG(ERROR) << "Timer to update wallpaper expired, but the current "
                << "wallpaper info is missing or invalid.";
     return;
@@ -3424,7 +3115,7 @@
 void WallpaperControllerImpl::CheckGooglePhotosStaleness(
     const AccountId& account_id,
     const WallpaperInfo& info) {
-  DCHECK(info.type == WallpaperType::kOnceGooglePhotos);
+  DCHECK_EQ(info.type, WallpaperType::kOnceGooglePhotos);
   wallpaper_controller_client_->FetchGooglePhotosPhoto(
       account_id, info.location,
       base::BindOnce(&WallpaperControllerImpl::HandleGooglePhotosStalenessCheck,
@@ -3474,8 +3165,8 @@
   if (!success)
     return;
   WallpaperInfo local_info;
-  CHECK(GetLocalWallpaperInfo(account_id, &local_info));
-  SetSyncedWallpaperInfo(account_id, local_info);
+  CHECK(pref_manager_->GetLocalWallpaperInfo(account_id, &local_info));
+  pref_manager_->SetSyncedWallpaperInfo(account_id, local_info);
 }
 
 void WallpaperControllerImpl::HandleCustomWallpaperInfoSyncedIn(
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index 3606500..fe16975 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -46,8 +46,6 @@
 #include "ui/native_theme/native_theme_observer.h"
 #include "url/gurl.h"
 
-class PrefRegistrySimple;
-
 namespace base {
 class SequencedTaskRunner;
 }  // namespace base
@@ -59,6 +57,7 @@
 namespace ash {
 
 class WallpaperColorCalculator;
+class WallpaperPrefManager;
 class WallpaperResizer;
 class WallpaperWindowStateManager;
 
@@ -98,28 +97,13 @@
   static const char kLargeWallpaperSubDir[];
   static const char kOriginalWallpaperSubDir[];
 
-  // Names of nodes with wallpaper info in |kUserWallpaperInfo| dictionary.
-  static const char kNewWallpaperAssetIdNodeName[];
-  static const char kNewWallpaperCollectionIdNodeName[];
-  static const char kNewWallpaperDateNodeName[];
-  static const char kNewWallpaperDedupKeyNodeName[];
-  static const char kNewWallpaperLayoutNodeName[];
-  static const char kNewWallpaperLocationNodeName[];
-  static const char kNewWallpaperTypeNodeName[];
-  static const char kNewWallpaperUnitIdNodeName[];
-  static const char kNewWallpaperVariantListNodeName[];
-
-  // Names of nodes for the online wallpaper variant dictionary.
-  static const char kOnlineWallpaperTypeNodeName[];
-  static const char kOnlineWallpaperUrlNodeName[];
-
   static std::unique_ptr<WallpaperControllerImpl> Create(
       PrefService* local_state);
 
   // Prefer to use to obtain an new instance unless injecting non-production
   // members i.e. in tests.
   explicit WallpaperControllerImpl(
-      PrefService* local_state,
+      std::unique_ptr<WallpaperPrefManager> pref_manager,
       std::unique_ptr<OnlineWallpaperVariantInfoFetcher> fetcher);
 
   WallpaperControllerImpl(const WallpaperControllerImpl&) = delete;
@@ -127,9 +111,6 @@
 
   ~WallpaperControllerImpl() override;
 
-  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
-  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
   // Returns the maximum size of all displays combined in native
   // resolutions.  Note that this isn't the bounds of the display who
   // has maximum resolutions. Instead, this returns the size of the
@@ -417,9 +398,6 @@
   // Returns the wallpaper container id for unlocked and locked states.
   int GetWallpaperContainerId(bool locked);
 
-  // Removes |account_id|'s wallpaper info and color cache if it exists.
-  void RemoveUserWallpaperInfo(const AccountId& account_id);
-
   // Implementation of |RemoveUserWallpaper|, which deletes |account_id|'s
   // custom wallpapers and directories.
   void RemoveUserWallpaperImpl(const AccountId& account_id);
@@ -520,7 +498,7 @@
                                       const gfx::ImageSkia& image);
 
   // Used as the callback of downloading wallpapers of type
-  // `WallpaperType::kGooglePhotos`. Shows the wallpaper immediately if
+  // `WallpaperType::kOnceGooglePhotos`. Shows the wallpaper immediately if
   // `params.account_id` is the active user.
   void OnGooglePhotosWallpaperDownloaded(
       const GooglePhotosWallpaperParams& params,
@@ -673,14 +651,6 @@
   // This is used when we want to change wallpaper dimming.
   void RepaintWallpaper();
 
-  bool SetLocalWallpaperInfo(const AccountId& account_id,
-                             const WallpaperInfo& info);
-  bool GetLocalWallpaperInfo(const AccountId& account_id,
-                             WallpaperInfo* info) const;
-  bool SetSyncedWallpaperInfo(const AccountId& account_id,
-                              const WallpaperInfo& info);
-  bool GetSyncedWallpaperInfo(const AccountId& account_id,
-                              WallpaperInfo* info) const;
   void HandleWallpaperInfoSyncedIn(const AccountId& account_id,
                                    WallpaperInfo info);
   void OnAttemptSetOnlineWallpaper(const OnlineWallpaperParams& params,
@@ -778,6 +748,9 @@
 
   std::unique_ptr<WallpaperResizer> current_wallpaper_;
 
+  // Manages interactions with relevant preferences.
+  std::unique_ptr<WallpaperPrefManager> pref_manager_;
+
   // Asynchronous task to extract colors from the wallpaper.
   std::unique_ptr<WallpaperColorCalculator> color_calculator_;
 
@@ -877,11 +850,6 @@
   // default wallpaper decoding is initiated.)
   std::vector<base::FilePath> decode_requests_for_testing_;
 
-  // PrefService provided by Shell when constructing this.
-  // Valid for the lifetime of ash::Shell which owns WallpaperControllerImpl.
-  // May be null in tests.
-  PrefService* local_state_ = nullptr;
-
   base::WallClockTimer update_wallpaper_timer_;
 
   base::WeakPtrFactory<WallpaperControllerImpl> weak_factory_{this};
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 8be5204..d3689b3 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -26,6 +26,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wallpaper/test_wallpaper_controller_client.h"
+#include "ash/wallpaper/wallpaper_pref_manager.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
 #include "ash/wallpaper/wallpaper_view.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
@@ -267,50 +268,49 @@
   base::Value wallpaper_info_dict(base::Value::Type::DICTIONARY);
   if (info.asset_id.has_value()) {
     wallpaper_info_dict.SetStringKey(
-        WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
+        WallpaperPrefManager::kNewWallpaperAssetIdNodeName,
         base::NumberToString(info.asset_id.value()));
   }
   if (info.dedup_key.has_value()) {
     wallpaper_info_dict.SetStringKey(
-        WallpaperControllerImpl::kNewWallpaperDedupKeyNodeName,
+        WallpaperPrefManager::kNewWallpaperDedupKeyNodeName,
         info.dedup_key.value());
   }
   if (info.unit_id.has_value()) {
     wallpaper_info_dict.SetStringKey(
-        WallpaperControllerImpl::kNewWallpaperUnitIdNodeName,
+        WallpaperPrefManager::kNewWallpaperUnitIdNodeName,
         base::NumberToString(info.unit_id.value()));
   }
   base::Value online_wallpaper_variant_list(base::Value::Type::LIST);
   for (const auto& variant : info.variants) {
     base::Value online_wallpaper_variant_dict(base::Value::Type::DICTIONARY);
     online_wallpaper_variant_dict.SetStringKey(
-        WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
+        WallpaperPrefManager::kNewWallpaperAssetIdNodeName,
         base::NumberToString(variant.asset_id));
     online_wallpaper_variant_dict.SetStringKey(
-        WallpaperControllerImpl::kOnlineWallpaperUrlNodeName,
+        WallpaperPrefManager::kOnlineWallpaperUrlNodeName,
         variant.raw_url.spec());
     online_wallpaper_variant_dict.SetIntKey(
-        WallpaperControllerImpl::kOnlineWallpaperTypeNodeName,
+        WallpaperPrefManager::kOnlineWallpaperTypeNodeName,
         static_cast<int>(variant.type));
     online_wallpaper_variant_list.Append(
         std::move(online_wallpaper_variant_dict));
   }
   wallpaper_info_dict.SetKey(
-      WallpaperControllerImpl::kNewWallpaperVariantListNodeName,
+      WallpaperPrefManager::kNewWallpaperVariantListNodeName,
       std::move(online_wallpaper_variant_list));
   wallpaper_info_dict.SetStringKey(
-      WallpaperControllerImpl::kNewWallpaperCollectionIdNodeName,
+      WallpaperPrefManager::kNewWallpaperCollectionIdNodeName,
       info.collection_id);
   wallpaper_info_dict.SetStringKey(
-      WallpaperControllerImpl::kNewWallpaperDateNodeName,
+      WallpaperPrefManager::kNewWallpaperDateNodeName,
       base::NumberToString(info.date.ToInternalValue()));
   wallpaper_info_dict.SetStringKey(
-      WallpaperControllerImpl::kNewWallpaperLocationNodeName, info.location);
+      WallpaperPrefManager::kNewWallpaperLocationNodeName, info.location);
   wallpaper_info_dict.SetIntKey(
-      WallpaperControllerImpl::kNewWallpaperLayoutNodeName, info.layout);
-  wallpaper_info_dict.SetIntKey(
-      WallpaperControllerImpl::kNewWallpaperTypeNodeName,
-      static_cast<int>(info.type));
+      WallpaperPrefManager::kNewWallpaperLayoutNodeName, info.layout);
+  wallpaper_info_dict.SetIntKey(WallpaperPrefManager::kNewWallpaperTypeNodeName,
+                                static_cast<int>(info.type));
   return wallpaper_info_dict;
 }
 
@@ -3557,16 +3557,16 @@
   // DO NOT CHANGE as there are preferences like this in production.
   base::Value wallpaper_info_dict(base::Value::Type::DICTIONARY);
   wallpaper_info_dict.SetStringPath(
-      WallpaperControllerImpl::kNewWallpaperDateNodeName,
+      WallpaperPrefManager::kNewWallpaperDateNodeName,
       base::NumberToString(
           base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds()));
   wallpaper_info_dict.SetStringPath(
-      WallpaperControllerImpl::kNewWallpaperLocationNodeName, "location");
+      WallpaperPrefManager::kNewWallpaperLocationNodeName, "location");
   wallpaper_info_dict.SetIntPath(
-      WallpaperControllerImpl::kNewWallpaperLayoutNodeName,
+      WallpaperPrefManager::kNewWallpaperLayoutNodeName,
       WallpaperLayout::WALLPAPER_LAYOUT_CENTER);
   wallpaper_info_dict.SetIntPath(
-      WallpaperControllerImpl::kNewWallpaperTypeNodeName,
+      WallpaperPrefManager::kNewWallpaperTypeNodeName,
       static_cast<int>(WallpaperType::kOnline));
 
   {
diff --git a/ash/wallpaper/wallpaper_pref_manager.cc b/ash/wallpaper/wallpaper_pref_manager.cc
new file mode 100644
index 0000000..b1b8bcbb
--- /dev/null
+++ b/ash/wallpaper/wallpaper_pref_manager.cc
@@ -0,0 +1,477 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wallpaper/wallpaper_pref_manager.h"
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "ash/constants/ash_features.h"
+#include "ash/constants/ash_pref_names.h"
+#include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
+#include "ash/public/cpp/wallpaper/wallpaper_info.h"
+#include "ash/public/cpp/wallpaper/wallpaper_types.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+
+namespace {
+
+// Populates online wallpaper related info in |info|.
+void PopulateOnlineWallpaperInfo(WallpaperInfo* info,
+                                 const base::Value& info_dict) {
+  const std::string* asset_id_str = info_dict.FindStringPath(
+      WallpaperPrefManager::kNewWallpaperAssetIdNodeName);
+  const std::string* collection_id = info_dict.FindStringPath(
+      WallpaperPrefManager::kNewWallpaperCollectionIdNodeName);
+  const std::string* dedup_key = info_dict.FindStringPath(
+      WallpaperPrefManager::kNewWallpaperDedupKeyNodeName);
+  const std::string* unit_id_str = info_dict.FindStringPath(
+      WallpaperPrefManager::kNewWallpaperUnitIdNodeName);
+  const base::Value* variant_list = info_dict.FindListPath(
+      WallpaperPrefManager::kNewWallpaperVariantListNodeName);
+
+  info->collection_id = collection_id ? *collection_id : std::string();
+  info->dedup_key = dedup_key ? absl::make_optional(*dedup_key) : absl::nullopt;
+
+  if (asset_id_str) {
+    uint64_t asset_id;
+    if (base::StringToUint64(*asset_id_str, &asset_id))
+      info->asset_id = absl::make_optional(asset_id);
+  }
+  if (unit_id_str) {
+    uint64_t unit_id;
+    if (base::StringToUint64(*unit_id_str, &unit_id))
+      info->unit_id = absl::make_optional(unit_id);
+  }
+  if (variant_list) {
+    std::vector<OnlineWallpaperVariant> variants;
+    for (const auto& variant_info : variant_list->GetListDeprecated()) {
+      const std::string* variant_asset_id_str = variant_info.FindStringPath(
+          WallpaperPrefManager::kNewWallpaperAssetIdNodeName);
+      const std::string* url = variant_info.FindStringPath(
+          WallpaperPrefManager::kOnlineWallpaperUrlNodeName);
+      absl::optional<int> type = variant_info.FindIntPath(
+          WallpaperPrefManager::kOnlineWallpaperTypeNodeName);
+      if (variant_asset_id_str && url && type.has_value()) {
+        uint64_t variant_asset_id;
+        if (base::StringToUint64(*variant_asset_id_str, &variant_asset_id))
+          variants.emplace_back(
+              variant_asset_id, GURL(*url),
+              static_cast<backdrop::Image::ImageType>(type.value()));
+      }
+    }
+    info->variants = std::move(variants);
+  }
+}
+
+// Populates |info| with the data from |pref_name| sourced from |pref_service|.
+bool GetWallpaperInfo(const AccountId& account_id,
+                      const PrefService* const pref_service,
+                      const std::string& pref_name,
+                      WallpaperInfo* info) {
+  if (!pref_service)
+    return false;
+
+  const base::Value* users_dict = pref_service->GetDictionary(pref_name);
+  if (!users_dict)
+    return false;
+
+  const base::Value* info_dict =
+      users_dict->FindDictKey(account_id.GetUserEmail());
+  if (!info_dict)
+    return false;
+
+  // Use temporary variables to keep |info| untouched in the error case.
+  const std::string* location = info_dict->FindStringPath(
+      WallpaperPrefManager::kNewWallpaperLocationNodeName);
+  absl::optional<int> layout =
+      info_dict->FindIntPath(WallpaperPrefManager::kNewWallpaperLayoutNodeName);
+  absl::optional<int> type =
+      info_dict->FindIntPath(WallpaperPrefManager::kNewWallpaperTypeNodeName);
+  const std::string* date_string = info_dict->FindStringPath(
+      WallpaperPrefManager::kNewWallpaperDateNodeName);
+
+  if (!location || !layout || !type || !date_string)
+    return false;
+
+  if (type.value() >= static_cast<int>(WallpaperType::kCount))
+    return false;
+
+  WallpaperType wallpaper_type = static_cast<WallpaperType>(type.value());
+  if (!features::IsWallpaperGooglePhotosIntegrationEnabled() &&
+      (wallpaper_type == WallpaperType::kOnceGooglePhotos ||
+       wallpaper_type == WallpaperType::kDailyGooglePhotos)) {
+    return false;
+  }
+  info->type = wallpaper_type;
+
+  int64_t date_val;
+  if (!base::StringToInt64(*date_string, &date_val))
+    return false;
+
+  info->location = *location;
+  info->layout = static_cast<WallpaperLayout>(layout.value());
+  // TODO(skau): Switch to TimeFromValue
+  info->date =
+      base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(date_val));
+  PopulateOnlineWallpaperInfo(info, *info_dict);
+  return true;
+}
+
+// Populates |pref_name| with data from |info|. Returns false if the
+// pref_service was inaccessbile.
+bool SetWallpaperInfo(const AccountId& account_id,
+                      const WallpaperInfo& info,
+                      PrefService* const pref_service,
+                      const std::string& pref_name) {
+  if (!pref_service)
+    return false;
+
+  DictionaryPrefUpdate wallpaper_update(pref_service, pref_name);
+  base::Value wallpaper_info_dict(base::Value::Type::DICTIONARY);
+  if (info.asset_id.has_value()) {
+    wallpaper_info_dict.SetStringPath(
+        WallpaperPrefManager::kNewWallpaperAssetIdNodeName,
+        base::NumberToString(info.asset_id.value()));
+  }
+  if (info.unit_id.has_value()) {
+    wallpaper_info_dict.SetStringPath(
+        WallpaperPrefManager::kNewWallpaperUnitIdNodeName,
+        base::NumberToString(info.unit_id.value()));
+  }
+  base::Value online_wallpaper_variant_list(base::Value::Type::LIST);
+  for (const auto& variant : info.variants) {
+    base::Value online_wallpaper_variant_dict(base::Value::Type::DICTIONARY);
+    online_wallpaper_variant_dict.SetStringPath(
+        WallpaperPrefManager::kNewWallpaperAssetIdNodeName,
+        base::NumberToString(variant.asset_id));
+    online_wallpaper_variant_dict.SetStringPath(
+        WallpaperPrefManager::kOnlineWallpaperUrlNodeName,
+        variant.raw_url.spec());
+    online_wallpaper_variant_dict.SetIntPath(
+        WallpaperPrefManager::kOnlineWallpaperTypeNodeName,
+        static_cast<int>(variant.type));
+    online_wallpaper_variant_list.Append(
+        std::move(online_wallpaper_variant_dict));
+  }
+
+  wallpaper_info_dict.SetKey(
+      WallpaperPrefManager::kNewWallpaperVariantListNodeName,
+      std::move(online_wallpaper_variant_list));
+  wallpaper_info_dict.SetStringPath(
+      WallpaperPrefManager::kNewWallpaperCollectionIdNodeName,
+      info.collection_id);
+  // TODO(skau): Change time representation to TimeToValue.
+  wallpaper_info_dict.SetStringPath(
+      WallpaperPrefManager::kNewWallpaperDateNodeName,
+      base::NumberToString(
+          info.date.ToDeltaSinceWindowsEpoch().InMicroseconds()));
+  if (info.dedup_key) {
+    wallpaper_info_dict.SetStringPath(
+        WallpaperPrefManager::kNewWallpaperDedupKeyNodeName,
+        info.dedup_key.value());
+  }
+  wallpaper_info_dict.SetStringPath(
+      WallpaperPrefManager::kNewWallpaperLocationNodeName, info.location);
+  wallpaper_info_dict.SetIntPath(
+      WallpaperPrefManager::kNewWallpaperLayoutNodeName, info.layout);
+  wallpaper_info_dict.SetIntPath(
+      WallpaperPrefManager::kNewWallpaperTypeNodeName,
+      static_cast<int>(info.type));
+  wallpaper_update->SetKey(account_id.GetUserEmail(),
+                           std::move(wallpaper_info_dict));
+  return true;
+}
+
+// Deletes the entry in |pref_name| for |account_id| from |pref_service|.
+void RemoveWallpaperInfo(const AccountId& account_id,
+                         PrefService* const pref_service,
+                         const std::string& pref_name) {
+  if (!pref_service)
+    return;
+  DictionaryPrefUpdate prefs_wallpapers_info_update(pref_service, pref_name);
+  prefs_wallpapers_info_update->RemoveKey(account_id.GetUserEmail());
+}
+
+// Wrappers around SessionControllerImpl and UserManager to make this easier to
+// test. Also, the objects can't be provided at construction because they're
+// created after WallpaperControllerImpl.
+class WallpaperProfileHelperImpl : public WallpaperProfileHelper {
+ public:
+  WallpaperProfileHelperImpl() = default;
+  ~WallpaperProfileHelperImpl() override = default;
+
+  void SetClient(WallpaperControllerClient* client) override {
+    wallpaper_controller_client_ = client;
+  }
+
+  bool IsEphemeralUser(const AccountId& id) const override {
+    const UserSession* user_session =
+        Shell::Get()->session_controller()->GetUserSessionByAccountId(id);
+    if (!user_session)
+      return true;
+
+    return user_session->user_info.is_ephemeral;
+  }
+
+  PrefService* GetUserPrefServiceSyncable(const AccountId& id) override {
+    if (!features::IsWallpaperWebUIEnabled())
+      return nullptr;
+
+    if (!IsWallpaperSyncEnabled(id))
+      return nullptr;
+
+    return Shell::Get()->session_controller()->GetUserPrefServiceForUser(id);
+  }
+
+  AccountId GetActiveAccountId() const override {
+    const UserSession* const session =
+        Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+    DCHECK(session);
+    return session->user_info.account_id;
+  }
+
+  bool IsWallpaperSyncEnabled(const AccountId& id) const override {
+    DCHECK(wallpaper_controller_client_);
+    return wallpaper_controller_client_->IsWallpaperSyncEnabled(id);
+  }
+
+  bool IsActiveUserSessionStarted() const override {
+    return Shell::Get()->session_controller()->IsActiveUserSessionStarted();
+  }
+
+ private:
+  WallpaperControllerClient* wallpaper_controller_client_ = nullptr;
+};
+
+class WallpaperPrefManagerImpl : public WallpaperPrefManager {
+ public:
+  WallpaperPrefManagerImpl(
+      PrefService* local_state,
+      std::unique_ptr<WallpaperProfileHelper> profile_helper)
+      : local_state_(local_state), profile_helper_(std::move(profile_helper)) {
+    // local_state is null for tests under AshTestHelper
+    DCHECK(profile_helper_);
+  }
+
+  ~WallpaperPrefManagerImpl() override = default;
+
+  void SetClient(WallpaperControllerClient* client) override {
+    profile_helper_->SetClient(client);
+  }
+
+  void RemoveUserWallpaperInfo(const AccountId& account_id) override {
+    RemoveWallpaperInfo(account_id, local_state_, prefs::kUserWallpaperInfo);
+    RemoveWallpaperInfo(account_id,
+                        profile_helper_->GetUserPrefServiceSyncable(account_id),
+                        prefs::kSyncableWallpaperInfo);
+  }
+
+  void CacheProminentColors(const AccountId& account_id,
+                            const std::vector<SkColor>& colors) override {
+    WallpaperInfo old_info;
+    if (!GetLocalWallpaperInfo(account_id, &old_info)) {
+      return;
+    }
+
+    // TODO(crbug.com/787134): A blank key cannot be used as a key. This should
+    // be fixed (with a key that will not collide).
+    if (old_info.location.empty())
+      return;
+
+    DictionaryPrefUpdate wallpaper_colors_update(local_state_,
+                                                 prefs::kWallpaperColors);
+    base::Value wallpaper_colors(base::Value::Type::LIST);
+    for (SkColor color : colors)
+      wallpaper_colors.Append(static_cast<double>(color));
+    wallpaper_colors_update->SetKey(old_info.location,
+                                    std::move(wallpaper_colors));
+  }
+
+  void RemoveProminentColors(const AccountId& account_id) override {
+    WallpaperInfo old_info;
+    if (!GetLocalWallpaperInfo(account_id, &old_info)) {
+      return;
+    }
+
+    // Remove the color cache of the previous wallpaper if it exists.
+    DictionaryPrefUpdate wallpaper_colors_update(local_state_,
+                                                 prefs::kWallpaperColors);
+    wallpaper_colors_update->RemoveKey(old_info.location);
+  }
+
+  absl::optional<std::vector<SkColor>> GetCachedColors(
+      const AccountId& account_id) const override {
+    WallpaperInfo info;
+    if (!GetLocalWallpaperInfo(account_id, &info))
+      return absl::nullopt;
+
+    // TODO(crbug.com/787134): When we can handle blank keys, remove this.
+    if (info.location.empty())
+      return absl::nullopt;
+
+    const base::Value* prominent_colors =
+        local_state_->GetDictionary(prefs::kWallpaperColors)
+            ->FindListKey(info.location);
+    if (!prominent_colors)
+      return absl::nullopt;
+
+    absl::optional<std::vector<SkColor>> cached_colors_out;
+    cached_colors_out = std::vector<SkColor>();
+    cached_colors_out.value().reserve(
+        prominent_colors->GetListDeprecated().size());
+    for (const auto& value : prominent_colors->GetListDeprecated()) {
+      cached_colors_out.value().push_back(
+          static_cast<SkColor>(value.GetDouble()));
+    }
+    return cached_colors_out;
+  }
+
+  bool SetDailyGooglePhotosWallpaperIdCache(
+      const AccountId& account_id,
+      const WallpaperController::DailyGooglePhotosIdCache& ids) override {
+    if (!local_state_)
+      return false;
+    DictionaryPrefUpdate daily_google_photos_ids_update(
+        local_state_, prefs::kRecentDailyGooglePhotosWallpapers);
+    base::Value id_list(base::Value::Type::LIST);
+    for (auto id = ids.rbegin(); id != ids.rend(); id++) {
+      id_list.Append(base::NumberToString(*id));
+    }
+    daily_google_photos_ids_update->SetKey(account_id.GetUserEmail(),
+                                           std::move(id_list));
+    return true;
+  }
+
+  bool GetDailyGooglePhotosWallpaperIdCache(
+      const AccountId& account_id,
+      WallpaperController::DailyGooglePhotosIdCache& ids_out) const override {
+    if (!local_state_)
+      return false;
+
+    const base::Value::Dict* dict =
+        local_state_->GetDictionary(prefs::kRecentDailyGooglePhotosWallpapers)
+            ->GetIfDict();
+    if (!dict)
+      return false;
+
+    const base::Value::List* id_list =
+        dict->FindList(account_id.GetUserEmail());
+    if (!id_list)
+      return false;
+
+    for (auto& id_str : *id_list) {
+      uint32_t id;
+      if (base::StringToUint(id_str.GetString(), &id)) {
+        ids_out.Put(std::move(id));
+      }
+    }
+    return true;
+  }
+
+  bool GetLocalWallpaperInfo(const AccountId& account_id,
+                             WallpaperInfo* info) const override {
+    if (!local_state_)
+      return false;
+
+    return GetWallpaperInfo(account_id, local_state_, prefs::kUserWallpaperInfo,
+                            info);
+  }
+
+  bool SetLocalWallpaperInfo(const AccountId& account_id,
+                             const WallpaperInfo& info) override {
+    if (!local_state_)
+      return false;
+
+    return SetWallpaperInfo(account_id, info, local_state_,
+                            prefs::kUserWallpaperInfo);
+  }
+
+  bool GetSyncedWallpaperInfo(const AccountId& account_id,
+                              WallpaperInfo* info) const override {
+    PrefService* pref_service =
+        profile_helper_->GetUserPrefServiceSyncable(account_id);
+    if (!pref_service)
+      return false;
+
+    return GetWallpaperInfo(account_id, pref_service,
+                            prefs::kSyncableWallpaperInfo, info);
+  }
+
+  // Store |info| into the syncable pref service for |account_id|.
+  bool SetSyncedWallpaperInfo(const AccountId& account_id,
+                              const WallpaperInfo& info) override {
+    PrefService* pref_service =
+        profile_helper_->GetUserPrefServiceSyncable(account_id);
+    if (!pref_service)
+      return false;
+
+    return SetWallpaperInfo(account_id, info, pref_service,
+                            prefs::kSyncableWallpaperInfo);
+  }
+
+ private:
+  PrefService* local_state_ = nullptr;
+  std::unique_ptr<WallpaperProfileHelper> profile_helper_;
+};
+
+}  // namespace
+
+const char WallpaperPrefManager::kNewWallpaperAssetIdNodeName[] = "asset_id";
+const char WallpaperPrefManager::kNewWallpaperCollectionIdNodeName[] =
+    "collection_id";
+const char WallpaperPrefManager::kNewWallpaperDateNodeName[] = "date";
+const char WallpaperPrefManager::kNewWallpaperDedupKeyNodeName[] = "dedup_key";
+const char WallpaperPrefManager::kNewWallpaperLayoutNodeName[] = "layout";
+const char WallpaperPrefManager::kNewWallpaperLocationNodeName[] = "file";
+const char WallpaperPrefManager::kNewWallpaperTypeNodeName[] = "type";
+const char WallpaperPrefManager::kNewWallpaperUnitIdNodeName[] = "unit_id";
+const char WallpaperPrefManager::kNewWallpaperVariantListNodeName[] =
+    "variants";
+const char WallpaperPrefManager::kOnlineWallpaperTypeNodeName[] =
+    "online_image_type";
+const char WallpaperPrefManager::kOnlineWallpaperUrlNodeName[] = "url";
+
+// static
+std::unique_ptr<WallpaperPrefManager> WallpaperPrefManager::Create(
+    PrefService* local_state) {
+  std::unique_ptr<WallpaperProfileHelper> profile_helper =
+      std::make_unique<WallpaperProfileHelperImpl>();
+  return std::make_unique<WallpaperPrefManagerImpl>(local_state,
+                                                    std::move(profile_helper));
+}
+
+// static
+void WallpaperPrefManager::RegisterLocalStatePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(prefs::kUserWallpaperInfo);
+  registry->RegisterDictionaryPref(prefs::kWallpaperColors);
+  registry->RegisterDictionaryPref(prefs::kRecentDailyGooglePhotosWallpapers);
+}
+
+// static
+void WallpaperPrefManager::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  using user_prefs::PrefRegistrySyncable;
+
+  registry->RegisterDictionaryPref(prefs::kSyncableWallpaperInfo,
+                                   PrefRegistrySyncable::SYNCABLE_OS_PREF);
+}
+
+}  // namespace ash
diff --git a/ash/wallpaper/wallpaper_pref_manager.h b/ash/wallpaper/wallpaper_pref_manager.h
new file mode 100644
index 0000000..a5c4a450
--- /dev/null
+++ b/ash/wallpaper/wallpaper_pref_manager.h
@@ -0,0 +1,134 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WALLPAPER_WALLPAPER_PREF_MANAGER_H_
+#define ASH_WALLPAPER_WALLPAPER_PREF_MANAGER_H_
+
+#include <string>
+
+#include "ash/ash_export.h"
+#include "ash/public/cpp/session/session_observer.h"
+#include "ash/public/cpp/style/color_mode_observer.h"
+#include "ash/public/cpp/wallpaper/wallpaper_controller.h"
+#include "ash/public/cpp/wallpaper/wallpaper_info.h"
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/time/time.h"
+#include "base/timer/wall_clock_timer.h"
+#include "components/account_id/account_id.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace ash {
+
+class WallpaperControllerClient;
+
+// Interface that provides user profiles from an account id. Abstracts the
+// details of the PrefService from clients and makes testing easier.
+class WallpaperProfileHelper {
+ public:
+  virtual ~WallpaperProfileHelper() = default;
+
+  virtual void SetClient(WallpaperControllerClient* client) = 0;
+
+  // Returns true if the user's wallpaper is to be treated as ephemeral.
+  virtual bool IsEphemeralUser(const AccountId& id) const = 0;
+
+  // Returns the syncable pref service for the user with |id| if it's available.
+  // Otherwise, returns null.
+  virtual PrefService* GetUserPrefServiceSyncable(const AccountId& id) = 0;
+
+  // Returns the AccountId for the currently active account.
+  virtual AccountId GetActiveAccountId() const = 0;
+
+  // Returns true iff wallpaper sync is enabled for |id|.
+  virtual bool IsWallpaperSyncEnabled(const AccountId& id) const = 0;
+
+  // Returns true if at least one user is logged in.
+  virtual bool IsActiveUserSessionStarted() const = 0;
+};
+
+// Manages wallpaper preferences and tracks the currently configured wallpaper.
+class ASH_EXPORT WallpaperPrefManager
+    : public base::SupportsWeakPtr<WallpaperPrefManager>,
+      public ColorModeObserver,
+      public SessionObserver {
+ public:
+  // Names of nodes with wallpaper info in |kUserWallpaperInfo| dictionary.
+  static const char kNewWallpaperAssetIdNodeName[];
+  static const char kNewWallpaperCollectionIdNodeName[];
+  static const char kNewWallpaperDateNodeName[];
+  static const char kNewWallpaperDedupKeyNodeName[];
+  static const char kNewWallpaperLayoutNodeName[];
+  static const char kNewWallpaperLocationNodeName[];
+  static const char kNewWallpaperTypeNodeName[];
+  static const char kNewWallpaperUnitIdNodeName[];
+  static const char kNewWallpaperVariantListNodeName[];
+
+  // Names of nodes for the online wallpaper variant dictionary.
+  static const char kOnlineWallpaperTypeNodeName[];
+  static const char kOnlineWallpaperUrlNodeName[];
+
+  static std::unique_ptr<WallpaperPrefManager> Create(PrefService* local_state);
+
+  // Create a PrefManager where pref service retrieval can be modified through
+  // |profile_helper|.
+  static std::unique_ptr<WallpaperPrefManager> CreateForTesting(
+      PrefService* local_state,
+      std::unique_ptr<WallpaperProfileHelper> profile_helper);
+
+  WallpaperPrefManager(const WallpaperPrefManager&) = delete;
+
+  ~WallpaperPrefManager() override = default;
+
+  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  virtual void SetClient(WallpaperControllerClient* client) = 0;
+
+  // Remove the wallpaper entry for |account_id|.
+  virtual void RemoveUserWallpaperInfo(const AccountId& account_id) = 0;
+
+  virtual void CacheProminentColors(const AccountId& account_id,
+                                    const std::vector<SkColor>& colors) = 0;
+
+  virtual void RemoveProminentColors(const AccountId& account_id) = 0;
+
+  virtual absl::optional<std::vector<SkColor>> GetCachedColors(
+      const AccountId& account_id) const = 0;
+
+  virtual bool SetDailyGooglePhotosWallpaperIdCache(
+      const AccountId& account_id,
+      const WallpaperController::DailyGooglePhotosIdCache& ids) = 0;
+  virtual bool GetDailyGooglePhotosWallpaperIdCache(
+      const AccountId& account_id,
+      WallpaperController::DailyGooglePhotosIdCache& ids_out) const = 0;
+
+  // Get/Set the wallpaper info in local storage.
+  // TODO(crbug.com/1298586): Remove these functions from the interface when
+  // this can be abstracted from callers.
+  virtual bool GetLocalWallpaperInfo(const AccountId& account_id,
+                                     WallpaperInfo* info) const = 0;
+  virtual bool SetLocalWallpaperInfo(const AccountId& account_id,
+                                     const WallpaperInfo& info) = 0;
+
+  // Get/Set the wallpaper info from synced prefs.
+  // TODO(crbug.com/1298586): Remove these functions from the interface when
+  // this can be abstracted from callers.
+  virtual bool GetSyncedWallpaperInfo(const AccountId& account_id,
+                                      WallpaperInfo* info) const = 0;
+  virtual bool SetSyncedWallpaperInfo(const AccountId& account_id,
+                                      const WallpaperInfo& info) = 0;
+
+ protected:
+  WallpaperPrefManager() = default;
+};
+
+}  // namespace ash
+
+#endif  //  ASH_WALLPAPER_WALLPAPER_PREF_MANAGER_H_
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 08ccaf2..a70137fd 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -1221,6 +1221,20 @@
   }
 }
 
+void DesksController::MaybeCancelDeskRemoval() {
+  if (!temporary_removed_desk_)
+    return;
+
+  // `UndoDeskRemoval()` will take ownership of `temporary_removed_desk` so we
+  // need to get the toast id beforehand. It also needs to come before
+  // cancelling the toast as if animations are disabled, cancelling will call
+  // `MaybeCommitPendingDeskRemoval()` right away, which would delete
+  // `temporary_removed_desk`.
+  const std::string toast_id = temporary_removed_desk_->toast_id();
+  UndoDeskRemoval();
+  ToastManager::Get()->Cancel(toast_id);
+}
+
 void DesksController::OnWindowActivating(ActivationReason reason,
                                          aura::Window* gaining_active,
                                          aura::Window* losing_active) {
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 97c4fe3..a6cca6e 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -322,6 +322,10 @@
   // the names based on the desks order.
   void UpdateDesksDefaultNames();
 
+  // Cancels the desk removal toast and then triggers `UndoDeskRemoval()` if
+  // there is a desk removal in progress.
+  void MaybeCancelDeskRemoval();
+
   // ::wm::ActivationChangeObserver:
   void OnWindowActivating(ActivationReason reason,
                           aura::Window* gaining_active,
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index e04faed..7e21baad8 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -7486,6 +7486,34 @@
   EXPECT_FALSE(window2.is_valid());
 }
 
+TEST_F(DesksCloseAllTest, ShortcutUndoCloseAll) {
+  WindowHolder window1(CreateAppWindow());
+  WindowHolder window2(CreateAppWindow());
+  NewDesk();
+  auto* controller = DesksController::Get();
+  ASSERT_EQ(2u, controller->desks().size());
+  Desk* desk_1 = controller->desks()[0].get();
+  ASSERT_TRUE(desk_1->is_active());
+  ASSERT_TRUE(base::Contains(desk_1->windows(), window1.window()));
+  ASSERT_TRUE(base::Contains(desk_1->windows(), window2.window()));
+
+  EnterOverview();
+  auto* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
+  ASSERT_TRUE(overview_session);
+
+  // Closes the active desk.
+  ClickOnCloseAllButtonForDesk(0);
+  ASSERT_TRUE(DesksTestApi::DesksControllerCanUndoDeskRemoval());
+  ASSERT_EQ(1u, controller->desks().size());
+
+  // Tests that after hitting Ctrl + Z, the desk deleting is undone.
+  SendKey(ui::VKEY_Z, ui::EF_CONTROL_DOWN);
+  EXPECT_EQ(2u, controller->desks().size());
+  EXPECT_TRUE(window1.is_valid());
+  EXPECT_TRUE(window2.is_valid());
+}
+
 // Tests CloseAll on active desk will close app windows on it.
 TEST_F(DesksCloseAllTest, CloseActiveDeskCloseWindows) {
   WindowHolder window1(CreateAppWindow());
@@ -7503,7 +7531,7 @@
   // Closes the active desk.
   RemoveDesk(desk_1, DeskCloseType::kCloseAllWindowsAndWait);
 
-  // Waits for the toaster to disappear.
+  // Waits for the toast to disappear.
   WaitForMilliseconds(
       ToastData::kDefaultToastDuration.InMilliseconds() +
       DesksController::kCloseAllWindowCloseTimeout.InMilliseconds());
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 42a130b..ace576c 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1320,6 +1320,14 @@
         return;
       break;
     }
+    case ui::VKEY_Z: {
+      // Ctrl + Z undos a close all operation if the toast has not yet expired.
+      if (!is_control_down || !features::IsDesksCloseAllEnabled())
+        return;
+
+      DesksController::Get()->MaybeCancelDeskRemoval();
+      break;
+    }
     case ui::VKEY_RETURN: {
       if (!highlight_controller_->MaybeActivateHighlightedView())
         return;
diff --git a/base/allocator/partition_allocator/DEPS b/base/allocator/partition_allocator/DEPS
index d05f961..261b2de 100644
--- a/base/allocator/partition_allocator/DEPS
+++ b/base/allocator/partition_allocator/DEPS
@@ -6,7 +6,6 @@
 include_rules = [
     "+base/allocator/buildflags.h",
     "+base/base_export.h",
-    "+base/compiler_specific.h",
     "+base/dcheck_is_on.h",
     "+base/logging_buildflags.h",
     "+base/mac/foundation_util.h",
diff --git a/base/allocator/partition_allocator/partition_alloc_base/check.h b/base/allocator/partition_allocator/partition_alloc_base/check.h
index 50f46e71..101efd9 100644
--- a/base/allocator/partition_allocator/partition_alloc_base/check.h
+++ b/base/allocator/partition_allocator/partition_alloc_base/check.h
@@ -7,9 +7,9 @@
 
 #include <iosfwd>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
 
 // This header defines the CHECK, DCHECK, and DPCHECK macros.
@@ -85,7 +85,7 @@
   // Stream for adding optional details to the error message.
   std::ostream& stream();
 
-  NOMERGE ~CheckError();
+  PA_NOMERGE ~CheckError();
 
   CheckError(const CheckError& other) = delete;
   CheckError& operator=(const CheckError& other) = delete;
@@ -105,8 +105,9 @@
 // This is not calling BreakDebugger since this is called frequently, and
 // calling an out-of-line function instead of a noreturn inline macro prevents
 // compiler optimizations.
-#define PA_BASE_CHECK(condition) \
-  UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() : PA_EAT_CHECK_STREAM_PARAMS()
+#define PA_BASE_CHECK(condition)                   \
+  PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \
+                            : PA_EAT_CHECK_STREAM_PARAMS()
 
 // TODO(1151236): base/test/gtest_util.h uses CHECK_WILL_STREAM(). After
 // copying (or removing) gtest_util.h and removing gtest_uti.h from partition
@@ -118,7 +119,7 @@
       ::partition_alloc::internal::logging::CheckError::PCheck(__FILE__, \
                                                                __LINE__) \
           .stream(),                                                     \
-      UNLIKELY(!(condition)))
+      PA_UNLIKELY(!(condition)))
 
 #else
 
@@ -127,7 +128,7 @@
       ::partition_alloc::internal::logging::CheckError::Check( \
           __FILE__, __LINE__, #condition)                      \
           .stream(),                                           \
-      !ANALYZER_ASSUME_TRUE(condition))
+      !PA_ANALYZER_ASSUME_TRUE(condition))
 
 #define CHECK_WILL_STREAM() true
 
@@ -136,7 +137,7 @@
       ::partition_alloc::internal::logging::CheckError::PCheck( \
           __FILE__, __LINE__, #condition)                       \
           .stream(),                                            \
-      !ANALYZER_ASSUME_TRUE(condition))
+      !PA_ANALYZER_ASSUME_TRUE(condition))
 
 #endif
 
@@ -147,14 +148,14 @@
       ::partition_alloc::internal::logging::CheckError::DCheck( \
           __FILE__, __LINE__, #condition)                       \
           .stream(),                                            \
-      !ANALYZER_ASSUME_TRUE(condition))
+      !PA_ANALYZER_ASSUME_TRUE(condition))
 
 #define PA_BASE_DPCHECK(condition)                               \
   PA_LAZY_CHECK_STREAM(                                          \
       ::partition_alloc::internal::logging::CheckError::DPCheck( \
           __FILE__, __LINE__, #condition)                        \
           .stream(),                                             \
-      !ANALYZER_ASSUME_TRUE(condition))
+      !PA_ANALYZER_ASSUME_TRUE(condition))
 
 #else
 
diff --git a/base/allocator/partition_allocator/starscan/pcscan.cc b/base/allocator/partition_allocator/starscan/pcscan.cc
index 11e014c..dc05963 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan.cc
@@ -4,6 +4,7 @@
 
 #include "base/allocator/partition_allocator/starscan/pcscan.h"
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/starscan/pcscan_internal.h"
 
 namespace partition_alloc::internal {
@@ -107,6 +108,6 @@
   PCScanInternal::Instance().RegisterStatsReporter(reporter);
 }
 
-PCScan PCScan::instance_ CONSTINIT;
+PCScan PCScan::instance_ PA_CONSTINIT;
 
 }  // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/starscan/pcscan.h b/base/allocator/partition_allocator/starscan/pcscan.h
index 8c2ea9e..4da5c1f 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.h
+++ b/base/allocator/partition_allocator/starscan/pcscan.h
@@ -8,13 +8,13 @@
 #include <atomic>
 
 #include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_forward.h"
 #include "base/allocator/partition_allocator/partition_direct_map_extent.h"
 #include "base/allocator/partition_allocator/partition_page.h"
 #include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h"
 #include "base/allocator/partition_allocator/tagging.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 
 // Double free detection comes with expensive cmpxchg (with the loop around it).
 // We currently disable it to improve the runtime.
@@ -26,7 +26,8 @@
 
 namespace internal {
 
-[[noreturn]] BASE_EXPORT NOINLINE NOT_TAIL_CALLED void DoubleFreeAttempt();
+[[noreturn]] BASE_EXPORT PA_NOINLINE PA_NOT_TAIL_CALLED void
+DoubleFreeAttempt();
 
 // PCScan (Probabilistic Conservative Scanning) is the algorithm that eliminates
 // use-after-free bugs by verifying that there are no pointers in memory which
@@ -98,10 +99,10 @@
   // Registers a newly allocated super page for |root|.
   static void RegisterNewSuperPage(Root* root, uintptr_t super_page_base);
 
-  ALWAYS_INLINE static void MoveToQuarantine(void* object,
-                                             size_t usable_size,
-                                             uintptr_t slot_start,
-                                             size_t slot_size);
+  PA_ALWAYS_INLINE static void MoveToQuarantine(void* object,
+                                                size_t usable_size,
+                                                uintptr_t slot_start,
+                                                size_t slot_size);
 
   // Performs scanning unconditionally.
   static void PerformScan(InvocationMode invocation_mode);
@@ -117,7 +118,7 @@
   static void JoinScanIfNeeded();
 
   // Checks if there is a PCScan task currently in progress.
-  ALWAYS_INLINE static bool IsInProgress();
+  PA_ALWAYS_INLINE static bool IsInProgress();
 
   // Sets process name (used for histograms). |name| must be a string literal.
   static void SetProcessName(const char* name);
@@ -160,10 +161,10 @@
     kSweepingAndFinishing
   };
 
-  ALWAYS_INLINE static PCScan& Instance();
+  PA_ALWAYS_INLINE static PCScan& Instance();
 
-  ALWAYS_INLINE bool IsJoinable() const;
-  ALWAYS_INLINE void SetJoinableIfSafepointEnabled(bool);
+  PA_ALWAYS_INLINE bool IsJoinable() const;
+  PA_ALWAYS_INLINE void SetJoinableIfSafepointEnabled(bool);
 
   inline constexpr PCScan();
 
@@ -178,8 +179,8 @@
 
   size_t epoch() const { return scheduler_.epoch(); }
 
-  // CONSTINIT for fast access (avoiding static thread-safe initialization).
-  static PCScan instance_ CONSTINIT;
+  // PA_CONSTINIT for fast access (avoiding static thread-safe initialization).
+  static PCScan instance_ PA_CONSTINIT;
 
   PCScanScheduler scheduler_{};
   std::atomic<State> state_{State::kNotRunning};
@@ -191,7 +192,7 @@
 // To please Chromium's clang plugin.
 constexpr PCScan::PCScan() = default;
 
-ALWAYS_INLINE PCScan& PCScan::Instance() {
+PA_ALWAYS_INLINE PCScan& PCScan::Instance() {
   // The instance is declared as a static member, not static local. The reason
   // is that we want to use the require_constant_initialization attribute to
   // avoid double-checked-locking which would otherwise have been introduced
@@ -200,17 +201,17 @@
   return instance_;
 }
 
-ALWAYS_INLINE bool PCScan::IsInProgress() {
+PA_ALWAYS_INLINE bool PCScan::IsInProgress() {
   const PCScan& instance = Instance();
   return instance.state_.load(std::memory_order_relaxed) != State::kNotRunning;
 }
 
-ALWAYS_INLINE bool PCScan::IsJoinable() const {
+PA_ALWAYS_INLINE bool PCScan::IsJoinable() const {
   // This has acquire semantics since a mutator relies on the task being set up.
   return is_joinable_.load(std::memory_order_acquire);
 }
 
-ALWAYS_INLINE void PCScan::SetJoinableIfSafepointEnabled(bool value) {
+PA_ALWAYS_INLINE void PCScan::SetJoinableIfSafepointEnabled(bool value) {
   if (!is_safepoint_enabled_) {
     PA_DCHECK(!is_joinable_.load(std::memory_order_relaxed));
     return;
@@ -220,21 +221,21 @@
   is_joinable_.store(value, std::memory_order_release);
 }
 
-ALWAYS_INLINE void PCScan::EnableSafepoints() {
+PA_ALWAYS_INLINE void PCScan::EnableSafepoints() {
   PCScan& instance = Instance();
   instance.is_safepoint_enabled_ = true;
 }
 
-ALWAYS_INLINE void PCScan::JoinScanIfNeeded() {
+PA_ALWAYS_INLINE void PCScan::JoinScanIfNeeded() {
   PCScan& instance = Instance();
-  if (UNLIKELY(instance.IsJoinable()))
+  if (PA_UNLIKELY(instance.IsJoinable()))
     instance.JoinScan();
 }
 
-ALWAYS_INLINE void PCScan::MoveToQuarantine(void* object,
-                                            size_t usable_size,
-                                            uintptr_t slot_start,
-                                            size_t slot_size) {
+PA_ALWAYS_INLINE void PCScan::MoveToQuarantine(void* object,
+                                               size_t usable_size,
+                                               uintptr_t slot_start,
+                                               size_t slot_size) {
   PCScan& instance = Instance();
   if (instance.clear_type_ == ClearType::kEager) {
     // We need to distinguish between usable_size and slot_size in this context:
@@ -256,14 +257,14 @@
   [[maybe_unused]] const bool succeeded =
       state_bitmap->Quarantine(unmasked_slot_start, instance.epoch());
 #if PA_STARSCAN_EAGER_DOUBLE_FREE_DETECTION_ENABLED
-  if (UNLIKELY(!succeeded))
+  if (PA_UNLIKELY(!succeeded))
     DoubleFreeAttempt();
 #else
   // The compiler is able to optimize cmpxchg to a lock-prefixed and.
 #endif
 
   const bool is_limit_reached = instance.scheduler_.AccountFreed(slot_size);
-  if (UNLIKELY(is_limit_reached)) {
+  if (PA_UNLIKELY(is_limit_reached)) {
     // Perform a quick check if another scan is already in progress.
     if (instance.IsInProgress())
       return;
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index 70ba0479..0cabd193 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -25,6 +25,7 @@
 #include "base/allocator/partition_allocator/partition_address_space.h"
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/cpu.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
@@ -48,7 +49,6 @@
 #include "base/allocator/partition_allocator/starscan/stats_reporter.h"
 #include "base/allocator/partition_allocator/tagging.h"
 #include "base/allocator/partition_allocator/thread_cache.h"
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 // TODO(bikineev): Temporarily disable inlining in *Scan to get clearer
@@ -56,14 +56,14 @@
 #define PA_STARSCAN_NOINLINE_SCAN_FUNCTIONS
 
 #if defined(PA_STARSCAN_NOINLINE_SCAN_FUNCTIONS)
-#define PA_SCAN_INLINE NOINLINE
+#define PA_SCAN_INLINE PA_NOINLINE
 #else
-#define PA_SCAN_INLINE ALWAYS_INLINE
+#define PA_SCAN_INLINE PA_ALWAYS_INLINE
 #endif
 
 namespace partition_alloc::internal {
 
-[[noreturn]] NOINLINE NOT_TAIL_CALLED void DoubleFreeAttempt() {
+[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void DoubleFreeAttempt() {
   PA_NO_CODE_FOLDING();
   PA_IMMEDIATE_CRASH();
 }
@@ -134,24 +134,24 @@
 class QuarantineCardTable final {
  public:
   // Avoid the load of the base of the regular pool.
-  ALWAYS_INLINE static QuarantineCardTable& GetFrom(uintptr_t address) {
+  PA_ALWAYS_INLINE static QuarantineCardTable& GetFrom(uintptr_t address) {
     PA_SCAN_DCHECK(IsManagedByPartitionAllocRegularPool(address));
     return *reinterpret_cast<QuarantineCardTable*>(
         address & PartitionAddressSpace::RegularPoolBaseMask());
   }
 
-  ALWAYS_INLINE void Quarantine(uintptr_t begin, size_t size) {
+  PA_ALWAYS_INLINE void Quarantine(uintptr_t begin, size_t size) {
     return SetImpl(begin, size, true);
   }
 
-  ALWAYS_INLINE void Unquarantine(uintptr_t begin, size_t size) {
+  PA_ALWAYS_INLINE void Unquarantine(uintptr_t begin, size_t size) {
     return SetImpl(begin, size, false);
   }
 
   // Returns whether the card to which |address| points to contains quarantined
   // slots. May return false positives for but should never return false
   // negatives, as otherwise this breaks security.
-  ALWAYS_INLINE bool IsQuarantined(uintptr_t address) const {
+  PA_ALWAYS_INLINE bool IsQuarantined(uintptr_t address) const {
     address = ::partition_alloc::internal::UnmaskPtr(address);
     const size_t byte = Byte(address);
     PA_SCAN_DCHECK(byte < bytes_.size());
@@ -164,12 +164,12 @@
 
   QuarantineCardTable() = default;
 
-  ALWAYS_INLINE static size_t Byte(uintptr_t address) {
+  PA_ALWAYS_INLINE static size_t Byte(uintptr_t address) {
     return (address & ~PartitionAddressSpace::RegularPoolBaseMask()) /
            kCardSize;
   }
 
-  ALWAYS_INLINE void SetImpl(uintptr_t begin, size_t size, bool value) {
+  PA_ALWAYS_INLINE void SetImpl(uintptr_t begin, size_t size, bool value) {
     const size_t byte = Byte(begin);
     const size_t need_bytes = (size + (kCardSize - 1)) / kCardSize;
     PA_SCAN_DCHECK(bytes_.size() >= byte + need_bytes);
@@ -198,7 +198,7 @@
                        MetadataAllocator<std::pair<const K, V>>>;
 
 struct GetSlotStartResult final {
-  ALWAYS_INLINE bool is_found() const {
+  PA_ALWAYS_INLINE bool is_found() const {
     PA_SCAN_DCHECK(!slot_start || slot_size);
     return slot_start;
   }
@@ -435,9 +435,10 @@
       super_page, nonempty_slot_spans, [this, &current](SlotSpan* slot_span) {
         const uintptr_t payload_begin = SlotSpan::ToSlotSpanStart(slot_span);
         // For single-slot slot-spans, scan only utilized slot part.
-        const size_t provisioned_size = UNLIKELY(slot_span->CanStoreRawSize())
-                                            ? slot_span->GetRawSize()
-                                            : slot_span->GetProvisionedSize();
+        const size_t provisioned_size =
+            PA_UNLIKELY(slot_span->CanStoreRawSize())
+                ? slot_span->GetRawSize()
+                : slot_span->GetProvisionedSize();
         // Free & decommitted slot spans are skipped.
         PA_SCAN_DCHECK(provisioned_size > 0);
         const uintptr_t payload_end = payload_begin + provisioned_size;
@@ -624,7 +625,8 @@
 #if defined(PA_HAS_64_BITS_POINTERS)
 #if PA_STARSCAN_USE_CARD_TABLE
   // Check if |maybe_ptr| points to a quarantined card.
-  if (LIKELY(!QuarantineCardTable::GetFrom(maybe_ptr).IsQuarantined(maybe_ptr)))
+  if (PA_LIKELY(
+          !QuarantineCardTable::GetFrom(maybe_ptr).IsQuarantined(maybe_ptr)))
     return nullptr;
 #else
   // Without the card table, use the reservation offset table to check if
@@ -635,12 +637,12 @@
   // ReservationOffsetPointer().
   const uintptr_t offset =
       maybe_ptr & ~PartitionAddressSpace::RegularPoolBaseMask();
-  if (LIKELY(*ReservationOffsetPointer(kRegularPoolHandle, offset) !=
-             kOffsetTagNormalBuckets))
+  if (PA_LIKELY(*ReservationOffsetPointer(kRegularPoolHandle, offset) !=
+                kOffsetTagNormalBuckets))
     return nullptr;
 #endif
 #else   // defined(PA_HAS_64_BITS_POINTERS)
-  if (LIKELY(!IsManagedByPartitionAllocRegularPool(maybe_ptr)))
+  if (PA_LIKELY(!IsManagedByPartitionAllocRegularPool(maybe_ptr)))
     return nullptr;
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
 
@@ -681,7 +683,7 @@
   // yet. This can happen as arbitrary pointers may point into a super-page
   // during its set up. Make sure to check |root| is not null before
   // dereferencing it.
-  if (UNLIKELY(!root || !root->IsQuarantineEnabled()))
+  if (PA_UNLIKELY(!root || !root->IsQuarantineEnabled()))
     return 0;
 #endif
 
@@ -692,20 +694,21 @@
     return 0;
 
   const uintptr_t slot_start = slot_start_result.slot_start;
-  if (LIKELY(!state_map->IsQuarantined(slot_start)))
+  if (PA_LIKELY(!state_map->IsQuarantined(slot_start)))
     return 0;
 
   PA_SCAN_DCHECK((maybe_ptr & kSuperPageBaseMask) ==
                  (slot_start & kSuperPageBaseMask));
 
-  if (UNLIKELY(immediatelly_free_slots_))
+  if (PA_UNLIKELY(immediatelly_free_slots_))
     return 0;
 
   // Now we are certain that |maybe_ptr| is a dangling pointer. Mark it again in
   // the mutator bitmap and clear from the scanner bitmap. Note that since
   // PCScan has exclusive access to the scanner bitmap, we can avoid atomic rmw
   // operation for it.
-  if (LIKELY(state_map->MarkQuarantinedAsReachable(slot_start, pcscan_epoch_)))
+  if (PA_LIKELY(
+          state_map->MarkQuarantinedAsReachable(slot_start, pcscan_epoch_)))
     return slot_start_result.slot_size;
 
   return 0;
@@ -773,10 +776,10 @@
 
  private:
 #if defined(PA_HAS_64_BITS_POINTERS)
-  ALWAYS_INLINE static uintptr_t CageBase() {
+  PA_ALWAYS_INLINE static uintptr_t CageBase() {
     return PartitionAddressSpace::RegularPoolBase();
   }
-  ALWAYS_INLINE static uintptr_t CageMask() {
+  PA_ALWAYS_INLINE static uintptr_t CageMask() {
     return PartitionAddressSpace::RegularPoolBaseMask();
   }
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
@@ -832,7 +835,7 @@
   // Check if the stack top was registered. It may happen that it's not if the
   // current allocation happens from pthread trampolines.
   void* stack_top = pcscan.GetCurrentThreadStackTop();
-  if (UNLIKELY(!stack_top))
+  if (PA_UNLIKELY(!stack_top))
     return;
 
   Stack stack_scanner(stack_top);
@@ -904,8 +907,8 @@
           const uintptr_t end =
               begin + scan_area.size_in_words * sizeof(uintptr_t);
 
-          if (UNLIKELY(scan_area.slot_size_in_words >=
-                       kLargeScanAreaThresholdInWords)) {
+          if (PA_UNLIKELY(scan_area.slot_size_in_words >=
+                          kLargeScanAreaThresholdInWords)) {
             ScanLargeArea(pcscan, scan_loop, begin, end,
                           scan_area.slot_size_in_words * sizeof(uintptr_t));
           } else {
@@ -973,7 +976,7 @@
   bitmap->IterateQuarantined(epoch, [root, &stat](uintptr_t slot_start,
                                                   bool is_marked) {
     auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start);
-    if (LIKELY(!is_marked)) {
+    if (PA_LIKELY(!is_marked)) {
       stat.swept_bytes += FreeAndUnmarkInCardTable(root, slot_span, slot_start);
       return;
     }
@@ -1068,7 +1071,7 @@
         SweepSuperPageWithBatchedFree(root, super_page, pcscan_epoch_, stat);
         (void)should_discard;
 #else
-        if (UNLIKELY(should_discard && !root->flags.allow_cookie))
+        if (PA_UNLIKELY(should_discard && !root->flags.allow_cookie))
           SweepSuperPageAndDiscardMarkedQuarantine(root, super_page,
                                                    pcscan_epoch_, stat);
         else
@@ -1342,8 +1345,8 @@
   auto task = base::MakeRefCounted<PCScanTask>(frontend, last_quarantine_size);
   PCScanInternal::Instance().SetCurrentPCScanTask(task);
 
-  if (UNLIKELY(invocation_mode ==
-               PCScan::InvocationMode::kScheduleOnlyForTesting)) {
+  if (PA_UNLIKELY(invocation_mode ==
+                  PCScan::InvocationMode::kScheduleOnlyForTesting)) {
     // Immediately change the state to enable safepoint testing.
     frontend.state_.store(PCScan::State::kScanning, std::memory_order_release);
     frontend.SetJoinableIfSafepointEnabled(true);
@@ -1351,7 +1354,7 @@
   }
 
   // Post PCScan task.
-  if (LIKELY(invocation_mode == PCScan::InvocationMode::kNonBlocking)) {
+  if (PA_LIKELY(invocation_mode == PCScan::InvocationMode::kNonBlocking)) {
     PCScan::PCScanThread::Instance().PostTask(std::move(task));
   } else {
     PA_SCAN_DCHECK(PCScan::InvocationMode::kBlocking == invocation_mode ||
diff --git a/base/allocator/partition_allocator/starscan/pcscan_scheduling.cc b/base/allocator/partition_allocator/starscan/pcscan_scheduling.cc
index a27efdb..99a43378 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_scheduling.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_scheduling.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <atomic>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/time/time.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_hooks.h"
@@ -106,13 +107,13 @@
 
       // 2. Unlikely case: If also above hard limit, start scan right away. This
       // ignores explicit PCScan disabling.
-      if (UNLIKELY(data.current_size.load(std::memory_order_relaxed) >
-                   data.size_limit.load(std::memory_order_relaxed))) {
+      if (PA_UNLIKELY(data.current_size.load(std::memory_order_relaxed) >
+                      data.size_limit.load(std::memory_order_relaxed))) {
         return true;
       }
 
       // 3. Check if PCScan was explicitly disabled.
-      if (UNLIKELY(!is_scheduling_enabled())) {
+      if (PA_UNLIKELY(!is_scheduling_enabled())) {
         return false;
       }
 
diff --git a/base/allocator/partition_allocator/starscan/pcscan_scheduling.h b/base/allocator/partition_allocator/starscan/pcscan_scheduling.h
index 919cd81..34d9ed9 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_scheduling.h
+++ b/base/allocator/partition_allocator/starscan/pcscan_scheduling.h
@@ -8,11 +8,11 @@
 #include <atomic>
 #include <cstdint>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/time/time.h"
 #include "base/allocator/partition_allocator/partition_lock.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 
 namespace partition_alloc::internal {
 
@@ -148,7 +148,7 @@
 
   // Account freed `bytes`. Returns true if scan should be triggered
   // immediately, and false otherwise.
-  ALWAYS_INLINE bool AccountFreed(size_t bytes);
+  PA_ALWAYS_INLINE bool AccountFreed(size_t bytes);
 
   size_t epoch() const {
     return quarantine_data_.epoch.load(std::memory_order_relaxed);
diff --git a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
index f73721e..8354882 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/allocator/partition_allocator/starscan/pcscan.h"
 
 #include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/cpu.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/logging.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
@@ -643,19 +644,19 @@
   dangling_reference = nullptr;
 
   // Create and set dangling reference in the global.
-  [this]() NOINLINE {
+  [this]() PA_NOINLINE {
     auto* value = ValueList::Create(root(), nullptr);
     ValueList::Destroy(root(), value);
     dangling_reference = value;
   }();
 
-  [this]() NOINLINE {
+  [this]() PA_NOINLINE {
     // Register the top of the stack to be the current pointer.
     PCScan::NotifyThreadCreated(GetStackPointer());
-    [this]() NOINLINE {
+    [this]() PA_NOINLINE {
       // This writes the pointer to the stack.
       [[maybe_unused]] auto* volatile stack_ref = dangling_reference;
-      [this]() NOINLINE {
+      [this]() PA_NOINLINE {
         // Schedule PCScan but don't scan.
         SchedulePCScan();
         // Enter safepoint and scan from mutator. This will scan the stack.
diff --git a/base/allocator/partition_allocator/starscan/raceful_worklist.h b/base/allocator/partition_allocator/starscan/raceful_worklist.h
index 80be7f0f..1fee777 100644
--- a/base/allocator/partition_allocator/starscan/raceful_worklist.h
+++ b/base/allocator/partition_allocator/starscan/raceful_worklist.h
@@ -9,10 +9,10 @@
 #include <atomic>
 #include <vector>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/starscan/metadata_allocator.h"
-#include "base/compiler_specific.h"
 
 namespace partition_alloc::internal {
 
@@ -124,7 +124,7 @@
 
   // Finally, racefully visit items that were scanned by some other thread.
   for (auto it : to_revisit) {
-    if (LIKELY(it->is_visited.load(std::memory_order_relaxed)))
+    if (PA_LIKELY(it->is_visited.load(std::memory_order_relaxed)))
       continue;
     // Don't bail out here if the item is being visited by another thread.
     // This is helpful to guarantee forward progress if the other thread
diff --git a/base/allocator/partition_allocator/starscan/scan_loop.h b/base/allocator/partition_allocator/starscan/scan_loop.h
index 229a67a..1e03e717 100644
--- a/base/allocator/partition_allocator/starscan/scan_loop.h
+++ b/base/allocator/partition_allocator/starscan/scan_loop.h
@@ -8,10 +8,10 @@
 #include <cstddef>
 #include <cstdint>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/starscan/starscan_fwd.h"
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 #if defined(ARCH_CPU_X86_64)
@@ -103,7 +103,7 @@
     // Keep it MTE-untagged. See DisableMTEScope for details.
     const uintptr_t maybe_ptr = *reinterpret_cast<uintptr_t*>(begin);
 #if defined(PA_HAS_64_BITS_POINTERS)
-    if (LIKELY((maybe_ptr & mask) != base))
+    if (PA_LIKELY((maybe_ptr & mask) != base))
       continue;
 #else
     if (!maybe_ptr)
@@ -136,7 +136,7 @@
     const __m256i vand = _mm256_and_si256(maybe_ptrs, cage_mask);
     const __m256i vcmp = _mm256_cmpeq_epi64(vand, vbase);
     const int mask = _mm256_movemask_pd(_mm256_castsi256_pd(vcmp));
-    if (LIKELY(!mask))
+    if (PA_LIKELY(!mask))
       continue;
     // It's important to extract pointers from the already loaded vector.
     // Otherwise, new loads can break in-cage assumption checked above.
@@ -172,7 +172,7 @@
     const __m128i vand = _mm_and_si128(maybe_ptrs, cage_mask);
     const __m128i vcmp = _mm_cmpeq_epi64(vand, vbase);
     const int mask = _mm_movemask_pd(_mm_castsi128_pd(vcmp));
-    if (LIKELY(!mask))
+    if (PA_LIKELY(!mask))
       continue;
     // It's important to extract pointers from the already loaded vector.
     // Otherwise, new loads can break in-cage assumption checked above.
@@ -208,7 +208,7 @@
     const uint64x2_t vand = vandq_u64(maybe_ptrs, cage_mask);
     const uint64x2_t vcmp = vceqq_u64(vand, vbase);
     const uint32_t max = vmaxvq_u32(vreinterpretq_u32_u64(vcmp));
-    if (LIKELY(!max))
+    if (PA_LIKELY(!max))
       continue;
     // It's important to extract pointers from the already loaded vector.
     // Otherwise, new loads can break in-cage assumption checked above.
diff --git a/base/allocator/partition_allocator/starscan/stack/stack.cc b/base/allocator/partition_allocator/starscan/stack/stack.cc
index eff96ae3..b0f1894 100644
--- a/base/allocator/partition_allocator/starscan/stack/stack.cc
+++ b/base/allocator/partition_allocator/starscan/stack/stack.cc
@@ -7,8 +7,8 @@
 #include <cstdint>
 #include <limits>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -87,7 +87,7 @@
   PA_DCHECK(stack_top);
 }
 
-NOINLINE uintptr_t* GetStackPointer() {
+PA_NOINLINE uintptr_t* GetStackPointer() {
   return reinterpret_cast<uintptr_t*>(__builtin_frame_address(0));
 }
 
@@ -116,7 +116,7 @@
 // should never be inlined to ensure that a possible redzone cannot contain
 // any data that needs to be scanned.
 // No ASAN support as method accesses redzones while walking the stack.
-[[maybe_unused]] NOINLINE NO_SANITIZE("address") void IteratePointersImpl(
+[[maybe_unused]] PA_NOINLINE PA_NO_SANITIZE("address") void IteratePointersImpl(
     const Stack* stack,
     StackVisitor* visitor,
     uintptr_t* stack_ptr) {
diff --git a/base/allocator/partition_allocator/starscan/stack/stack.h b/base/allocator/partition_allocator/starscan/stack/stack.h
index 6b27223..b4de003 100644
--- a/base/allocator/partition_allocator/starscan/stack/stack.h
+++ b/base/allocator/partition_allocator/starscan/stack/stack.h
@@ -7,14 +7,14 @@
 
 #include <cstdint>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 
 namespace partition_alloc::internal {
 
 // Returns the current stack pointer.
 // TODO(bikineev,1202644): Remove this once base/stack_util.h lands.
-BASE_EXPORT NOINLINE uintptr_t* GetStackPointer();
+BASE_EXPORT PA_NOINLINE uintptr_t* GetStackPointer();
 // Returns the top of the stack using system API.
 BASE_EXPORT void* GetStackTop();
 
diff --git a/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc b/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc
index ca1fbb6..345271f 100644
--- a/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc
+++ b/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <ostream>
 
-#include "base/compiler_specific.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -91,16 +91,16 @@
 // must not actually be materialized.
 //
 // Parameter positiosn are explicit to test various calling conventions.
-NOINLINE void* RecursivelyPassOnParameterImpl(void* p1,
-                                              void* p2,
-                                              void* p3,
-                                              void* p4,
-                                              void* p5,
-                                              void* p6,
-                                              void* p7,
-                                              void* p8,
-                                              Stack* stack,
-                                              StackVisitor* visitor) {
+PA_NOINLINE void* RecursivelyPassOnParameterImpl(void* p1,
+                                                 void* p2,
+                                                 void* p3,
+                                                 void* p4,
+                                                 void* p5,
+                                                 void* p6,
+                                                 void* p7,
+                                                 void* p8,
+                                                 Stack* stack,
+                                                 StackVisitor* visitor) {
   if (p1) {
     return RecursivelyPassOnParameterImpl(nullptr, p1, nullptr, nullptr,
                                           nullptr, nullptr, nullptr, nullptr,
@@ -136,10 +136,10 @@
   return nullptr;
 }
 
-NOINLINE void* RecursivelyPassOnParameter(size_t num,
-                                          void* parameter,
-                                          Stack* stack,
-                                          StackVisitor* visitor) {
+PA_NOINLINE void* RecursivelyPassOnParameter(size_t num,
+                                             void* parameter,
+                                             Stack* stack,
+                                             StackVisitor* visitor) {
   switch (num) {
     case 0:
       stack->IteratePointers(visitor);
@@ -297,7 +297,7 @@
 // (Ignoring implementation-dependent dirty registers/stack.)
 #define KEEP_ALIVE_FROM_CALLEE_SAVED(reg)                                \
   local_scanner->Reset();                                                \
-  [local_stack, local_scanner]() NOINLINE {                              \
+  [local_stack, local_scanner]() PA_NOINLINE {                           \
     asm volatile("   mov %0, %%" reg                                     \
                  "\n mov %1, %%rdi"                                      \
                  "\n mov %2, %%rsi"                                      \
diff --git a/base/allocator/partition_allocator/starscan/state_bitmap.h b/base/allocator/partition_allocator/starscan/state_bitmap.h
index 193a149..6c7ef29 100644
--- a/base/allocator/partition_allocator/starscan/state_bitmap.h
+++ b/base/allocator/partition_allocator/starscan/state_bitmap.h
@@ -16,8 +16,8 @@
 #include <utility>
 
 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
-#include "base/compiler_specific.h"
 
 namespace partition_alloc::internal {
 
@@ -90,24 +90,25 @@
   inline StateBitmap();
 
   // Sets the bits corresponding to |address| as allocated.
-  ALWAYS_INLINE void Allocate(uintptr_t address);
+  PA_ALWAYS_INLINE void Allocate(uintptr_t address);
 
   // Sets the bits corresponding to |address| as quarantined. Must be called
   // only once, in which case returns |true|. Otherwise, if the object was
   // already quarantined or freed before, returns |false|.
-  ALWAYS_INLINE bool Quarantine(uintptr_t address, Epoch epoch);
+  PA_ALWAYS_INLINE bool Quarantine(uintptr_t address, Epoch epoch);
 
   // Marks ("promotes") quarantined object. Returns |true| on success, otherwise
   // |false| if the object was marked before.
-  ALWAYS_INLINE bool MarkQuarantinedAsReachable(uintptr_t address, Epoch epoch);
+  PA_ALWAYS_INLINE bool MarkQuarantinedAsReachable(uintptr_t address,
+                                                   Epoch epoch);
 
   // Sets the bits corresponding to |address| as freed.
-  ALWAYS_INLINE void Free(uintptr_t address);
+  PA_ALWAYS_INLINE void Free(uintptr_t address);
 
   // Getters that check object state.
-  ALWAYS_INLINE bool IsAllocated(uintptr_t address) const;
-  ALWAYS_INLINE bool IsQuarantined(uintptr_t address) const;
-  ALWAYS_INLINE bool IsFreed(uintptr_t address) const;
+  PA_ALWAYS_INLINE bool IsAllocated(uintptr_t address) const;
+  PA_ALWAYS_INLINE bool IsQuarantined(uintptr_t address) const;
+  PA_ALWAYS_INLINE bool IsFreed(uintptr_t address) const;
 
   // Iterate objects depending on their state.
   //
@@ -145,43 +146,43 @@
     return reinterpret_cast<const std::atomic<CellType>&>(bitmap_[cell_index]);
   }
 
-  ALWAYS_INLINE unsigned GetBits(uintptr_t address) const;
+  PA_ALWAYS_INLINE unsigned GetBits(uintptr_t address) const;
 
   struct FilterQuarantine {
-    ALWAYS_INLINE bool operator()(CellType cell) const;
+    PA_ALWAYS_INLINE bool operator()(CellType cell) const;
     const size_t epoch;
   };
 
   struct FilterUnmarkedQuarantine {
-    ALWAYS_INLINE bool operator()(CellType cell) const;
+    PA_ALWAYS_INLINE bool operator()(CellType cell) const;
     const size_t epoch;
   };
 
   struct FilterAllocated {
-    ALWAYS_INLINE bool operator()(CellType cell) const;
+    PA_ALWAYS_INLINE bool operator()(CellType cell) const;
     const size_t epoch;
   };
 
   // Simply calls the callback.
   struct SimpleCallbackForwarder {
-    ALWAYS_INLINE explicit SimpleCallbackForwarder(size_t epoch) {}
+    PA_ALWAYS_INLINE explicit SimpleCallbackForwarder(size_t epoch) {}
 
     template <typename Callback>
-    ALWAYS_INLINE void operator()(Callback,
-                                  uintptr_t pointer,
-                                  CellType bits) const;
+    PA_ALWAYS_INLINE void operator()(Callback,
+                                     uintptr_t pointer,
+                                     CellType bits) const;
   };
 
   // Calls the callback and passes a bool argument, indicating whether a
   // quarantine object is marked or not.
   struct QuarantineCallbackForwarder {
-    ALWAYS_INLINE explicit QuarantineCallbackForwarder(size_t epoch)
+    PA_ALWAYS_INLINE explicit QuarantineCallbackForwarder(size_t epoch)
         : is_unmarked{epoch} {}
 
     template <typename Callback>
-    ALWAYS_INLINE void operator()(Callback,
-                                  uintptr_t pointer,
-                                  CellType bits) const;
+    PA_ALWAYS_INLINE void operator()(Callback,
+                                     uintptr_t pointer,
+                                     CellType bits) const;
     FilterUnmarkedQuarantine is_unmarked;
   };
 
@@ -191,8 +192,8 @@
             bool Clear>
   inline void IterateImpl(size_t epoch, Callback);
 
-  ALWAYS_INLINE CellType LoadCell(size_t cell_index) const;
-  ALWAYS_INLINE static constexpr std::pair<size_t, size_t>
+  PA_ALWAYS_INLINE CellType LoadCell(size_t cell_index) const;
+  PA_ALWAYS_INLINE static constexpr std::pair<size_t, size_t>
       AllocationIndexAndBit(uintptr_t);
 
   std::array<CellType, kBitmapSize> bitmap_;
@@ -205,7 +206,7 @@
     StateBitmap() = default;
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE void
+PA_ALWAYS_INLINE void
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::Allocate(
     uintptr_t address) {
   PA_SCAN_DCHECK(IsFreed(address));
@@ -216,7 +217,7 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE bool
+PA_ALWAYS_INLINE bool
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::Quarantine(
     uintptr_t address,
     Epoch epoch) {
@@ -239,7 +240,8 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE bool StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
+PA_ALWAYS_INLINE bool
+StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
     MarkQuarantinedAsReachable(uintptr_t address, Epoch epoch) {
   static_assert((~static_cast<CellType>(State::kQuarantined1) & kStateMask) ==
                     (static_cast<CellType>(State::kQuarantined2) & kStateMask),
@@ -256,9 +258,9 @@
   CellType expected =
       (cell.load(std::memory_order_relaxed) & clear_mask) | set_mask_old;
   CellType desired = expected ^ xor_mask;
-  while (UNLIKELY(!cell.compare_exchange_weak(expected, desired,
-                                              std::memory_order_relaxed,
-                                              std::memory_order_relaxed))) {
+  while (PA_UNLIKELY(!cell.compare_exchange_weak(expected, desired,
+                                                 std::memory_order_relaxed,
+                                                 std::memory_order_relaxed))) {
     // First check if the object was already marked before or in parallel.
     if ((expected & set_mask_old) == 0) {
       // Check that the bits can't be in any state other than
@@ -276,7 +278,7 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE void
+PA_ALWAYS_INLINE void
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::Free(
     uintptr_t address) {
   // *Scan is enabled at runtime, which means that we can free an allocation,
@@ -292,14 +294,14 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE bool
+PA_ALWAYS_INLINE bool
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::IsAllocated(
     uintptr_t address) const {
   return GetBits(address) == static_cast<unsigned>(State::kAlloced);
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE bool
+PA_ALWAYS_INLINE bool
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::IsQuarantined(
     uintptr_t address) const {
   // On x86 CPI of popcnt is the same as tzcnt, so we use it instead of tzcnt +
@@ -308,14 +310,14 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE bool
+PA_ALWAYS_INLINE bool
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::IsFreed(
     uintptr_t address) const {
   return GetBits(address) == static_cast<unsigned>(State::kFreed);
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE
+PA_ALWAYS_INLINE
     typename StateBitmap<PageSize, PageAlignment, AllocationAlignment>::CellType
     StateBitmap<PageSize, PageAlignment, AllocationAlignment>::LoadCell(
         size_t cell_index) const {
@@ -323,7 +325,7 @@
 }
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
-ALWAYS_INLINE constexpr std::pair<size_t, size_t>
+PA_ALWAYS_INLINE constexpr std::pair<size_t, size_t>
 StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
     AllocationIndexAndBit(uintptr_t address) {
   const uintptr_t offset_in_page = address & kPageOffsetMask;
@@ -367,7 +369,8 @@
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
 template <typename Callback>
-ALWAYS_INLINE void StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
+PA_ALWAYS_INLINE void
+StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
     SimpleCallbackForwarder::operator()(Callback callback,
                                         uintptr_t pointer,
                                         CellType bits) const {
@@ -376,7 +379,8 @@
 
 template <size_t PageSize, size_t PageAlignment, size_t AllocationAlignment>
 template <typename Callback>
-ALWAYS_INLINE void StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
+PA_ALWAYS_INLINE void
+StateBitmap<PageSize, PageAlignment, AllocationAlignment>::
     QuarantineCallbackForwarder::operator()(Callback callback,
                                             uintptr_t pointer,
                                             CellType bits) const {
diff --git a/base/allocator/partition_allocator/tagging.cc b/base/allocator/partition_allocator/tagging.cc
index c342f44..72b1a45 100644
--- a/base/allocator/partition_allocator/tagging.cc
+++ b/base/allocator/partition_allocator/tagging.cc
@@ -4,6 +4,7 @@
 
 #include "base/allocator/partition_allocator/tagging.h"
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/cpu.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
@@ -158,7 +159,7 @@
 }
 
 void* RemaskVoidPtrForMTE(void* ptr) {
-  if (LIKELY(ptr)) {
+  if (PA_LIKELY(ptr)) {
     // Can't look up the tag for a null ptr (segfaults).
     return __arm_mte_get_tag(ptr);
   }
diff --git a/base/allocator/partition_allocator/tagging.h b/base/allocator/partition_allocator/tagging.h
index b317ea9..b9db910 100644
--- a/base/allocator/partition_allocator/tagging.h
+++ b/base/allocator/partition_allocator/tagging.h
@@ -11,9 +11,9 @@
 #include <cstddef>
 #include <cstdint>
 
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 namespace partition_alloc {
@@ -85,14 +85,14 @@
 // range is set to the same tag.
 // TODO(bartekn): Remove the T* variant.
 template <typename T>
-ALWAYS_INLINE T* TagMemoryRangeIncrement(T* ptr, size_t size) {
+PA_ALWAYS_INLINE T* TagMemoryRangeIncrement(T* ptr, size_t size) {
 #if defined(PA_HAS_MEMORY_TAGGING)
   return reinterpret_cast<T*>(global_tag_memory_range_increment_fn(ptr, size));
 #else
   return ptr;
 #endif
 }
-ALWAYS_INLINE uintptr_t TagMemoryRangeIncrement(uintptr_t ptr, size_t size) {
+PA_ALWAYS_INLINE uintptr_t TagMemoryRangeIncrement(uintptr_t ptr, size_t size) {
   return reinterpret_cast<uintptr_t>(
       TagMemoryRangeIncrement(reinterpret_cast<void*>(ptr), size));
 }
@@ -102,9 +102,9 @@
 // range is set to the same tag.
 // TODO(bartekn): Remove the T* variant.
 template <typename T>
-ALWAYS_INLINE T* TagMemoryRangeRandomly(T* ptr,
-                                        size_t size,
-                                        uint64_t mask = 0u) {
+PA_ALWAYS_INLINE T* TagMemoryRangeRandomly(T* ptr,
+                                           size_t size,
+                                           uint64_t mask = 0u) {
 #if defined(PA_HAS_MEMORY_TAGGING)
   return reinterpret_cast<T*>(
       global_tag_memory_range_randomly_fn(ptr, size, mask));
@@ -112,9 +112,9 @@
   return ptr;
 #endif
 }
-ALWAYS_INLINE uintptr_t TagMemoryRangeRandomly(uintptr_t ptr,
-                                               size_t size,
-                                               uint64_t mask = 0u) {
+PA_ALWAYS_INLINE uintptr_t TagMemoryRangeRandomly(uintptr_t ptr,
+                                                  size_t size,
+                                                  uint64_t mask = 0u) {
   return reinterpret_cast<uintptr_t>(
       TagMemoryRangeRandomly(reinterpret_cast<void*>(ptr), size, mask));
 }
@@ -122,7 +122,7 @@
 // Gets a version of ptr that's safe to dereference.
 // TODO(bartekn): Remove the T* variant.
 template <typename T>
-ALWAYS_INLINE T* RemaskPtr(T* ptr) {
+PA_ALWAYS_INLINE T* RemaskPtr(T* ptr) {
 #if defined(PA_HAS_MEMORY_TAGGING)
   return reinterpret_cast<T*>(global_remask_void_ptr_fn(ptr));
 #else
@@ -130,13 +130,13 @@
 #endif
 }
 // Gets a version of address that's safe to dereference, if cast to a pointer.
-ALWAYS_INLINE uintptr_t RemaskPtr(uintptr_t address) {
+PA_ALWAYS_INLINE uintptr_t RemaskPtr(uintptr_t address) {
   return reinterpret_cast<uintptr_t>(
       RemaskPtr(reinterpret_cast<void*>(address)));
 }
 
 // Strips the tag bits off address.
-ALWAYS_INLINE uintptr_t UnmaskPtr(uintptr_t address) {
+PA_ALWAYS_INLINE uintptr_t UnmaskPtr(uintptr_t address) {
 #if defined(PA_HAS_MEMORY_TAGGING)
   return address & kMemTagUnmask;
 #else
@@ -146,7 +146,7 @@
 // Strips the tag bits off ptr.
 // TODO(bartekn): Remove the T* variant.
 template <typename T>
-ALWAYS_INLINE T* UnmaskPtr(T* ptr) {
+PA_ALWAYS_INLINE T* UnmaskPtr(T* ptr) {
   return reinterpret_cast<T*>(UnmaskPtr(reinterpret_cast<uintptr_t>(ptr)));
 }
 
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index 570a4072..bc6e9cf9 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -17,7 +17,6 @@
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_root.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
 #include "build/build_config.h"
 
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 697815d..297d09c 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -870,7 +870,6 @@
     }
 
     @VisibleForTesting
-    // After Android M, this function is likely a no-op.
     protected void loadNonMainDex() {
         if (mLoadState == LoadState.LOADED) return;
         synchronized (mNonMainDexLock) {
@@ -878,6 +877,7 @@
             if (mLoadState == LoadState.LOADED) return;
             try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadNonMainDex")) {
                 if (!JNIUtils.isSelectiveJniRegistrationEnabled()) {
+                    // On M+ the native symbols are exported, and registering natives seems fast.
                     LibraryLoaderJni.get().registerNonMainDexJni();
                 }
                 mLoadState = LoadState.LOADED;
diff --git a/base/android/java/src/org/chromium/base/task/TaskTraits.java b/base/android/java/src/org/chromium/base/task/TaskTraits.java
index c27c44f..f967aa9 100644
--- a/base/android/java/src/org/chromium/base/task/TaskTraits.java
+++ b/base/android/java/src/org/chromium/base/task/TaskTraits.java
@@ -100,13 +100,6 @@
         mExtensionData = other.mExtensionData;
     }
 
-    // Exposed to allow changing the priority of UiThreadTaskTraits.DEFAULT for the experiment being
-    // tracked in crbug.com/1259560. SHOULD NOT BE CALLED FOR ANY OTHER REASON and will be removed
-    // once that experiment is concluded.
-    public void setTaskPriorityToUserBlockingForUiThreadDefaultTaskPriorityExperiment() {
-        mPriority = TaskPriority.USER_BLOCKING;
-    }
-
     public TaskTraits taskPriority(int taskPriority) {
         TaskTraits taskTraits = new TaskTraits(this);
         taskTraits.mPriority = taskPriority;
diff --git a/base/logging.cc b/base/logging.cc
index 67c09db..5f9eba0 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
+#include <atomic>
 
 // logging.h is a widely included header and its size has significant impact on
 // build time. Try not to raise this limit unless absolutely necessary. See
@@ -23,6 +24,9 @@
 
 #include "base/base_export.h"
 #include "base/debug/crash_logging.h"
+#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)
+#include "base/debug/leak_annotations.h"
+#endif  // defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)
 #include "base/immediate_crash.h"
 #include "base/pending_task.h"
 #include "base/strings/string_piece.h"
@@ -122,8 +126,18 @@
 namespace {
 
 #if BUILDFLAG(USE_RUNTIME_VLOG)
-VlogInfo* g_vlog_info = nullptr;
-VlogInfo* g_vlog_info_prev = nullptr;
+// NOTE: Once |g_vlog_info| has been initialized, it might be in use
+// by another thread. Never delete the old VLogInfo, just create a second
+// one and overwrite. We need to use leak-san annotations on this intentional
+// leak.
+//
+// This can be read/written on multiple threads. In tests we don't see that
+// causing a problem as updates tend to happen early. Atomic ensures there are
+// no problems. To avoid some of the overhead of Atomic, we use
+// |load(std::memory_order_acquire)| and |store(...,
+// std::memory_order_release)| when reading or writing. This
+// guarantees that referenced object is available at the time the point is read.
+std::atomic<VlogInfo*> g_vlog_info = nullptr;
 #endif  // BUILDFLAG(USE_RUNTIME_VLOG)
 
 const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
@@ -405,19 +419,18 @@
     // vlog switches.
     if (command_line->HasSwitch(switches::kV) ||
         command_line->HasSwitch(switches::kVModule)) {
-      // NOTE: If |g_vlog_info| has already been initialized, it might be in use
-      // by another thread. Don't delete the old VLogInfo, just create a second
-      // one. We keep track of both to avoid memory leak warnings.
-      CHECK(!g_vlog_info_prev);
-      g_vlog_info_prev = g_vlog_info;
-
-      g_vlog_info =
+#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)
+      // See comments on |g_vlog_info|.
+      ScopedLeakSanitizerDisabler lsan_disabler;
+#endif  // defined(LEAK_SANITIZER)
+      g_vlog_info.store(
           new VlogInfo(command_line->GetSwitchValueASCII(switches::kV),
                        command_line->GetSwitchValueASCII(switches::kVModule),
-                       &g_min_log_level);
+                       &g_min_log_level),
+          std::memory_order_release);
     }
   }
-#endif  // defined(USE_RUNTIME_VLOG)
+#endif  // BUILDFLAG(USE_RUNTIME_VLOG)
 
   g_logging_destination = settings.logging_dest;
 
@@ -504,7 +517,7 @@
 #if BUILDFLAG(USE_RUNTIME_VLOG)
   // Note: |g_vlog_info| may change on a different thread during startup
   // (but will always be valid or nullptr).
-  VlogInfo* vlog_info = g_vlog_info;
+  VlogInfo* vlog_info = g_vlog_info.load(std::memory_order_acquire);
   return vlog_info ?
       vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) :
       GetVlogVerbosity();
diff --git a/base/logging.h b/base/logging.h
index 39dba0d..dba8d3e 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -478,7 +478,7 @@
 
 #define VLOG_IS_ON(verboselevel) ((verboselevel) <= (ENABLED_VLOG_LEVEL))
 
-#else
+#else  // !BUILDFLAG(USE_RUNTIME_VLOG)
 
 // We don't do any caching tricks with VLOG_IS_ON() like the
 // google-glog version since it increases binary size.  This means
@@ -488,7 +488,7 @@
 #define VLOG_IS_ON(verboselevel) \
   ((verboselevel) <= ::logging::GetVlogLevel(__FILE__))
 
-#endif
+#endif  // !BUILDFLAG(USE_RUNTIME_VLOG)
 
 // Helper macro which avoids evaluating the arguments to a stream if
 // the condition doesn't hold. Condition is evaluated once and only once.
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index ad16a8a..6fd132f 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -464,10 +464,6 @@
         'com.google.common.flogger.backend.google.GooglePlatform',
         'com.google.common.flogger.backend.system.DefaultPlatform',
 
-        # trichrome_webview_google_bundle contains this missing reference.
-        # TODO(crbug.com/1142530): Fix this missing reference properly.
-        'org.chromium.build.NativeLibraries',
-
         # TODO(agrieve): Exclude these only when use_jacoco_coverage=true.
         'java.lang.instrument.ClassFileTransformer',
         'java.lang.instrument.IllegalClassFormatException',
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 1a9b3736..6c119948 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2133,6 +2133,7 @@
   #     uncompressed in the APK. Must be unset or true if load_library_from_apk
   #     is set to true.
   #   uncompress_dex: Store final .dex files uncompressed in the apk.
+  #   omit_dex: If true, do not build or include classes.dex.
   #   strip_resource_names: True if resource names should be stripped from the
   #     resources.arsc file in the apk or module.
   #   strip_unused_resources: True if unused resources should be stripped from
@@ -2215,6 +2216,7 @@
           defined(invoker.is_base_module) && invoker.is_base_module
     }
 
+    _omit_dex = defined(invoker.omit_dex) && invoker.omit_dex
     _enable_multidex =
         !defined(invoker.enable_multidex) || invoker.enable_multidex
 
@@ -2358,12 +2360,13 @@
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     assert(_rebased_build_config != "")  # Mark as used.
 
-    _generate_buildconfig_java = !defined(invoker.apk_under_test)
+    _generate_buildconfig_java = !defined(invoker.apk_under_test) && !_omit_dex
     if (defined(invoker.generate_buildconfig_java)) {
       _generate_buildconfig_java = invoker.generate_buildconfig_java
     }
 
-    _generate_productconfig_java = defined(invoker.product_config_java_packages)
+    _generate_productconfig_java =
+        defined(invoker.product_config_java_packages) && !_omit_dex
 
     # JNI generation usually goes hand-in-hand with buildconfig generation.
     _generate_final_jni = _generate_buildconfig_java
@@ -2427,7 +2430,7 @@
       _incremental_apk_path = "${_final_apk_path_no_ext}_incremental.apk"
     }
 
-    if (!_incremental_apk) {
+    if (!_incremental_apk && !_omit_dex) {
       # Bundle modules don't build the dex here, but need to write this path
       # to their .build_config.json file.
       if (_proguard_enabled) {
@@ -2684,8 +2687,9 @@
     } else {
       _generate_native_libraries_java =
           (!_is_bundle_module || _is_base_module) &&
-          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) &&
-          !_uses_static_library_synchronized_proguard
+          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != [] ||
+           defined(invoker.static_library_provider)) &&
+          !_uses_static_library_synchronized_proguard && !_omit_dex
     }
     if (_generate_native_libraries_java) {
       write_native_libraries_java("${_template_name}__native_libraries") {
@@ -2694,7 +2698,17 @@
         # Do not add a dep on the generated_file target in order to avoid having
         # to build the native libraries before this target. The dependency is
         # instead captured via a depfile.
-        if (_native_libs_deps != []) {
+        if (_uses_static_library) {
+          _prefix = get_label_info(invoker.static_library_provider,
+                                   "target_gen_dir") + "/" +
+                    get_label_info(invoker.static_library_provider, "name")
+          if (defined(invoker.static_library_provider_use_secondary_abi) &&
+              invoker.static_library_provider_use_secondary_abi) {
+            native_libraries_list_file = "${_prefix}.secondary_abi_native_libs"
+          } else {
+            native_libraries_list_file = "${_prefix}.native_libs"
+          }
+        } else if (_native_libs_deps != []) {
           native_libraries_list_file = _shared_library_list_file
         } else {
           native_libraries_list_file = _secondary_abi_shared_library_list_file
@@ -2790,14 +2804,15 @@
                  ])
     }
 
-    _java_target = "${_template_name}__java"
-
     if (_is_bundle_module) {
       _add_view_trace_events =
           defined(invoker.add_view_trace_events) &&
           invoker.add_view_trace_events && enable_trace_event_bytecode_rewriting
     }
 
+    # We cannot skip this target when omit_dex = true because it writes the
+    # build_config.json.
+    _java_target = "${_template_name}__java"
     java_library_impl(_java_target) {
       forward_variables_from(invoker,
                              [
@@ -2929,7 +2944,7 @@
 
     if (_uses_static_library_synchronized_proguard) {
       _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
-    } else if (_is_bundle_module && _proguard_enabled) {
+    } else if ((_is_bundle_module && _proguard_enabled) || _omit_dex) {
       _final_deps += [ ":$_java_target" ]
     } else if (_incremental_apk) {
       if (defined(invoker.enable_proguard_checks)) {
@@ -3205,7 +3220,7 @@
         deps = _deps + [ ":$_build_config_target" ]
 
         if ((!_proguard_enabled || _incremental_apk) &&
-            enable_jdk_library_desugaring) {
+            enable_jdk_library_desugaring && !_omit_dex) {
           _all_jdk_libs = "//build/android:all_jdk_libs"
           deps += [ _all_jdk_libs ]
           jdk_libs_dex = get_label_info(_all_jdk_libs, "target_out_dir") +
@@ -3485,6 +3500,7 @@
                                "expected_libs_and_assets_base",
                                "generate_buildconfig_java",
                                "generate_final_jni",
+                               "generate_native_libraries_java",
                                "include_size_info",
                                "input_jars_paths",
                                "use_modern_linker",
@@ -3509,6 +3525,7 @@
                                "native_lib_placeholders",
                                "never_incremental",
                                "no_xml_namespaces",
+                               "omit_dex",
                                "png_to_webp",
                                "post_process_package_resources_script",
                                "processor_args_javac",
@@ -3533,6 +3550,7 @@
                                "srcjar_deps",
                                "static_library_dependent_targets",
                                "static_library_provider",
+                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "target_sdk_version",
                                "testonly",
@@ -3632,6 +3650,7 @@
                                "load_library_from_apk",
                                "loadable_modules",
                                "product_config_java_packages",
+                               "main_component_library",
                                "manifest_package",
                                "max_sdk_version",
                                "min_sdk_version",
@@ -3660,6 +3679,7 @@
                                "short_resource_paths",
                                "srcjar_deps",
                                "static_library_provider",
+                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "strip_resource_names",
                                "strip_unused_resources",
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 9e4e0f3..fc7f171 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220524.0.1
+8.20220524.1.1
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 0f261f5..a9c16c2 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -442,7 +442,6 @@
     : using_synchronous_renderer_compositor_(
           using_synchronous_renderer_compositor),
       enabled_(false),
-      did_send_begin_main_frame_(false),
       compositor_drawing_continuously_(false),
       begin_main_frame_queue_duration_history_(kDurationHistorySize),
       begin_main_frame_queue_duration_critical_history_(kDurationHistorySize),
@@ -597,8 +596,6 @@
 
   if (frame_type == viz::BeginFrameArgs::NORMAL)
     uma_reporter_->AddBeginImplFrameLatency(now - frame_time);
-
-  did_send_begin_main_frame_ = false;
 }
 
 void CompositorTimingHistory::WillFinishImplFrame(bool needs_redraw) {
@@ -616,8 +613,6 @@
 
   begin_main_frame_on_critical_path_ = args.on_critical_path;
   begin_main_frame_sent_time_ = Now();
-
-  did_send_begin_main_frame_ = true;
 }
 
 void CompositorTimingHistory::BeginMainFrameStarted(
diff --git a/cc/metrics/compositor_timing_history.h b/cc/metrics/compositor_timing_history.h
index 22e7ac9..83bd347 100644
--- a/cc/metrics/compositor_timing_history.h
+++ b/cc/metrics/compositor_timing_history.h
@@ -112,7 +112,6 @@
   bool enabled_;
 
   // Used to calculate frame rates of Main and Impl threads.
-  bool did_send_begin_main_frame_;
   bool compositor_drawing_continuously_;
   base::TimeTicks new_active_tree_draw_end_time_prev_;
   base::TimeTicks draw_end_time_prev_;
diff --git a/chrome/VERSION b/chrome/VERSION
index 0e77f6e..27fcb466 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=104
 MINOR=0
-BUILD=5082
+BUILD=5083
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 33cb3aa6..bd7131e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2924,12 +2924,6 @@
   }
 }
 
-# TODO(agrieve): Remove this once we switch to using bundle targets to
-# generate APK stubs.
-android_resources("trichrome_dummy_resources") {
-  sources = [ "trichrome/res_dummy/values/strings.xml" ]
-}
-
 chrome_public_unit_test_apk_manifest =
     "$root_gen_dir/chrome_public_unit_test_apk_manifest/AndroidManifest.xml"
 chrome_public_test_apk_manifest =
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 6fb7170..87220dc 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -235,6 +235,19 @@
       use_chromium_linker = chromium_linker_supported
     }
 
+    if (_is_trichrome) {
+      static_library_provider_use_secondary_abi = _is_secondary_abi_primary
+
+      # http://crbug.com/1042107.
+      if (is_component_build) {
+        if (android_64bit_target_cpu && _is_64_bit_browser) {
+          main_component_library = "libmonochrome_64.cr.so"
+        } else {
+          main_component_library = "libmonochrome.cr.so"
+        }
+      }
+    }
+
     if (!_is_monochrome && !_is_trichrome) {
       deps += [
         "//chrome/android:chrome_public_v8_assets",
diff --git a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
index fd2f714..cb6d641c 100644
--- a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
@@ -10,6 +10,7 @@
   <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
   <application
       android:extractNativeLibs="false"
+      android:hasCode="false"
       android:icon="@drawable/icon_webview"
       android:label="Trichrome Library"
       android:multiArch="true"
diff --git a/chrome/android/java/AndroidManifest_trichrome_library.xml b/chrome/android/java/AndroidManifest_trichrome_library.xml
index 97e9ddd..070efc3 100644
--- a/chrome/android/java/AndroidManifest_trichrome_library.xml
+++ b/chrome/android/java/AndroidManifest_trichrome_library.xml
@@ -18,6 +18,7 @@
 
     <!-- TODO(torne): we should specify an icon, roundIcon, and label from resources. -->
     <application
+        android:hasCode="false"
         android:label="{{ application_label|default('Trichrome Library') }}"
         android:icon="@drawable/icon_webview"
         android:multiArch="true"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index 470cef6..ca1681c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -91,8 +91,6 @@
                 add(ChromeFeatureList.ELASTIC_OVERSCROLL);
                 add(ChromeFeatureList.ELIDE_PRIORITIZATION_OF_PRE_NATIVE_BOOTSTRAP_TASKS);
                 add(ChromeFeatureList.FEED_LOADING_PLACEHOLDER);
-                add(ChromeFeatureList
-                                .GIVE_JAVA_UI_THREAD_DEFAULT_TASK_TRAITS_USER_BLOCKING_PRIORITY);
                 add(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS);
                 add(ChromeFeatureList.IMMERSIVE_UI_MODE);
                 add(ChromeFeatureList.INSTANT_START);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
index 99ce3e7..9bfd785 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
@@ -24,8 +24,9 @@
         @Override
         public void onCreate() {
             super.onCreate();
-            // TODO(crbug.com/1126301): This matches logic in MonochromeApplication.java.
-            // Deduplicate if chrome split launches.
+            if (getApplication().isWebViewProcess()) {
+                WebViewApkApplication.checkForAppRecovery();
+            }
             if (!VersionInfo.isStableBuild() && getApplication().isWebViewProcess()) {
                 WebViewApkApplication.postDeveloperUiLauncherIconTask();
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index fe571f3..188b059 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -114,7 +114,6 @@
 import org.chromium.content_public.browser.ChildProcessLauncherHelper;
 import org.chromium.content_public.browser.ContactsPicker;
 import org.chromium.content_public.browser.ContactsPickerListener;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.ui.base.Clipboard;
 import org.chromium.ui.base.PhotoPicker;
@@ -182,13 +181,6 @@
      * Performs the shared class initialization.
      */
     protected void handlePreNativeInitialization() {
-        if (CachedFeatureFlags.isEnabled(
-                    ChromeFeatureList
-                            .GIVE_JAVA_UI_THREAD_DEFAULT_TASK_TRAITS_USER_BLOCKING_PRIORITY)) {
-            UiThreadTaskTraits.DEFAULT
-                    .setTaskPriorityToUserBlockingForUiThreadDefaultTaskPriorityExperiment();
-        }
-
         BrowserTaskExecutor.register();
         // This function controls whether BrowserTaskExecutor posts pre-native bootstrap tasks at
         // the front or back of the Looper's queue.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index a4dc78e..0b3c4c4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -310,6 +310,8 @@
     private static final int PANEL_INTERACTION_MAX_RETRIES = 3;
     private static final int PANEL_INTERACTION_RETRY_DELAY_MS = 200;
 
+    private static final int DOUBLE_TAP_DELAY_MULTIPLIER = 3;
+
     // Search request URL paths and CGI parameters.
     private static final String LOW_PRIORITY_SEARCH_ENDPOINT = "/s?";
     private static final String NORMAL_PRIORITY_SEARCH_ENDPOINT = "/search?";
@@ -1288,7 +1290,7 @@
         // refinement from nearby taps. The double-tap timeout is sufficiently
         // short that this shouldn't conflict with tap refinement by the user.
         int doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
-        Thread.sleep(doubleTapTimeout);
+        Thread.sleep(doubleTapTimeout * DOUBLE_TAP_DELAY_MULTIPLIER);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index e489abd..5e0068fb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -168,31 +168,6 @@
     //============================================================================================
 
     /**
-     * Tests Ranker logging for a simple trigger that resolves.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    @DisabledTest(message = "https://crbug.com/1291065")
-    // TODO(donnd): remove with Ranker support.
-    public void testResolvingSearchRankerLogging() throws Exception {
-        FeatureList.setTestFeatures(ENABLE_NONE);
-
-        simulateResolveSearch("intelligence");
-        assertLoadedLowPriorityUrl();
-
-        assertLoggedAllExpectedFeaturesToRanker();
-        Assert.assertEquals(
-                true, loggedToRanker(ContextualSearchInteractionRecorder.Feature.IS_LONG_WORD));
-        // The panel must be closed for outcomes to be logged.
-        // Close the panel by clicking far away in order to make sure the outcomes get logged by
-        // the hideContextualSearchUi call to writeRankerLoggerOutcomesAndReset.
-        clickWordNode("states-far");
-        waitForPanelToClose();
-        assertLoggedAllExpectedOutcomesToRanker();
-    }
-
-    /**
      * Tests swiping the overlay open, after an initial trigger that activates the peeking card.
      */
     @Test
@@ -559,21 +534,6 @@
         Assert.assertFalse(mManager.getRequest().isTranslationForced());
     }
 
-    /**
-     * Tests that a non-resolve search does trigger translation.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
-    @DisabledTest(message = "http://crbug.com/1296677")
-    public void testNonResolveTranslates(@EnabledFeature int enabledFeature) throws Exception {
-        // A non-resolving gesture on any word should trigger a forced translation.
-        simulateNonResolveSearch("search");
-        // Make sure we did try to trigger translate.
-        Assert.assertTrue(mManager.getRequest().isTranslationForced());
-    }
-
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
diff --git a/chrome/android/proguard/trichrome.flags b/chrome/android/proguard/trichrome.flags
deleted file mode 100644
index 68fb39a..0000000
--- a/chrome/android/proguard/trichrome.flags
+++ /dev/null
@@ -1,8 +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.
-
-# TODO(agrieve): Once this -keep is removed, add a @CheckDiscard to LibraryLoaderConfig.java.
-# Currently the Trichrome library just contains NativeLibraries, which we keep.
-# https://crbug.com/901465
--keep class org.chromium.build.NativeLibraries { *; }
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index 01b7f0b..1e2eec7 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -114,7 +114,7 @@
       product_version_resources_dep =
           "//chrome/android:product_version_resources"
     } else {
-      generate_buildconfig_java = false
+      omit_dex = true
     }
 
     # TODO(torne): using icon_resources just to get a temporary icon
@@ -194,37 +194,6 @@
         }
       }
     }
-
-    # http://crbug.com/1042107.
-    if (is_component_build) {
-      if (android_64bit_target_cpu && invoker.is_64_bit_browser) {
-        main_component_library = "libmonochrome_64.cr.so"
-      } else {
-        main_component_library = "libmonochrome.cr.so"
-      }
-    }
-
-    if (!is_java_debug) {
-      proguard_enabled = true
-      proguard_configs = [
-        "//base/android/proguard/chromium_apk.flags",
-        "//base/android/proguard/chromium_code.flags",
-        "//chrome/android/proguard/trichrome.flags",
-      ]
-      if (trichrome_synchronized_proguard) {
-        proguard_configs += [
-          "//chrome/android/proguard/static_library_dex_reference_workarounds.flags",
-          "//base/android/proguard/enable_obfuscation.flags",
-        ]
-      } else {
-        # Disabling all obfuscation for the Trichrome library as a temporary
-        # workaround for crbug.com/1012842. There were naming conflicts between
-        # Library and Chrome, since each Proguard run doesn't know about the
-        # other, and thus handed out the first names (a, b, c) to both.
-        proguard_enable_obfuscation = false
-      }
-    }
-    deps += [ "//chrome/android:trichrome_dummy_resources" ]
   }
 }
 
diff --git a/chrome/android/trichrome/res_dummy/values/strings.xml b/chrome/android/trichrome/res_dummy/values/strings.xml
deleted file mode 100644
index a0d71c24..0000000
--- a/chrome/android/trichrome/res_dummy/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 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. -->
-
-<!-- DO NOT ADD MORE RESOURCES HERE -->
-<resources>
-    <string name="dummy"></string>
-</resources>
\ No newline at end of file
diff --git a/chrome/android/trichrome/static_library_shared_java_code.md b/chrome/android/trichrome/static_library_shared_java_code.md
deleted file mode 100644
index a288902..0000000
--- a/chrome/android/trichrome/static_library_shared_java_code.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Static Library Java code
-
-[TOC]
-
-## Overview
-
-This document describes how static library targets can be used to share common
-Java code between multiple APKs. More detail can be found at
-[go/proguarding-trichrome](goto.google.com/proguarding-trichrome).
-
-## TrichromeLibrary
-
-Currently (Jan 2020) trichrome library is the only target to make use of static
-shared library APKs and is used to share common code used by both Chrome and
-Webview.
-
-## Status
-
-Java code sharing is mostly implemented at this point but there is one remaining
-blocker related to how
-[native method resolution works in Webview](crbug.com/1025009).
-
-## How it works
-
-### Build variables
-
-For `android_apk_or_module` base templates:
-
-`static_library_provider`: Specifies that this target depends on a static shared
-library APK. When synchronized proguard is turned on, the
-`static_library_provider` becomes the target that provides the final dex file.
-
-`static_library_dependent_targets`: If set, generates final dex files for
-itself and for all targets in the `static_library_dependent_targets` list.
-
-`static_library_synchronized_proguard`: Turns on synchronized proguard for
-targets that also set `static_library_provider`.
-
-### .build_config
-
-`write_build_config.py` is responsible for figuring out where code and related
-artifacts for the `static_library_provider` and
-`static_library_dependent_targets` belongs. The main difference from regular
-`.build_configs` is the mapping recording which input jars belong to each final
-dex file. Ex:
-
-```
-"deps_info": {
-  ...
-  "static_library_dependent_classpath_configs": {
-      "gen/android_webview/trichrome_webview_apk.build_config.json": [
-        "obj/android_webview/trichrome_webview_apk/trichrome_webview_apk.jar",
-        ...
-      ],
-      "gen/chrome/android/trichrome_chrome_bundle.build_config.json": [
-        "lib.java/chrome/android/app_hooks_java.jar",
-        ...
-      "gen/chrome/android/trichrome_library_apk.build_config.json": [
-        "lib.java/base/base_java.jar",
-        ...
-      ]
-      ...
-  }
-}
-```
-
-### Synchronized ProGuard
-
-TrichromeChromeBundle (base module) and TrichromeWebview do not have a final
-`dex` or `proguard` step. Instead the library APK creates a "fat" dex from the
-`.build_config.json:deps_info:java_runtime_classpath`.
-
-Then, the mapping of `.build_config.json` -> owned input jars stored in the
-`.build_config.json` is used by `dexsplitter` to generate final .dex files for
-TrichromeLibrary, TrichromeChrome, and TrichromeWebview.
-
-### Resources
-
-For Java code to be shared between Chrome and Webview in [Trichrome][trichrome],
-we ensure that Chrome and Webview use the same resource IDs. This requires a
-few adjustments to how resources are created.
-
-1. Webview's resources are compiled first without any changes.
-2. Chrome's resources are compiled second, but use the same resource IDs as
-   Webview when possible.
-3. When synchronized proguarding is turned on, the `R.java` files generated in
-   the previous step are discarded. The shared static library APK target
-   (trichrome library) takes the output `R.txt` files from the previous steps
-   and includes those resources in its own `R.java` generation.
-
-[trichrome]: /chrome/android/trichrome/static_library_shared_java_code.md
-
-### Usage
-
-* Building trichrome_chrome_bundle or trichrome_webview_apk (and various arch
-  variants) will ensure the correct library target is also built.
-* Using the generated wrapper script from the main APK is sufficient (no need
-  to explicitly install the library).
-  * `bin/trichrome_chrome_bundle run` will ensure TrichromeChromeBundle and
-    TrichromeLibrary are installed before launching Chrome.
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index de7f049..56669383 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -723,10 +723,10 @@
     Disabled
   </message>
   <message name="IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB" desc="Title for the link to open personalization hub.">
-    Personalize your device
+    Set your wallpaper &amp; style
   </message>
   <message name="IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE" desc="Description for the link to open personalization hub.">
-    Customize wallpaper, avatar, screensaver and more
+    Personalize wallpaper, screen saver, accent colors, and more
   </message>
 
   <!-- Ambient Mode Page -->
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB.png.sha1
index 4a3e03e..5e6fc71 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB.png.sha1
@@ -1 +1 @@
-e7a8d7eb5c5f3e8ae3d9213f28dbd150e11b8c5c
\ No newline at end of file
+e2afb90226333af2ce0d1479a8b66e2446d2c1b7
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE.png.sha1
index 4a3e03e..5e6fc71 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE.png.sha1
@@ -1 +1 @@
-e7a8d7eb5c5f3e8ae3d9213f28dbd150e11b8c5c
\ No newline at end of file
+e2afb90226333af2ce0d1479a8b66e2446d2c1b7
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 76578c6..4a6e685e9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1212,8 +1212,6 @@
     "policy/messaging_layer/util/report_queue_manual_test_context.h",
     "policy/messaging_layer/util/user_dm_token_retriever.cc",
     "policy/messaging_layer/util/user_dm_token_retriever.h",
-    "policy/network_prediction_policy_handler.cc",
-    "policy/network_prediction_policy_handler.h",
     "policy/policy_ui_utils.cc",
     "policy/policy_ui_utils.h",
     "policy/profile_policy_connector.cc",
@@ -1831,14 +1829,6 @@
     "universal_web_contents_observers.h",
     "update_client/chrome_update_query_params_delegate.cc",
     "update_client/chrome_update_query_params_delegate.h",
-    "url_param_filter/cross_otr_observer.cc",
-    "url_param_filter/cross_otr_observer.h",
-    "url_param_filter/url_param_classifications_loader.cc",
-    "url_param_filter/url_param_classifications_loader.h",
-    "url_param_filter/url_param_filter_throttle.cc",
-    "url_param_filter/url_param_filter_throttle.h",
-    "url_param_filter/url_param_filterer.cc",
-    "url_param_filter/url_param_filterer.h",
     "usb/frame_usb_services.cc",
     "usb/frame_usb_services.h",
     "usb/usb_blocklist.cc",
@@ -1958,7 +1948,6 @@
     ":permissions_proto",
     ":resource_prefetch_predictor_proto",
     ":unexpire_flags",
-    ":url_param_filter_classification_proto",
     "//base",
     "//base:i18n",
     "//base/allocator:buildflags",
@@ -2333,6 +2322,8 @@
     "//components/url_formatter/spoof_checks/top_domains:top500_domains",
     "//components/url_formatter/spoof_checks/top_domains:top500_domains_header",
     "//components/url_matcher",
+    "//components/url_param_filter/content",
+    "//components/url_param_filter/core",
     "//components/user_manager",
     "//components/user_prefs",
     "//components/variations",
@@ -3415,6 +3406,7 @@
       "//components/translate/core/language_detection",
       "//components/ukm/android:ukm_recorder",
       "//components/ukm/content",
+      "//components/url_param_filter/core",
       "//components/viz/common",
       "//components/webapk:proto",
       "//components/webauthn/android",
@@ -7576,10 +7568,6 @@
   sources = [ "permissions/crowd_deny.proto" ]
 }
 
-proto_library("url_param_filter_classification_proto") {
-  sources = [ "url_param_filter/url_param_filter_classification.proto" ]
-}
-
 grit("resources") {
   source = "browser_resources.grd"
 
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index b66fcba..46a766c 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -354,6 +354,7 @@
   "+components/upload_list",
   "+components/url_matcher",
   "+components/url_pattern_index/proto",
+  "+components/url_param_filter/content",
   "+components/user_manager",
   "+components/user_notes",
   "+components/user_prefs",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 40b20f13..31f47b7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4927,6 +4927,10 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(
          chromeos::features::kDriveFsBidirectionalNativeMessaging)},
+    {"drive-fs-chrome-networking",
+     flag_descriptions::kDriveFsChromeNetworkingName,
+     flag_descriptions::kDriveFsChromeNetworkingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kDriveFsChromeNetworking)},
     {"files-archivemount", flag_descriptions::kFilesArchivemountName,
      flag_descriptions::kFilesArchivemountDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFilesArchivemount)},
@@ -5925,6 +5929,9 @@
     {"allow-dsp-based-agc", flag_descriptions::kCrOSDspBasedAgcAllowedName,
      flag_descriptions::kCrOSDspBasedAgcAllowedDescription, kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(features::kCrOSDspBasedAgcAllowed)},
+    {"enable-cros-privacy-hub", flag_descriptions::kCrosPrivacyHubName,
+     flag_descriptions::kCrosPrivacyHubDescription, kOsCrOS | kOsLacros,
+     FEATURE_VALUE_TYPE(features::kCrosPrivacyHub)},
     {"enforce-system-aec", flag_descriptions::kCrOSEnforceSystemAecName,
      flag_descriptions::kCrOSEnforceSystemAecDescription, kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAec)},
@@ -6602,10 +6609,6 @@
      FEATURE_VALUE_TYPE(
          chrome::android::kContextMenuSearchAndShopWithGoogleLens)},
 
-    {"google-lens-sdk-intent", flag_descriptions::kGoogleLensSdkIntentName,
-     flag_descriptions::kGoogleLensSdkIntentDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kGoogleLensSdkIntent)},
-
     {"lens-camera-assisted-search",
      flag_descriptions::kLensCameraAssistedSearchName,
      flag_descriptions::kLensCameraAssistedSearchDescription, kOsAndroid,
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 8c77d93..cc88bf2 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -10,6 +10,7 @@
   "+components/browser_ui/sms/android",
   "+components/browser_ui/util/android/url_constants.h",
   "+components/query_tiles",
+  "+components/url_param_filter/content",
   "+components/webapk",
   "+device/vr/buildflags/buildflags.h",
   "+media/gpu",
diff --git a/chrome/browser/android/url_param_filter/cross_otr_observer_android.cc b/chrome/browser/android/url_param_filter/cross_otr_observer_android.cc
index b366d64..906f445a 100644
--- a/chrome/browser/android/url_param_filter/cross_otr_observer_android.cc
+++ b/chrome/browser/android/url_param_filter/cross_otr_observer_android.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/android/url_param_filter/cross_otr_observer_android.h"
 
+#include "components/url_param_filter/content/cross_otr_observer.h"
+
 namespace url_param_filter {
 void MaybeCreateCrossOtrObserverForTabLaunchType(
     content::WebContents* web_contents,
diff --git a/chrome/browser/android/url_param_filter/cross_otr_observer_android.h b/chrome/browser/android/url_param_filter/cross_otr_observer_android.h
index 6ac4d0b..9d8b097 100644
--- a/chrome/browser/android/url_param_filter/cross_otr_observer_android.h
+++ b/chrome/browser/android/url_param_filter/cross_otr_observer_android.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_ANDROID_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_ANDROID_H_
 
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
 
 namespace url_param_filter {
 
diff --git a/chrome/browser/android/url_param_filter/cross_otr_observer_android_unittest.cc b/chrome/browser/android/url_param_filter/cross_otr_observer_android_unittest.cc
index ea97445..0c195d5 100644
--- a/chrome/browser/android/url_param_filter/cross_otr_observer_android_unittest.cc
+++ b/chrome/browser/android/url_param_filter/cross_otr_observer_android_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/android/url_param_filter/cross_otr_observer_android.h"
 
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 21594c2..623d048 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -1589,7 +1589,8 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(OobeSpokenFeedbackTest, SpokenFeedbackInOobe) {
+// TODO(crbug.com/1310682) - Re-enable this test.
+IN_PROC_BROWSER_TEST_P(OobeSpokenFeedbackTest, DISABLED_SpokenFeedbackInOobe) {
   ui_controls::EnableUIControls();
   ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
   AccessibilityManager::Get()->EnableSpokenFeedbackWithTutorial();
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
index fe4d120..71eda08 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
@@ -37,6 +37,26 @@
   kOther = 2,
 };
 
+enum class CpuRestrictionVmResult {
+  // Successfully set/reset CPU restrictions in ARCVM.
+  kSuccess = 0,
+  // Other failure reason.
+  kOther = 1,
+  // VM concierge service is not available.
+  kNoConciergeService = 2,
+  // VM concierge client is not available.
+  kNoConciergeClient = 3,
+  // VM Concierge did not respond.
+  kConciergeDidNotRespond = 4,
+
+  // Note: kMaxValue is needed only for histograms.
+  kMaxValue = kConciergeDidNotRespond,
+};
+
+void RecordCpuRestrictionVMResult(CpuRestrictionVmResult result) {
+  base::UmaHistogramEnumeration("Arc.CpuRestrictionVmResult", result);
+}
+
 // Checks all the |observers| for active ones to find out the reason why the
 // instance is being unthrottled.
 // This function can only be called when the instance is being unthrottled.
@@ -73,10 +93,16 @@
     absl::optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response) {
   if (!response) {
     LOG(ERROR) << "Failed to call SetVmCpuRestriction";
+    RecordCpuRestrictionVMResult(
+        CpuRestrictionVmResult::kConciergeDidNotRespond);
     return;
   }
-  if (!response->success())
+  if (response->success()) {
+    RecordCpuRestrictionVMResult(CpuRestrictionVmResult::kSuccess);
+  } else {
     LOG(ERROR) << "SetVmCpuRestriction for ARCVM failed";
+    RecordCpuRestrictionVMResult(CpuRestrictionVmResult::kOther);
+  }
 }
 
 void SetArcVmCpuRestrictionImpl(
@@ -85,12 +111,16 @@
   if (!service_is_available) {
     LOG(ERROR)
         << "vm_concierge is not available. ArcInstanceThrottle won't work.";
+    RecordCpuRestrictionVMResult(CpuRestrictionVmResult::kNoConciergeService);
     return;
   }
 
   auto* const client = ash::ConciergeClient::Get();
+  // TODO(khmel): This should never be possible. Confirm via histogram and
+  // change to DCHECK.
   if (!client) {
     LOG(ERROR) << "ConciergeClient is not available";
+    RecordCpuRestrictionVMResult(CpuRestrictionVmResult::kNoConciergeClient);
     return;
   }
 
@@ -101,8 +131,11 @@
 void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state,
                             bool use_quota) {
   auto* const client = ash::ConciergeClient::Get();
+  // TODO(khmel): This should never be possible. Confirm via histogram and
+  // change to DCHECK.
   if (!client) {
     LOG(ERROR) << "ConciergeClient is not available";
+    RecordCpuRestrictionVMResult(CpuRestrictionVmResult::kNoConciergeClient);
     return;
   }
 
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
index cee0012..212ba58 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
@@ -22,6 +22,7 @@
 #include "ash/components/arc/test/fake_power_instance.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/ash/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
 #include "chrome/browser/ash/arc/instance_throttle/arc_boot_phase_throttle_observer.h"
 #include "chrome/browser/ash/arc/instance_throttle/arc_power_throttle_observer.h"
@@ -30,6 +31,7 @@
 #include "chrome/browser/ash/throttle_observer.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
+#include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "components/arc/test/fake_intent_helper_host.h"
@@ -368,4 +370,118 @@
   EXPECT_EQ(0, power_instance()->cpu_restriction_state_count());
 }
 
+class ArcInstanceThrottleVMTest : public testing::Test {
+ public:
+  ArcInstanceThrottleVMTest() = default;
+  ~ArcInstanceThrottleVMTest() override = default;
+
+  explicit ArcInstanceThrottleVMTest(const ArcInstanceThrottleTest&) = delete;
+  ArcInstanceThrottleVMTest& operator=(const ArcInstanceThrottleTest&) = delete;
+
+  void SetUp() override {
+    auto* command_line = base::CommandLine::ForCurrentProcess();
+    command_line->InitFromArgv({"", "--enable-arcvm"});
+
+    SetArcAvailableCommandLineForTesting(command_line);
+
+    run_loop_ = std::make_unique<base::RunLoop>();
+
+    // Need to initialize DBusThreadManager before ArcSessionManager's
+    // constructor calls DBusThreadManager::Get().
+    chromeos::DBusThreadManager::Initialize();
+    chromeos::ConciergeClient::InitializeFake();
+    DCHECK(GetConciergeClient());
+
+    arc_service_manager_ = std::make_unique<ArcServiceManager>();
+    arc_session_manager_ =
+        CreateTestArcSessionManager(std::make_unique<ArcSessionRunner>(
+            base::BindRepeating(FakeArcSession::Create)));
+    testing_profile_ = std::make_unique<TestingProfile>();
+
+    arc_instance_throttle_ =
+        ArcInstanceThrottle::GetForBrowserContextForTesting(
+            testing_profile_.get());
+
+    run_loop()->RunUntilIdle();
+  }
+
+  void TearDown() override {
+    testing_profile_.reset();
+    arc_session_manager_.reset();
+    arc_service_manager_.reset();
+    chromeos::DBusThreadManager::Shutdown();
+  }
+
+ protected:
+  ash::FakeConciergeClient* GetConciergeClient() {
+    return ash::FakeConciergeClient::Get();
+  }
+
+  ash::ThrottleObserver* GetThrottleObserver() {
+    for (const auto& observer :
+         arc_instance_throttle_->observers_for_testing()) {
+      if (observer->name() == kArcPowerThrottleObserverName)
+        return observer.get();
+    }
+    NOTREACHED();
+    return nullptr;
+  }
+
+  base::RunLoop* run_loop() { return run_loop_.get(); }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  std::unique_ptr<ArcServiceManager> arc_service_manager_;
+  std::unique_ptr<ArcSessionManager> arc_session_manager_;
+  std::unique_ptr<TestingProfile> testing_profile_;
+
+  ArcInstanceThrottle* arc_instance_throttle_;
+};
+
+TEST_F(ArcInstanceThrottleVMTest, Histograms) {
+  constexpr char kHistogramName[] = "Arc.CpuRestrictionVmResult";
+  base::HistogramTester histogram_tester;
+
+  auto* const client = GetConciergeClient();
+  auto* const observer = GetThrottleObserver();
+
+  // No service
+  client->set_wait_for_service_to_be_available_response(false);
+  observer->SetActive(true);
+  run_loop()->RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kHistogramName, 1);
+  histogram_tester.ExpectBucketCount(kHistogramName,
+                                     2 /* kNoConciergeService */, 1);
+
+  // No response
+  client->set_wait_for_service_to_be_available_response(true);
+  absl::optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response;
+  client->set_set_vm_cpu_restriction_response(response);
+  observer->SetActive(false);
+  run_loop()->RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kHistogramName, 2);
+  histogram_tester.ExpectBucketCount(kHistogramName,
+                                     4 /* kConciergeDidNotRespond */, 1);
+
+  // Failure
+  response = vm_tools::concierge::SetVmCpuRestrictionResponse();
+  response->set_success(false);
+  client->set_set_vm_cpu_restriction_response(response);
+  observer->SetActive(true);
+  run_loop()->RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kHistogramName, 3);
+  histogram_tester.ExpectBucketCount(kHistogramName, 1 /* kOther */, 1);
+
+  // Success
+  response = vm_tools::concierge::SetVmCpuRestrictionResponse();
+  response->set_success(true);
+  client->set_set_vm_cpu_restriction_response(response);
+  observer->SetActive(false);
+  run_loop()->RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kHistogramName, 4);
+  histogram_tester.ExpectBucketCount(kHistogramName, 0 /* kSuccess */, 1);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 8cc7b92..73aa5e3 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -641,7 +641,8 @@
     LOG(ERROR) << "BrowserService was disconnected";
     return;
   }
-  browser_service_->service->NewTab(base::DoNothing());
+  browser_service_->service->NewTab(should_trigger_session_restore,
+                                    base::DoNothing());
 }
 
 void BrowserManager::OpenUrl(const GURL& url,
diff --git a/chrome/browser/ash/crosapi/file_system_provider_service_ash.cc b/chrome/browser/ash/crosapi/file_system_provider_service_ash.cc
index b119292..d682029 100644
--- a/chrome/browser/ash/crosapi/file_system_provider_service_ash.cc
+++ b/chrome/browser/ash/crosapi/file_system_provider_service_ash.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/ash/crosapi/file_system_provider_service_ash.h"
 
 #include "base/numerics/safe_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/types/expected.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
+#include "chrome/browser/ash/file_system_provider/request_manager.h"
 #include "chrome/browser/ash/file_system_provider/service.h"
 #include "chrome/browser/chromeos/extensions/file_system_provider/provider_function.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -16,12 +19,73 @@
 using ash::file_system_provider::ProvidedFileSystemInterface;
 using ash::file_system_provider::ProvidedFileSystemObserver;
 using ash::file_system_provider::ProviderId;
+using ash::file_system_provider::RequestValue;
 using ash::file_system_provider::Service;
 using ash::file_system_provider::Watchers;
 
 namespace crosapi {
 namespace {
 
+constexpr char kDeserializationError[] = "deserialization error";
+
+// Either returns a valid request manager or else an error string.
+base::expected<ash::file_system_provider::RequestManager*, std::string>
+GetRequestManager(Profile* profile,
+                  const mojom::FileSystemIdPtr& file_system_id) {
+  Service* service = Service::Get(profile);
+  if (!service) {
+    return base::unexpected("File system provider service not found.");
+  }
+
+  ProvidedFileSystemInterface* file_system = service->GetProvidedFileSystem(
+      ProviderId::CreateFromExtensionId(file_system_id->provider),
+      file_system_id->id);
+  if (!file_system) {
+    return base::unexpected(
+        extensions::FileErrorToString(base::File::FILE_ERROR_NOT_FOUND));
+  }
+
+  return file_system->GetRequestManager();
+}
+
+// Forwards an operation response from an extension to the request manager and
+// then returns the error message. Empty string means success.
+std::string ForwardOperationResponse(mojom::FileSystemIdPtr file_system_id,
+                                     int64_t request_id,
+                                     std::unique_ptr<RequestValue> value,
+                                     bool has_more,
+                                     Profile* profile) {
+  auto manager = GetRequestManager(profile, file_system_id);
+  if (!manager.has_value())
+    return manager.error();
+
+  const base::File::Error result =
+      manager.value()->FulfillRequest(request_id, std::move(value), has_more);
+  if (result != base::File::FILE_OK) {
+    return extensions::FileErrorToString(result);
+  }
+  return "";
+}
+
+// Forwards an operation failure from an extension to the request manager and
+// then returns the error message. Empty string means success.
+std::string ForwardOperationFailure(mojom::FileSystemIdPtr file_system_id,
+                                    int64_t request_id,
+                                    std::unique_ptr<RequestValue> value,
+                                    base::File::Error error,
+                                    Profile* profile) {
+  auto manager = GetRequestManager(profile, file_system_id);
+  if (!manager.has_value())
+    return manager.error();
+
+  const base::File::Error result =
+      manager.value()->RejectRequest(request_id, std::move(value), error);
+  if (result != base::File::FILE_OK) {
+    return extensions::FileErrorToString(result);
+  }
+  return "";
+}
+
 // Convert |result| to a string, empty string for success and invokes
 // |callback|.
 void RunErrorCallback(base::OnceCallback<void(const std::string&)> callback,
@@ -170,6 +234,17 @@
                     ProfileManager::GetPrimaryUserProfile());
 }
 
+void FileSystemProviderServiceAsh::OperationFinished(
+    mojom::FSPOperationResponse response,
+    mojom::FileSystemIdPtr file_system_id,
+    int64_t request_id,
+    std::vector<base::Value> args,
+    OperationFinishedCallback callback) {
+  OperationFinishedWithProfile(response, std::move(file_system_id), request_id,
+                               std::move(args), std::move(callback),
+                               ProfileManager::GetPrimaryUserProfile());
+}
+
 void FileSystemProviderServiceAsh::MountWithProfile(
     mojom::FileSystemMetadataPtr metadata,
     bool persistent,
@@ -268,4 +343,122 @@
                       base::BindOnce(&RunErrorCallback, std::move(callback)));
 }
 
+void FileSystemProviderServiceAsh::OperationFinishedWithProfile(
+    mojom::FSPOperationResponse response,
+    mojom::FileSystemIdPtr file_system_id,
+    int64_t request_id,
+    std::vector<base::Value> args,
+    OperationFinishedCallback callback,
+    Profile* profile) {
+  std::string error;
+  switch (response) {
+    case mojom::FSPOperationResponse::kUnknown:
+      error = "unknown operation response";
+      break;
+    case mojom::FSPOperationResponse::kUnmountSuccess: {
+      using extensions::api::file_system_provider_internal::
+          UnmountRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      auto value = RequestValue::CreateForUnmountSuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), /*has_more=*/false,
+                                       profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kGetEntryMetadataSuccess: {
+      using extensions::api::file_system_provider_internal::
+          GetMetadataRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      auto value = RequestValue::CreateForGetMetadataSuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), /*has_more=*/false,
+                                       profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kGetActionsSuccess: {
+      using extensions::api::file_system_provider_internal::
+          GetActionsRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      auto value = RequestValue::CreateForGetActionsSuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), /*has_more=*/false,
+                                       profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kReadDirectorySuccess: {
+      using extensions::api::file_system_provider_internal::
+          ReadDirectoryRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      bool has_more = params->has_more;
+      auto value =
+          RequestValue::CreateForReadDirectorySuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), has_more, profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kReadFileSuccess: {
+      TRACE_EVENT0("file_system_provider", "ReadFileSuccessWithProfile");
+      using extensions::api::file_system_provider_internal::
+          ReadFileRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      bool has_more = params->has_more;
+      auto value = RequestValue::CreateForReadFileSuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), has_more, profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kGenericSuccess: {
+      using extensions::api::file_system_provider_internal::
+          OperationRequestedSuccess::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      auto value = RequestValue::CreateForOperationSuccess(std::move(params));
+      error = ForwardOperationResponse(std::move(file_system_id), request_id,
+                                       std::move(value), /*has_more=*/false,
+                                       profile);
+      break;
+    }
+    case mojom::FSPOperationResponse::kGenericFailure: {
+      using extensions::api::file_system_provider_internal::
+          OperationRequestedError::Params;
+      std::unique_ptr<Params> params = Params::Create(std::move(args));
+      if (!params) {
+        error = kDeserializationError;
+        break;
+      }
+      base::File::Error operation_error =
+          extensions::ProviderErrorToFileError(params->error);
+      auto value = RequestValue::CreateForOperationError(std::move(params));
+      error =
+          ForwardOperationFailure(std::move(file_system_id), request_id,
+                                  std::move(value), operation_error, profile);
+      break;
+    }
+  }
+  std::move(callback).Run(std::move(error));
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/file_system_provider_service_ash.h b/chrome/browser/ash/crosapi/file_system_provider_service_ash.h
index f1c76595..a9eaf4e 100644
--- a/chrome/browser/ash/crosapi/file_system_provider_service_ash.h
+++ b/chrome/browser/ash/crosapi/file_system_provider_service_ash.h
@@ -40,6 +40,11 @@
               mojom::FSPChangeType type,
               std::vector<mojom::FSPChangePtr> changes,
               NotifyCallback callback) override;
+  void OperationFinished(mojom::FSPOperationResponse response,
+                         mojom::FileSystemIdPtr file_system_id,
+                         int64_t request_id,
+                         std::vector<base::Value> args,
+                         OperationFinishedCallback callback) override;
 
   // In order to support multi-login in ash, a legacy feature that is going
   // away in Lacros, all methods above are redirected to a variation that
@@ -63,6 +68,12 @@
                          std::vector<mojom::FSPChangePtr> changes,
                          NotifyCallback callback,
                          Profile* profile);
+  void OperationFinishedWithProfile(mojom::FSPOperationResponse response,
+                                    mojom::FileSystemIdPtr file_system_id,
+                                    int64_t request_id,
+                                    std::vector<base::Value> args,
+                                    OperationFinishedCallback callback,
+                                    Profile* profile);
 };
 
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
index 9fff7bd..36880e61 100644
--- a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
+++ b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
@@ -82,7 +82,8 @@
   void NewFullscreenWindow(const GURL& url,
                            NewFullscreenWindowCallback callback) override {}
   void NewGuestWindow(NewGuestWindowCallback callback) override {}
-  void NewTab(NewTabCallback callback) override {}
+  void NewTab(bool should_trigger_session_restore,
+              NewTabCallback callback) override {}
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
                OpenUrlCallback callback) override {}
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index c127f66..61d6a67 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -1706,42 +1706,6 @@
     ::testing::Values(
         TestCase("recentsA11yMessages").EnableFiltersInRecents(),
         TestCase("recentsA11yMessages").EnableFiltersInRecents().FilesSwa(),
-        TestCase("recentsAllowCut")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsAllowCut")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
-        TestCase("recentsAllowDeletion")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsAllowDeletion")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
-        TestCase("recentsAllowMultipleFilesDeletion")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsAllowMultipleFilesDeletion")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
-        TestCase("recentsAllowRename")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsAllowRename")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
         TestCase("recentsDownloads"),
         TestCase("recentsDownloads").FilesSwa(),
         TestCase("recentsDownloads").EnableFiltersInRecents(),
@@ -1788,15 +1752,6 @@
         TestCase("recentsNested").FilesSwa(),
         TestCase("recentsNested").EnableFiltersInRecents(),
         TestCase("recentsNested").EnableFiltersInRecents().FilesSwa(),
-        TestCase("recentsNoRenameForPlayFiles")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsNoRenameForPlayFiles")
-            .EnableArc()
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
         TestCase("recentsPlayFiles").EnableArc(),
         TestCase("recentsPlayFiles").EnableArc().FilesSwa(),
         TestCase("recentsPlayFiles").EnableArc().EnableFiltersInRecents(),
@@ -1804,13 +1759,6 @@
             .EnableArc()
             .EnableFiltersInRecents()
             .FilesSwa(),
-        TestCase("recentsReadOnlyHidden")
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2(),
-        TestCase("recentsReadOnlyHidden")
-            .EnableFiltersInRecents()
-            .EnableFiltersInRecentsV2()
-            .FilesSwa(),
         TestCase("recentAudioDownloads"),
         TestCase("recentAudioDownloads").FilesSwa(),
         TestCase("recentAudioDownloads").EnableFiltersInRecents(),
diff --git a/chrome/browser/ash/login/debug_overlay_browsertest.cc b/chrome/browser/ash/login/debug_overlay_browsertest.cc
index 834b2bc..551ebfd 100644
--- a/chrome/browser/ash/login/debug_overlay_browsertest.cc
+++ b/chrome/browser/ash/login/debug_overlay_browsertest.cc
@@ -21,10 +21,8 @@
 constexpr char kDebugOverlay[] = "debuggerOverlay";
 constexpr char kScreensPanel[] = "DebuggerPanelScreens";
 
-// TODO(crbug.com/1261902): Decrease |kOobeScreensCount| and
-// |kLoginScreensCount| by 1.
-constexpr int kOobeScreensCount = 45;
-constexpr int kLoginScreensCount = 41;
+constexpr int kOobeScreensCount = 44;
+constexpr int kLoginScreensCount = 40;
 constexpr int kOsInstallScreensCount = 2;
 
 std::string ElementsInPanel(const std::string& panel) {
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
index eddcca4..5ffa8dd 100644
--- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -264,18 +264,17 @@
 // HandleArcTermsOfServiceScreen.
 void HandleRecommendAppsScreen() {
   OobeScreenWaiter(RecommendAppsScreenView::kScreenId).Wait();
-  LOG(INFO)
-      << "OobeInteractiveUITest: Switched to 'recommend-apps-old' screen.";
+  LOG(INFO) << "OobeInteractiveUITest: Switched to 'recommend-apps' screen.";
 
   EXPECT_FALSE(LoginScreenTestApi::IsShutdownButtonShown());
   EXPECT_FALSE(LoginScreenTestApi::IsGuestButtonShown());
   EXPECT_FALSE(LoginScreenTestApi::IsAddUserButtonShown());
 
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"recommend-apps-old", "appsDialog"})
+      .CreateVisibilityWaiter(true, {"recommend-apps", "appsDialog"})
       ->Wait();
 
-  test::OobeJS().ExpectPathDisplayed(true, {"recommend-apps-old", "appView"});
+  test::OobeJS().ExpectPathDisplayed(true, {"recommend-apps", "appView"});
 
   std::string toggle_apps_script = base::StringPrintf(
       "(function() {"
@@ -293,7 +292,7 @@
       "test.package");
 
   const std::string webview_path =
-      test::GetOobeElementPath({"recommend-apps-old", "appView"});
+      test::GetOobeElementPath({"recommend-apps", "appView"});
   const std::string script = base::StringPrintf(
       "(function() {"
       "  var toggleApp = function() {"
@@ -315,12 +314,12 @@
   EXPECT_TRUE(result);
 
   const std::initializer_list<base::StringPiece> install_button = {
-      "recommend-apps-old", "installButton"};
+      "recommend-apps", "installButton"};
   test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
   test::OobeJS().TapOnPath(install_button);
 
   OobeScreenExitWaiter(RecommendAppsScreenView::kScreenId).Wait();
-  LOG(INFO) << "OobeInteractiveUITest: 'recommend-apps-old' screen done.";
+  LOG(INFO) << "OobeInteractiveUITest: 'recommend-apps' screen done.";
 }
 
 // Waits for AppDownloadingScreen to be shown, clicks 'Continue' button, and
diff --git a/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc b/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc
index c443375..3ab81bd 100644
--- a/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc
+++ b/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc
@@ -482,6 +482,8 @@
   // Wait for proxy login handler and authenticate.
   WaitForLoginHandler();
   ASSERT_TRUE(login_handler());
+  ASSERT_EQ(login_handler()->web_contents()->GetOuterWebContents(),
+            reauth_dialog_helper->DialogWebContents());
   login_handler()->SetAuth(u"foo", u"bar");
 
   reauth_dialog_helper->WaitForIdpPageLoad();
@@ -516,6 +518,8 @@
   // Appearance of login handler means that proxy authentication was requested
   WaitForLoginHandler();
   ASSERT_TRUE(login_handler());
+  ASSERT_EQ(login_handler()->web_contents()->GetOuterWebContents(),
+            reauth_dialog_helper->DialogWebContents());
 
   content::WindowedNotificationObserver auth_cancelled_waiter(
       chrome::NOTIFICATION_AUTH_CANCELLED,
diff --git a/chrome/browser/ash/login/screens/recommend_apps_screen.cc b/chrome/browser/ash/login/screens/recommend_apps_screen.cc
index 26a5ef0..6fc82a3 100644
--- a/chrome/browser/ash/login/screens/recommend_apps_screen.cc
+++ b/chrome/browser/ash/login/screens/recommend_apps_screen.cc
@@ -77,7 +77,8 @@
 }
 
 void RecommendAppsScreen::ShowImpl() {
-  view_->Show();
+  if (view_)
+    view_->Show();
 
   recommend_apps_fetcher_ = RecommendAppsFetcher::Create(this);
   recommend_apps_fetcher_->Start();
diff --git a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
index 2d7cbd57..47ca3a8 100644
--- a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
@@ -41,9 +41,11 @@
 namespace ash {
 namespace {
 
-const test::UIPath webview_ui_path = {"recommend-apps-old", "appView"};
-const test::UIPath install_button = {"recommend-apps-old", "installButton"};
-const test::UIPath skip_button = {"recommend-apps-old", "skipButton"};
+constexpr char kRecommendAppsId[] = "recommend-apps";
+
+const test::UIPath webview_ui_path = {kRecommendAppsId, "appView"};
+const test::UIPath install_button = {kRecommendAppsId, "installButton"};
+const test::UIPath skip_button = {kRecommendAppsId, "skipButton"};
 
 struct FakeAppInfo {
  public:
@@ -150,17 +152,17 @@
   void ExpectLoadingStep() {
     // Wait for loading screen.
     test::OobeJS()
-        .CreateVisibilityWaiter(true, {"recommend-apps-old", "loadingDialog"})
+        .CreateVisibilityWaiter(true, {kRecommendAppsId, "loadingDialog"})
         ->Wait();
 
-    test::OobeJS().ExpectHiddenPath({"recommend-apps-old", "appsDialog"});
+    test::OobeJS().ExpectHiddenPath({kRecommendAppsId, "appsDialog"});
   }
 
   void ExpectAppSelectionStep() {
     test::OobeJS()
-        .CreateVisibilityWaiter(true, {"recommend-apps-old", "appsDialog"})
+        .CreateVisibilityWaiter(true, {kRecommendAppsId, "appsDialog"})
         ->Wait();
-    test::OobeJS().ExpectHiddenPath({"recommend-apps-old", "loadingDialog"});
+    test::OobeJS().ExpectHiddenPath({kRecommendAppsId, "loadingDialog"});
   }
 
   bool WaitForAppListSize(const std::string& webview_path, int app_count) {
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 3ce9f3e6..81908ba 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -295,8 +295,7 @@
     {chromeos::ArcTermsOfServiceScreenView::kScreenId, "arc_tos"},
     {chromeos::EnrollmentScreenView::kScreenId, "enroll"},
     {chromeos::WelcomeView::kScreenId, "network"},
-    {chromeos::TermsOfServiceScreenView::kScreenId, "tos"},
-    {chromeos::RecommendAppsScreenView::kScreenId, "recommend-apps"}};
+    {chromeos::TermsOfServiceScreenView::kScreenId, "tos"}};
 
 void RecordUMAHistogramForOOBEStepShownStatus(
     OobeScreenId screen,
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 50bbb02..0001146 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -14,7 +14,7 @@
 #include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_pref_manager.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "base/callback_helpers.h"
@@ -120,8 +120,7 @@
 TestingPrefServiceSimple* RegisterPrefs(TestingPrefServiceSimple* local_state) {
   ash::device_settings_cache::RegisterPrefs(local_state->registry());
   user_manager::KnownUser::RegisterPrefs(local_state->registry());
-  ash::WallpaperControllerImpl::RegisterLocalStatePrefs(
-      local_state->registry());
+  ash::WallpaperPrefManager::RegisterLocalStatePrefs(local_state->registry());
   policy::DeviceWallpaperImageExternalDataHandler::RegisterPrefs(
       local_state->registry());
   ProfileAttributesStorage::RegisterPrefs(local_state->registry());
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
index d71361b..0fc8268d 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
@@ -11,10 +11,9 @@
 void ApcExternalActionDelegate::OnActionRequested(
     const autofill_assistant::external::Action& action_info,
     base::OnceCallback<void()> start_dom_checks_callback,
-    base::OnceCallback<
-        void(autofill_assistant::ExternalActionDelegate::ActionResult)>
+    base::OnceCallback<void(const autofill_assistant::external::Result& result)>
         end_action_callback) {
-  autofill_assistant::ExternalActionDelegate::ActionResult result;
-  result.success = true;
+  autofill_assistant::external::Result result;
+  result.set_success(true);
   std::move(end_action_callback).Run(result);
 }
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
index 079376cf4..b4decb3 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
@@ -20,9 +20,8 @@
   void OnActionRequested(
       const autofill_assistant::external::Action& action_info,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<
-          void(autofill_assistant::ExternalActionDelegate::ActionResult)>
-          end_action_callback) override;
+      base::OnceCallback<void(const autofill_assistant::external::Result&
+                                  result)> end_action_callback) override;
 };
 
 #endif  // CHROME_BROWSER_AUTOFILL_ASSISTANT_PASSWORD_CHANGE_APC_EXTERNAL_ACTION_DELEGATE_H_
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 1a4688f..412be33 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -49,7 +49,6 @@
 #include "chrome/services/speech/buildflags/buildflags.h"
 #include "chromeos/components/chromebox_for_meetings/buildflags/buildflags.h"
 #include "components/browsing_topics/mojom/browsing_topics_internals.mojom.h"
-#include "components/contextual_search/buildflags.h"
 #include "components/dom_distiller/content/browser/distillability_driver.h"
 #include "components/dom_distiller/content/browser/distiller_javascript_service_impl.h"
 #include "components/dom_distiller/content/common/mojom/distillability_service.mojom.h"
@@ -131,8 +130,6 @@
 #include "chrome/browser/ui/webui/feed_internals/feed_internals.mojom.h"
 #include "chrome/browser/ui/webui/feed_internals/feed_internals_ui.h"
 #include "chrome/common/offline_page_auto_fetcher.mojom.h"
-#include "components/contextual_search/content/browser/contextual_search_js_api_service_impl.h"
-#include "components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/digital_goods/digital_goods.mojom.h"
 #include "third_party/blink/public/mojom/installedapp/installed_app_provider.mojom.h"
@@ -361,26 +358,6 @@
 }
 #endif  // BUILDFLAG(ENABLE_UNHANDLED_TAP)
 
-#if BUILDFLAG(BUILD_CONTEXTUAL_SEARCH)
-void BindContextualSearchObserver(
-    content::RenderFrameHost* const host,
-    mojo::PendingReceiver<
-        contextual_search::mojom::ContextualSearchJsApiService> receiver) {
-  // Early return if the RenderFrameHost's delegate is not a WebContents.
-  auto* web_contents = content::WebContents::FromRenderFrameHost(host);
-  if (!web_contents)
-    return;
-
-  auto* contextual_search_observer =
-      contextual_search::ContextualSearchObserver::FromWebContents(
-          web_contents);
-  if (contextual_search_observer) {
-    contextual_search::CreateContextualSearchJsApiService(
-        contextual_search_observer->api_handler(), std::move(receiver));
-  }
-}
-#endif  // BUILDFLAG(BUILD_CONTEXTUAL_SEARCH)
-
 // Forward image Annotator requests to the profile's AccessibilityLabelsService.
 void BindImageAnnotator(
     content::RenderFrameHost* frame_host,
@@ -695,15 +672,10 @@
   map->Add<blink::mojom::ShareService>(base::BindRepeating(
       &ForwardToJavaWebContents<blink::mojom::ShareService>));
 
-#if BUILDFLAG(BUILD_CONTEXTUAL_SEARCH)
-  map->Add<contextual_search::mojom::ContextualSearchJsApiService>(
-      base::BindRepeating(&BindContextualSearchObserver));
-
 #if BUILDFLAG(ENABLE_UNHANDLED_TAP)
   map->Add<blink::mojom::UnhandledTapNotifier>(
       base::BindRepeating(&BindUnhandledTapWebContentsObserver));
 #endif  // BUILDFLAG(ENABLE_UNHANDLED_TAP)
-#endif  // BUILDFLAG(BUILD_CONTEXTUAL_SEARCH)
 
 #else
   map->Add<blink::mojom::BadgeService>(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9afc0228..69ab674 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -146,7 +146,6 @@
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
 #include "chrome/browser/universal_web_contents_observers.h"
-#include "chrome/browser/url_param_filter/url_param_filter_throttle.h"
 #include "chrome/browser/usb/frame_usb_services.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/browser/webapps/web_app_offline.h"
@@ -243,6 +242,7 @@
 #include "components/site_isolation/site_isolation_policy.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/translate/core/common/translate_switches.h"
+#include "components/url_param_filter/content/url_param_filter_throttle.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_switches.h"
 #include "content/public/browser/browser_child_process_host.h"
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
index 1a24188..13221a8 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
@@ -295,9 +295,9 @@
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  return FulfillRequest(
-      RequestValue::CreateForUnmountSuccess(std::move(params)),
-      false /* has_more */);
+  ForwardOperationResult(params, mutable_args(),
+                         crosapi::mojom::FSPOperationResponse::kUnmountSuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -306,9 +306,10 @@
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  return FulfillRequest(
-      RequestValue::CreateForGetMetadataSuccess(std::move(params)),
-      false /* has_more */);
+  ForwardOperationResult(
+      params, mutable_args(),
+      crosapi::mojom::FSPOperationResponse::kGetEntryMetadataSuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -316,10 +317,10 @@
   using api::file_system_provider_internal::GetActionsRequestedSuccess::Params;
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
-
-  return FulfillRequest(
-      RequestValue::CreateForGetActionsSuccess(std::move(params)),
-      false /* has_more */);
+  ForwardOperationResult(
+      params, mutable_args(),
+      crosapi::mojom::FSPOperationResponse::kGetActionsSuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -328,10 +329,10 @@
       Params;
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
-
-  const bool has_more = params->has_more;
-  return FulfillRequest(
-      RequestValue::CreateForReadDirectorySuccess(std::move(params)), has_more);
+  ForwardOperationResult(
+      params, mutable_args(),
+      crosapi::mojom::FSPOperationResponse::kReadDirectorySuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -339,12 +340,13 @@
   TRACE_EVENT0("file_system_provider", "ReadFileRequestedSuccess");
   using api::file_system_provider_internal::ReadFileRequestedSuccess::Params;
 
+  // TODO(https://crbug.com/1314397): Improve performance by removing copy.
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
-
-  const bool has_more = params->has_more;
-  return FulfillRequest(
-      RequestValue::CreateForReadFileSuccess(std::move(params)), has_more);
+  ForwardOperationResult(
+      params, mutable_args(),
+      crosapi::mojom::FSPOperationResponse::kReadFileSuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -353,10 +355,9 @@
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  return FulfillRequest(
-      std::unique_ptr<RequestValue>(
-          RequestValue::CreateForOperationSuccess(std::move(params))),
-      false /* has_more */);
+  ForwardOperationResult(params, mutable_args(),
+                         crosapi::mojom::FSPOperationResponse::kGenericSuccess);
+  return RespondLater();
 }
 
 ExtensionFunction::ResponseAction
@@ -370,9 +371,9 @@
     return ValidationFailure(this);
   }
 
-  const base::File::Error error = ProviderErrorToFileError(params->error);
-  return RejectRequest(RequestValue::CreateForOperationError(std::move(params)),
-                       error);
+  ForwardOperationResult(params, mutable_args(),
+                         crosapi::mojom::FSPOperationResponse::kGenericFailure);
+  return RespondLater();
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
index c782b17f..c47272a 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
@@ -5,7 +5,11 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_FILE_SYSTEM_PROVIDER_API_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_FILE_SYSTEM_PROVIDER_API_H_
 
+#include "chrome/browser/ash/crosapi/crosapi_ash.h"
+#include "chrome/browser/ash/crosapi/crosapi_manager.h"
+#include "chrome/browser/ash/crosapi/file_system_provider_service_ash.h"
 #include "chrome/browser/chromeos/extensions/file_system_provider/provider_function.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chromeos/crosapi/mojom/file_system_provider.mojom.h"
 #include "extensions/browser/extension_function.h"
 
@@ -72,8 +76,44 @@
   void OnNotifyCompleted(base::File::Error result);
 };
 
+class FileSystemProviderInternal : public FileSystemProviderBase {
+ protected:
+  ~FileSystemProviderInternal() override {}
+
+  // Returns the operation metadata from FileSystemProviderInternal methods via
+  // output parameters.
+  template <typename Params>
+  void GetOperationMetadata(const Params& params,
+                            crosapi::mojom::FileSystemIdPtr* file_system_id,
+                            int64_t* request_id) {
+    *file_system_id = crosapi::mojom::FileSystemId::New();
+    (*file_system_id)->provider = extension_id();
+    (*file_system_id)->id = params->file_system_id;
+    *request_id = params->request_id;
+  }
+
+  // Forwards the result of the operation to the file system provider service.
+  template <typename Params>
+  void ForwardOperationResult(const Params& params,
+                              std::vector<base::Value>& args,
+                              crosapi::mojom::FSPOperationResponse response) {
+    crosapi::mojom::FileSystemIdPtr file_system_id;
+    int64_t request_id;
+    GetOperationMetadata(params, &file_system_id, &request_id);
+    auto callback =
+        base::BindOnce(&FileSystemProviderInternal::RespondWithError, this);
+    crosapi::CrosapiManager::Get()
+        ->crosapi_ash()
+        ->file_system_provider_service_ash()
+        ->OperationFinishedWithProfile(
+            response, std::move(file_system_id), request_id, std::move(args),
+            std::move(callback),
+            Profile::FromBrowserContext(browser_context()));
+  }
+};
+
 class FileSystemProviderInternalUnmountRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.unmountRequestedSuccess",
@@ -85,7 +125,7 @@
 };
 
 class FileSystemProviderInternalGetMetadataRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.getMetadataRequestedSuccess",
@@ -97,7 +137,7 @@
 };
 
 class FileSystemProviderInternalGetActionsRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.getActionsRequestedSuccess",
@@ -109,7 +149,7 @@
 };
 
 class FileSystemProviderInternalReadDirectoryRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.readDirectoryRequestedSuccess",
@@ -121,7 +161,7 @@
 };
 
 class FileSystemProviderInternalReadFileRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.readFileRequestedSuccess",
@@ -133,7 +173,7 @@
 };
 
 class FileSystemProviderInternalOperationRequestedSuccessFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.operationRequestedSuccess",
@@ -145,7 +185,7 @@
 };
 
 class FileSystemProviderInternalOperationRequestedErrorFunction
-    : public FileSystemProviderInternalFunction {
+    : public FileSystemProviderInternal {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileSystemProviderInternal.operationRequestedError",
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.cc b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.cc
index 02b92bf..4f860799 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.cc
@@ -121,60 +121,4 @@
       FileErrorToProviderError(error));
 }
 
-FileSystemProviderInternalFunction::FileSystemProviderInternalFunction()
-    : request_id_(0), request_manager_(NULL) {
-}
-
-ExtensionFunction::ResponseAction
-FileSystemProviderInternalFunction::RejectRequest(
-    std::unique_ptr<RequestValue> value,
-    base::File::Error error) {
-  const base::File::Error result =
-      request_manager_->RejectRequest(request_id_, std::move(value), error);
-  if (result != base::File::FILE_OK)
-    return RespondNow(Error(FileErrorToString(result)));
-  return RespondNow(NoArguments());
-}
-
-ExtensionFunction::ResponseAction
-FileSystemProviderInternalFunction::FulfillRequest(
-    std::unique_ptr<RequestValue> value,
-    bool has_more) {
-  const base::File::Error result =
-      request_manager_->FulfillRequest(request_id_, std::move(value), has_more);
-  if (result != base::File::FILE_OK)
-    return RespondNow(Error(FileErrorToString(result)));
-  return RespondNow(NoArguments());
-}
-
-bool FileSystemProviderInternalFunction::PreRunValidation(std::string* error) {
-  if (!ExtensionFunction::PreRunValidation(error))
-    return false;
-
-  EXTENSION_FUNCTION_PRERUN_VALIDATE(args().size() >= 2);
-
-  const auto& file_system_id_value = args()[0];
-  const auto& request_id_value = args()[1];
-  EXTENSION_FUNCTION_PRERUN_VALIDATE(file_system_id_value.is_string());
-  EXTENSION_FUNCTION_PRERUN_VALIDATE(request_id_value.is_int());
-  std::string file_system_id = file_system_id_value.GetString();
-  request_id_ = request_id_value.GetInt();
-
-  Service* service = Service::Get(browser_context());
-  if (!service) {
-    *error = "File system provider service not found.";
-    return false;
-  }
-
-  ProvidedFileSystemInterface* file_system = service->GetProvidedFileSystem(
-      ProviderId::CreateFromExtensionId(extension_id()), file_system_id);
-  if (!file_system) {
-    *error = FileErrorToString(base::File::FILE_ERROR_NOT_FOUND);
-    return false;
-  }
-
-  request_manager_ = file_system->GetRequestManager();
-  return true;
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h
index 9f5aa18f..72b9cb9 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h
+++ b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h
@@ -12,13 +12,6 @@
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "extensions/browser/extension_function.h"
 
-namespace ash {
-namespace file_system_provider {
-class RequestManager;
-class RequestValue;
-}  // namespace file_system_provider
-}  // namespace ash
-
 namespace extensions {
 
 // Error names from
@@ -42,36 +35,6 @@
 base::File::Error ProviderErrorToFileError(
     api::file_system_provider::ProviderError error);
 
-// Base class for internal API functions handling request results, either
-// a success or a failure.
-class FileSystemProviderInternalFunction : public ExtensionFunction {
- public:
-  FileSystemProviderInternalFunction();
-
- protected:
-  ~FileSystemProviderInternalFunction() override {}
-
-  // Rejects the request and returns a response for this API function.
-  ResponseAction RejectRequest(
-      std::unique_ptr<ash::file_system_provider::RequestValue> value,
-      base::File::Error error);
-
-  // Fulfills the request with parsed arguments of this API function
-  // encapsulated as a RequestValue instance and returns a response.
-  // If |has_more| is set to true, then the function will be called again for
-  // this request.
-  ResponseAction FulfillRequest(
-      std::unique_ptr<ash::file_system_provider::RequestValue> value,
-      bool has_more);
-
- private:
-  // Guarantees |request_id_| and |request_manager_| are valid.
-  bool PreRunValidation(std::string* error) override;
-
-  int request_id_;
-  ash::file_system_provider::RequestManager* request_manager_;
-};
-
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_PROVIDER_FUNCTION_H_
diff --git a/chrome/browser/component_updater/DEPS b/chrome/browser/component_updater/DEPS
index 0c8676b..215d21b 100644
--- a/chrome/browser/component_updater/DEPS
+++ b/chrome/browser/component_updater/DEPS
@@ -7,6 +7,7 @@
   "+components/feed",
   "+components/live_caption",
   "+components/soda",
+  "+components/url_param_filter/core",
   "+media/cdm",
   "+ppapi/thunk",
   "+third_party/widevine",
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc
index 122e8bd..8655a2ed 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc
@@ -37,11 +37,11 @@
 #include "base/time/time.h"
 #include "base/win/registry.h"
 #include "base/win/windows_version.h"
-#include "build/branding_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
+#include "components/chrome_cleaner/public/constants/buildflags.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "components/component_updater/component_updater_service.h"
@@ -75,9 +75,9 @@
 const base::FilePath::CharType kSwReporterExeName[] =
     FILE_PATH_LITERAL("software_reporter_tool.exe");
 
-// SwReporter is normally only registered in official builds.  However, to
+// SwReporter is normally only registered in Chrome branded builds. However, to
 // enable testing in chromium build bots, test code can set this to true.
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if BUILDFLAG(ENABLE_SOFTWARE_REPORTER)
 bool is_sw_reporter_enabled = true;
 #else
 bool is_sw_reporter_enabled = false;
diff --git a/chrome/browser/component_updater/url_param_classification_component_installer.cc b/chrome/browser/component_updater/url_param_classification_component_installer.cc
index 511fcb99..0ccf4eb 100644
--- a/chrome/browser/component_updater/url_param_classification_component_installer.cc
+++ b/chrome/browser/component_updater/url_param_classification_component_installer.cc
@@ -15,9 +15,9 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/version.h"
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
-#include "chrome/common/chrome_features.h"
 #include "components/component_updater/component_updater_paths.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -39,7 +39,8 @@
 
 // Runs on a thread pool.
 absl::optional<std::string> LoadFileFromDisk(const base::FilePath& pb_path) {
-  if (!base::FeatureList::IsEnabled(features::kIncognitoParamFilterEnabled))
+  if (!base::FeatureList::IsEnabled(
+          url_param_filter::features::kIncognitoParamFilterEnabled))
     return absl::nullopt;
   VLOG(1) << "Reading Url Param Classifications from file: " << pb_path.value();
   std::string file_contents;
diff --git a/chrome/browser/component_updater/url_param_classification_component_installer_unittest.cc b/chrome/browser/component_updater/url_param_classification_component_installer_unittest.cc
index 73523e4..7fd3859 100644
--- a/chrome/browser/component_updater/url_param_classification_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/url_param_classification_component_installer_unittest.cc
@@ -14,11 +14,11 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/version.h"
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
 #include "components/component_updater/mock_component_updater_service.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -80,10 +80,11 @@
  public:
   UrlParamClassificationComponentInstallerFeatureAgnosticTest() {
     if (GetParam()) {
-      scoped_list_.InitAndEnableFeature(features::kIncognitoParamFilterEnabled);
+      scoped_list_.InitAndEnableFeature(
+          url_param_filter::features::kIncognitoParamFilterEnabled);
     } else {
       scoped_list_.InitAndDisableFeature(
-          features::kIncognitoParamFilterEnabled);
+          url_param_filter::features::kIncognitoParamFilterEnabled);
     }
   }
 
@@ -240,7 +241,7 @@
   base::test::ScopedFeatureList scoped_list;
 
   scoped_list.InitAndEnableFeatureWithParameters(
-      features::kIncognitoParamFilterEnabled,
+      url_param_filter::features::kIncognitoParamFilterEnabled,
       {{"classifications",
         url_param_filter::
             CreateBase64EncodedFilterParamClassificationForTesting(
@@ -273,7 +274,8 @@
        FeatureDisabled_ComponentReady_DoesntFireCallback) {
   content::BrowserTaskEnvironment task_env;
   base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndDisableFeature(features::kIncognitoParamFilterEnabled);
+  scoped_list.InitAndDisableFeature(
+      url_param_filter::features::kIncognitoParamFilterEnabled);
 
   base::RunLoop run_loop;
   std::unique_ptr<component_updater::ComponentInstallerPolicy> policy =
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index c3c2f8e..7ae523b 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -243,8 +243,7 @@
   form.signon_realm = password_manager::GetSignonRealm(form.url);
   form.username_value = username;
   form.password_value = password;
-  form.note.value = note;
-  form.note.date_created = base::Time::Now();
+  form.notes.emplace_back(/*value=*/note, /*date_created=*/base::Time::Now());
   form.in_store = use_account_store
                       ? password_manager::PasswordForm::Store::kAccountStore
                       : password_manager::PasswordForm::Store::kProfileStore;
@@ -383,7 +382,8 @@
     api::passwords_private::PasswordUiEntry entry;
     entry.urls = CreateUrlCollectionFromForm(*form);
     entry.username = base::UTF16ToUTF8(form->username_value);
-    entry.password_note = base::UTF16ToUTF8(form->note.value);
+    entry.password_note =
+        form->notes.empty() ? "" : base::UTF16ToUTF8(form->notes[0].value);
     entry.id = password_id_generator_.GenerateId(
         password_manager::CreateSortKey(*form));
     entry.frontend_id = password_frontend_id_generator_.GenerateId(
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
index 1295550..d0208fa 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
@@ -468,7 +468,7 @@
 TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPasswordWithNote) {
   password_manager::PasswordForm sample_form = CreateSampleForm();
   password_manager::PasswordNote note(u"example note", base::Time::Now());
-  sample_form.note = note;
+  sample_form.notes = {note};
   SetUpPasswordStore({sample_form});
 
   PasswordsPrivateDelegateImpl delegate(&profile_);
@@ -483,7 +483,7 @@
       .WillOnce([&](const PasswordsPrivateDelegate::UiEntries& passwords) {
         EXPECT_EQ(sample_form.username_value,
                   base::UTF8ToUTF16(passwords[0].username));
-        EXPECT_EQ(sample_form.note.value,
+        EXPECT_EQ(sample_form.notes[0].value,
                   base::UTF8ToUTF16(passwords[0].password_note));
       });
   delegate.GetSavedPasswordsList(callback.Get());
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 2a8d004..402dd46 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -63,7 +63,6 @@
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_pref_names.h"  // nogncheck
 #include "ash/public/cpp/ambient/ambient_prefs.h"
-#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/ash/app_restore/full_restore_prefs.h"
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
 #include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
@@ -586,14 +585,6 @@
   (*s_allowlist)[ash::ambient::prefs::kAmbientModeAnimationPlaybackSpeed] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
 
-  // Dark Mode.
-  (*s_allowlist)[ash::prefs::kColorModeThemed] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_allowlist)[ash::prefs::kDarkModeEnabled] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_allowlist)[ash::prefs::kDarkModeScheduleType] =
-      settings_api::PrefType::PREF_TYPE_NUMBER;
-
   // Google Assistant.
   (*s_allowlist)[chromeos::assistant::prefs::kAssistantConsentStatus] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
@@ -1152,18 +1143,6 @@
       return settings_private::SetPrefResult::PREF_TYPE_UNSUPPORTED;
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (pref_name == ash::prefs::kDarkModeEnabled) {
-    DCHECK(value->is_bool());
-    base::UmaHistogramBoolean("Ash.DarkTheme.Settings.IsDarkModeEnabled",
-                              value->GetBool());
-  } else if (pref_name == ash::prefs::kColorModeThemed) {
-    DCHECK(value->is_bool());
-    base::UmaHistogramBoolean("Ash.DarkTheme.Settings.IsThemed",
-                              value->GetBool());
-  }
-#endif
-
   // TODO(orenb): Process setting metrics here and in the CrOS setting method
   // too (like "ProcessUserMetric" in CoreOptionsHandler).
   return settings_private::SetPrefResult::SUCCESS;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c41e665..3532cc4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1410,6 +1410,11 @@
     "expiry_milestone": 104
   },
   {
+    "name": "drive-fs-chrome-networking",
+    "owners": [ "travislane@google.com" ],
+    "expiry_milestone": 108
+  },
+  {
     "name": "durable-client-hints-cache",
     "owners": [ "abeyad", "aarontag"],
     "expiry_milestone": 110
@@ -1770,6 +1775,11 @@
     "expiry_milestone": 100
   },
   {
+    "name": "enable-cros-privacy-hub",
+    "owners": [ "chromeos-privacyhub@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "enable-cros-system-chinese-physical-typing",
     "owners": [ "shend", "essential-inputs-team@google.com" ],
     "expiry_milestone": 105
@@ -2012,6 +2022,11 @@
     "expiry_milestone": 110
   },
   {
+    "name": "enable-enhanced-safe-browsing-phase-2",
+    "owners": [ "ajuma", "joemerramos", "bling-flags@google.com"],
+    "expiry_milestone": 110
+  },
+  {
     "name": "enable-experimental-accessibility-dictation-with-pumpkin",
     "owners": [ "akihiroota"],
     "expiry_milestone": 104
@@ -3435,11 +3450,6 @@
     "expiry_milestone": 110
   },
   {
-    "name": "google-lens-sdk-intent",
-    "owners": [ "juanmojica@google.com", "benwgold@google.com", "lens-chrome@google.com" ],
-    "expiry_milestone": 103
-  },
-  {
     "name": "google-mobile-services-passwords",
     "owners": [ "fhorschig@chromium.org", "vasilii" ],
     "expiry_milestone": 108
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 13d8507..1641023 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1427,11 +1427,6 @@
     "Enables querying for remoting capabilities for ALL devices, regardless of "
     "the contents of the allowlist or blocklist.";
 
-const char kGoogleLensSdkIntentName[] =
-    "Enable the use of the Lens SDK when starting intent into Lens.";
-const char kGoogleLensSdkIntentDescription[] =
-    "Starts Lens using the Lens SDK if supported.";
-
 const char kGpuRasterizationName[] = "GPU rasterization";
 const char kGpuRasterizationDescription[] = "Use GPU to rasterize web content.";
 
@@ -4609,6 +4604,12 @@
 const char kDriveFsBidirectionalNativeMessagingDescription[] =
     "Enable enhanced native messaging host to communicate with DriveFS.";
 
+const char kDriveFsChromeNetworkingName[] =
+    "Enable the DriveFS / Chrome Network Service bridge";
+const char kDriveFsChromeNetworkingDescription[] =
+    "Enable the bridge bewteen DriveFS and the Chrome Network Service for "
+    "communication with the Drive backend.";
+
 const char kEnableAppReinstallZeroStateName[] =
     "Enable Zero State App Reinstall Suggestions.";
 const char kEnableAppReinstallZeroStateDescription[] =
@@ -5650,6 +5651,9 @@
 const char kCrOSEnforceSystemAecNsAgcDescription[] =
     "Enforces using the system variants in CrAS of the AEC, NS and AGC.";
 
+const char kCrosPrivacyHubName[] = "Enable ChromeOS Privacy Hub";
+const char kCrosPrivacyHubDescription[] = "Enables ChromeOS Privacy Hub.";
+
 const char kDefaultCalculatorWebAppName[] = "Default install Calculator PWA";
 const char kDefaultCalculatorWebAppDescription[] =
     "Enable default installing of the calculator PWA instead of the deprecated "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9212a7a2..3bbbb97de 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -794,9 +794,6 @@
 extern const char kCastForceEnableRemotingQueryName[];
 extern const char kCastForceEnableRemotingQueryDescription[];
 
-extern const char kGoogleLensSdkIntentName[];
-extern const char kGoogleLensSdkIntentDescription[];
-
 extern const char kGpuRasterizationName[];
 extern const char kGpuRasterizationDescription[];
 
@@ -2628,6 +2625,9 @@
 extern const char kDriveFsBidirectionalNativeMessagingName[];
 extern const char kDriveFsBidirectionalNativeMessagingDescription[];
 
+extern const char kDriveFsChromeNetworkingName[];
+extern const char kDriveFsChromeNetworkingDescription[];
+
 extern const char kEnableAppReinstallZeroStateName[];
 extern const char kEnableAppReinstallZeroStateDescription[];
 
@@ -3226,6 +3226,9 @@
 extern const char kCrOSDspBasedAgcAllowedName[];
 extern const char kCrOSDspBasedAgcAllowedDescription[];
 
+extern const char kCrosPrivacyHubName[];
+extern const char kCrosPrivacyHubDescription[];
+
 extern const char kCrOSEnforceSystemAecName[];
 extern const char kCrOSEnforceSystemAecDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 38e2d56..306945e 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -117,7 +117,6 @@
     &shared_highlighting::kSharedHighlightingAmp,
     &features::kElasticOverscroll,
     &features::kElidePrioritizationOfPreNativeBootstrapTasks,
-    &features::kGiveJavaUiThreadDefaultTaskTraitsUserBlockingPriority,
     &features::kPrivacyGuideAndroid,
     &features::kPushMessagingDisallowSenderIDs,
     &features::kPwaUpdateDialogForIcon,
@@ -229,7 +228,6 @@
     &kExploreSites,
     &kFixedUmaSessionResumeOrder,
     &kFocusOmniboxInIncognitoTabIntents,
-    &kGoogleLensSdkIntent,
     &kGridTabSwitcherForTablets,
     &kHandleMediaIntents,
     &kImmersiveUiMode,
@@ -553,9 +551,6 @@
 const base::Feature kContextMenuTranslateWithGoogleLens{
     "ContextMenuTranslateWithGoogleLens", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kGoogleLensSdkIntent{"GoogleLensSdkIntent",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kLensCameraAssistedSearch{"LensCameraAssistedSearch",
                                               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 734853a..c650dc6 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -91,7 +91,6 @@
 extern const base::Feature kExperimentsForAgsa;
 extern const base::Feature kExploreSites;
 extern const base::Feature kFocusOmniboxInIncognitoTabIntents;
-extern const base::Feature kGoogleLensSdkIntent;
 extern const base::Feature kGridTabSwitcherForTablets;
 extern const base::Feature kHandleMediaIntents;
 extern const base::Feature kImmersiveUiMode;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 11eb876..f85dff0e1 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -58,9 +58,6 @@
                     .put(ChromeFeatureList.EARLY_LIBRARY_LOAD, true)
                     .put(ChromeFeatureList.ELASTIC_OVERSCROLL, true)
                     .put(ChromeFeatureList.ELIDE_PRIORITIZATION_OF_PRE_NATIVE_BOOTSTRAP_TASKS, true)
-                    .put(ChromeFeatureList
-                                    .GIVE_JAVA_UI_THREAD_DEFAULT_TASK_TRAITS_USER_BLOCKING_PRIORITY,
-                            false)
                     .put(ChromeFeatureList.IMMERSIVE_UI_MODE, false)
                     .put(ChromeFeatureList.OMNIBOX_ANDROID_AUXILIARY_SEARCH, false)
                     .put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, true)
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 a6181887..ebbbbb29 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
@@ -278,7 +278,6 @@
     public static final String CONTEXT_MENU_POPUP_STYLE = "ContextMenuPopupStyle";
     public static final String CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS =
             "ContextMenuSearchWithGoogleLens";
-    public static final String GOOGLE_LENS_SDK_INTENT = "GoogleLensSdkIntent";
     public static final String CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS = "ContextMenuShopWithGoogleLens";
     public static final String CONTEXT_MENU_SEARCH_AND_SHOP_WITH_GOOGLE_LENS =
             "ContextMenuSearchAndShopWithGoogleLens";
@@ -355,8 +354,6 @@
             "ForceDisableExtendedSyncPromos";
     public static final String FORCE_STARTUP_SIGNIN_PROMO = "ForceStartupSigninPromo";
     public static final String TANGIBLE_SYNC = "TangibleSync";
-    public static final String GIVE_JAVA_UI_THREAD_DEFAULT_TASK_TRAITS_USER_BLOCKING_PRIORITY =
-            "GiveJavaUiThreadDefaultTaskTraitsUserBlockingPriority";
     public static final String GRID_TAB_SWITCHER_FOR_TABLETS = "GridTabSwitcherForTablets";
     public static final String HANDLE_MEDIA_INTENTS = "HandleMediaIntents";
     public static final String HTTPS_FIRST_MODE = "HttpsOnlyMode";
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index 9342bd6..86aef2ef 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -244,10 +244,12 @@
       /*can_trigger_fre=*/false);
 }
 
-void BrowserServiceLacros::NewTab(NewTabCallback callback) {
+void BrowserServiceLacros::NewTab(bool should_trigger_session_restore,
+                                  NewTabCallback callback) {
   LoadMainProfile(
       base::BindOnce(&BrowserServiceLacros::NewTabWithProfile,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
+                     weak_ptr_factory_.GetWeakPtr(),
+                     should_trigger_session_restore, std::move(callback)),
       /*can_trigger_fre=*/true);
 }
 
@@ -563,8 +565,10 @@
                           platform_window->GetWindowUniqueId());
 }
 
-void BrowserServiceLacros::NewTabWithProfile(NewTabCallback callback,
-                                             Profile* profile) {
+void BrowserServiceLacros::NewTabWithProfile(
+    bool should_trigger_session_restore,
+    NewTabCallback callback,
+    Profile* profile) {
   if (!profile) {
     LOG(WARNING) << "No profile, it might be an early exit from the FRE. "
                     "Aborting the requested action.";
@@ -576,7 +580,7 @@
   if (browser) {
     chrome::NewTab(browser);
   } else {
-    chrome::NewEmptyWindow(profile, /*should_trigger_session_restore=*/false);
+    chrome::NewEmptyWindow(profile, should_trigger_session_restore);
   }
   std::move(callback).Run();
 }
diff --git a/chrome/browser/lacros/browser_service_lacros.h b/chrome/browser/lacros/browser_service_lacros.h
index dda9bec..a92483a3 100644
--- a/chrome/browser/lacros/browser_service_lacros.h
+++ b/chrome/browser/lacros/browser_service_lacros.h
@@ -45,7 +45,8 @@
       const std::u16string& tab_id,
       const std::u16string& group_id,
       NewWindowForDetachingTabCallback callback) override;
-  void NewTab(NewTabCallback callback) override;
+  void NewTab(bool should_trigger_session_restore,
+              NewTabCallback callback) override;
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
                OpenUrlCallback callback) override;
@@ -88,7 +89,9 @@
       const std::u16string& group_id,
       NewWindowForDetachingTabCallback callback,
       Profile* profile);
-  void NewTabWithProfile(NewTabCallback callback, Profile* profile);
+  void NewTabWithProfile(bool should_trigger_session_restore,
+                         NewTabCallback callback,
+                         Profile* profile);
   void OpenUrlWithProfile(const GURL& url,
                           crosapi::mojom::OpenUrlParamsPtr params,
                           OpenUrlCallback callback,
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 01fb848..1c0022e 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -35,6 +35,8 @@
 #include "chromeos/crosapi/mojom/crosapi.mojom-test-utils.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/startup/browser_init_params.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -424,6 +426,77 @@
             tab_strip->GetWebContentsAt(0)->GetLastCommittedURL().path());
 }
 
+IN_PROC_BROWSER_TEST_F(BrowserServiceLacrosWindowlessBrowserTest,
+                       NewTab_OpensWindowWithSessionRestore) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  auto* profile =
+      profile_manager->GetProfile(profile_manager->GetPrimaryUserProfilePath());
+  DisableWelcomePages({profile});
+  EXPECT_EQ(0u, BrowserList::GetInstance()->size());
+
+  // Set the startup pref to restore the last session.
+  SessionStartupPref pref(SessionStartupPref::LAST);
+
+  // Open a browser window with some URLs.
+  auto* browser = Browser::Create(
+      Browser::CreateParams(Browser::TYPE_NORMAL, profile, true));
+  auto* tab_strip = browser->tab_strip_model();
+
+  chrome::NewTab(browser);
+  tab_strip->ActivateTabAt(0);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser, embedded_test_server()->GetURL("/title1.html")));
+
+  chrome::NewTab(browser);
+  tab_strip->ActivateTabAt(1);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser, embedded_test_server()->GetURL("/title2.html")));
+
+  ASSERT_EQ(2, tab_strip->count());
+
+  // Keep the browser process running while the browser is closed.
+  ScopedKeepAlive keep_alive(KeepAliveOrigin::BROWSER,
+                             KeepAliveRestartOption::DISABLED);
+  ScopedProfileKeepAlive profile_keep_alive(
+      profile, ProfileKeepAliveOrigin::kBrowserWindow);
+
+  // Close the browser and ensure there are no longer any open browser windows.
+  CloseBrowserSynchronously(browser);
+  EXPECT_EQ(0u, BrowserList::GetInstance()->size());
+
+  // Trigger a new tab with session restore.
+  base::RunLoop run_loop;
+  SessionsRestoredWaiter restore_waiter(run_loop.QuitClosure(), 1);
+  browser_service()->NewTab(
+      /*should_trigger_session_restore=*/true,
+      /*callback=*/base::DoNothing());
+  run_loop.Run();
+
+  EXPECT_EQ(1u, BrowserList::GetInstance()->size());
+  auto* new_browser = chrome::FindBrowserWithProfile(profile);
+  ASSERT_TRUE(new_browser);
+  auto* new_tab_strip = new_browser->tab_strip_model();
+  ASSERT_EQ(2, new_tab_strip->count());
+
+  EXPECT_EQ("/title1.html",
+            new_tab_strip->GetWebContentsAt(0)->GetLastCommittedURL().path());
+  EXPECT_EQ("/title2.html",
+            new_tab_strip->GetWebContentsAt(1)->GetLastCommittedURL().path());
+
+  // A second call to NewTab() ignores session restore and adds a new tab to
+  // the existing browser.
+  base::RunLoop run_loop2;
+  browser_service()->NewTab(
+      /*should_trigger_session_restore=*/true,
+      /*callback=*/run_loop2.QuitClosure());
+  run_loop2.Run();
+
+  EXPECT_EQ(1u, BrowserList::GetInstance()->size());
+  ASSERT_EQ(3, new_tab_strip->count());
+}
+
 // Tests that requesting an incognito window when incognito mode is disallowed
 // does not crash, and opens a regular window instead. Regression test for
 // https://crbug.com/1314473
@@ -533,6 +606,7 @@
 
   base::RunLoop run_loop;
   browser_service()->NewTab(
+      /*should_trigger_session_restore=*/false,
       /*callback=*/run_loop.QuitClosure());
   profiles::testing::CompleteLacrosFirstRun(LoginUIService::ABORT_SYNC);
 
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc
index b00c873..5543e6f 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider.cc
+++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
+#include "base/files/file_util.h"
 #include "base/hash/md5.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -79,6 +80,20 @@
   return full_hardware_class;
 }
 
+// Called on a background thread to load cellular device variant
+// using ConfigFS.
+std::string GetCellularDeviceVariantOnBackgroundThread() {
+  constexpr char kFirmwareVariantPath[] =
+      "/run/chromeos-config/v1/modem/firmware-variant";
+  std::string cellular_device_variant;
+  const base::FilePath modem_path = base::FilePath(kFirmwareVariantPath);
+  if (base::PathExists(modem_path)) {
+    base::ReadFileToString(modem_path, &cellular_device_variant);
+  }
+  VLOG(1) << "cellular_device_variant: " << cellular_device_variant;
+  return cellular_device_variant;
+}
+
 bool IsFeatureEnabled(
     const ash::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
         feature_states_map,
@@ -140,10 +155,11 @@
 
 void ChromeOSMetricsProvider::AsyncInit(base::OnceClosure done_callback) {
   base::RepeatingClosure barrier =
-      base::BarrierClosure(3, std::move(done_callback));
+      base::BarrierClosure(4, std::move(done_callback));
   InitTaskGetFullHardwareClass(barrier);
   InitTaskGetArcFeatures(barrier);
   InitTaskGetTpmType(barrier);
+  InitTaskGetCellularDeviceVariant(barrier);
 }
 
 void ChromeOSMetricsProvider::OnDidCreateMetricsLog() {
@@ -198,6 +214,20 @@
                      weak_ptr_factory_.GetWeakPtr(), barrier));
 }
 
+void ChromeOSMetricsProvider::InitTaskGetCellularDeviceVariant(
+    base::OnceClosure callback) {
+  // Run the (potentially expensive) task in the background to avoid blocking
+  // the UI thread.
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::WithBaseSyncPrimitives(),
+       base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(&GetCellularDeviceVariantOnBackgroundThread),
+      base::BindOnce(&ChromeOSMetricsProvider::SetCellularDeviceVariant,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void ChromeOSMetricsProvider::ProvideSystemProfileMetrics(
     metrics::SystemProfileProto* system_profile_proto) {
   WriteLinkedAndroidPhoneProto(system_profile_proto);
@@ -215,6 +245,8 @@
 
   SetTpmType(system_profile_proto);
 
+  hardware->set_cellular_device_variant(cellular_device_variant_);
+
   if (arc_release_) {
     metrics::SystemProfileProto::OS::Arc* arc =
         system_profile_proto->mutable_os()->mutable_arc();
@@ -319,6 +351,23 @@
       feature_states_map, ash::multidevice_setup::mojom::Feature::kMessages));
 }
 
+// Writes cellular device variant to system profile proto
+// if present.
+void ChromeOSMetricsProvider::WriteCellularDeviceVariant(
+    metrics::SystemProfileProto* system_profile_proto) {
+  metrics::SystemProfileProto::Hardware* hardware =
+      system_profile_proto->mutable_hardware();
+  constexpr char kFirmwareVariantPath[] =
+      "/run/chromeos-config/v1/modem/firmware-variant";
+  std::string cellular_device_variant;
+  const base::FilePath modem_path = base::FilePath(kFirmwareVariantPath);
+
+  if (base::PathExists(modem_path)) {
+    base::ReadFileToString(modem_path, &cellular_device_variant);
+    hardware->set_cellular_device_variant(cellular_device_variant);
+  }
+}
+
 void ChromeOSMetricsProvider::UpdateMultiProfileUserCount(
     metrics::SystemProfileProto* system_profile_proto) {
   if (user_manager::UserManager::IsInitialized()) {
@@ -347,6 +396,13 @@
   std::move(callback).Run();
 }
 
+void ChromeOSMetricsProvider::SetCellularDeviceVariant(
+    base::OnceClosure callback,
+    std::string cellular_device_variant) {
+  cellular_device_variant_ = cellular_device_variant;
+  std::move(callback).Run();
+}
+
 void ChromeOSMetricsProvider::OnArcFeaturesParsed(
     base::OnceClosure callback,
     absl::optional<arc::ArcFeatures> features) {
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.h b/chrome/browser/metrics/chromeos_metrics_provider.h
index 37be0e1..7367fe9 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider.h
+++ b/chrome/browser/metrics/chromeos_metrics_provider.h
@@ -53,6 +53,10 @@
   // |callback| is run.
   void InitTaskGetArcFeatures(base::OnceClosure callback);
 
+  // Loads the cellular device variant. When this task is complete, |callback|
+  // is run.
+  void InitTaskGetCellularDeviceVariant(base::OnceClosure callback);
+
   // Retrieves TPM type using TpmManagerClient. When this task is complete,
   // |callback| is run.
   void InitTaskGetTpmType(base::OnceClosure callback);
@@ -84,6 +88,10 @@
   void SetFullHardwareClass(base::OnceClosure callback,
                             std::string full_hardware_class);
 
+  // Sets the cellular device variant, then calls the callback.
+  void SetCellularDeviceVariant(base::OnceClosure callback,
+                                std::string cellular_device_variant);
+
   // Updates ARC-related system profile fields, then calls the callback.
   void OnArcFeaturesParsed(base::OnceClosure callback,
                            absl::optional<arc::ArcFeatures> features);
@@ -109,6 +117,11 @@
   void WriteLinkedAndroidPhoneProto(
       metrics::SystemProfileProto* system_profile_proto);
 
+  // Writes info about cellular device variant if present in
+  // system profile proto.
+  void WriteCellularDeviceVariant(
+      metrics::SystemProfileProto* system_profile_proto);
+
   // For collecting systemwide performance data via the UMA channel.
   std::unique_ptr<metrics::ProfileProvider> profile_provider_;
 
@@ -127,6 +140,9 @@
   // the configured system components such as CPU, WiFi adapter, etc.
   std::string full_hardware_class_;
 
+  // Cellular device variant for Chrome OS devices with cellular support.
+  std::string cellular_device_variant_;
+
   // ARC release version obtained from build properties.
   absl::optional<std::string> arc_release_ = absl::nullopt;
 
diff --git a/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.cc b/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.cc
index 446d3680f..249adb1d 100644
--- a/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.cc
+++ b/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.cc
@@ -157,23 +157,21 @@
 
 void UsageScenarioDataStoreImpl::OnIsCapturingVideoStarted() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (tabs_capturing_video_ == 0) {
+  if (web_contents_capturing_video_ == 0) {
     DCHECK(capturing_video_since_.is_null());
     capturing_video_since_ = tick_clock_->NowTicks();
   }
-  ++tabs_capturing_video_;
-  DCHECK_GE(current_tab_count_, tabs_capturing_video_);
+  ++web_contents_capturing_video_;
 }
 
 void UsageScenarioDataStoreImpl::OnIsCapturingVideoEnded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_GT(tabs_capturing_video_, 0U);
-  --tabs_capturing_video_;
-  DCHECK_GE(current_tab_count_, tabs_capturing_video_);
+  DCHECK_GT(web_contents_capturing_video_, 0U);
+  --web_contents_capturing_video_;
 
   // If this was the last tab capturing video then the interval data should be
   // updated.
-  if (tabs_capturing_video_ == 0) {
+  if (web_contents_capturing_video_ == 0) {
     DCHECK(!capturing_video_since_.is_null());
     interval_data_.time_capturing_video +=
         tick_clock_->NowTicks() - capturing_video_since_;
diff --git a/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h b/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h
index 692c244..60a3042 100644
--- a/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h
+++ b/chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h
@@ -60,7 +60,7 @@
     base::TimeDelta time_playing_video_full_screen_single_monitor;
     // The time spent with at least one opened WebRTC connection.
     base::TimeDelta time_with_open_webrtc_connection;
-    // The time spent with at least one tab capturing video.
+    // The time spent with at least one WebContents capturing video.
     base::TimeDelta time_capturing_video;
     // The time spent playing video in at least one visible tab.
     base::TimeDelta time_playing_video_in_visible_tab;
@@ -212,12 +212,13 @@
   // ends (when ResetIntervalData is called).
   base::TimeTicks has_opened_webrtc_connection_since_;
 
-  // The number of tabs capturing video (e.g. webcam).
-  uint16_t tabs_capturing_video_ = 0;
+  // The number of WebContents capturing video (e.g. webcam). Usually a tab, but
+  // some exceptions exist (e.g. OOBE WebUI on ChromeOS).
+  uint16_t web_contents_capturing_video_ = 0;
 
   // The timestamp of the beginning of a video capture session that has caused
-  // |tabs_capturing_video_| to increase to 1. Reset to |now| when an internal
-  // ends (when ResetIntervalData is called).
+  // |web_contents_capturing_video_| to increase to 1. Reset to |now| when an
+  // internal ends (when ResetIntervalData is called).
   base::TimeTicks capturing_video_since_;
 
   // The number of tabs playing audio.
diff --git a/chrome/browser/net/stub_resolver_config_reader_browsertest.cc b/chrome/browser/net/stub_resolver_config_reader_browsertest.cc
index b151c34..660f3311 100644
--- a/chrome/browser/net/stub_resolver_config_reader_browsertest.cc
+++ b/chrome/browser/net/stub_resolver_config_reader_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
@@ -280,6 +281,12 @@
       /*force_check_parental_controls_for_automatic_mode=*/false);
   EXPECT_EQ(secure_dns_config.mode(), net::SecureDnsMode::kSecure);
   EXPECT_THAT(secure_dns_config.doh_servers().servers(), testing::IsEmpty());
+  // Deterministic regression test for flaky failures seen in
+  // https://crbug.com/1326526. This induces a DNS resolution while in secure
+  // mode with zero DoH server templates to use.
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("foo.example", "/")));
 
   // Invalid mode policy
   SetSecureDnsModePolicy("invalid");
diff --git a/chrome/browser/notifications/notification_permission_browsertest.cc b/chrome/browser/notifications/notification_permission_browsertest.cc
index 40e1f4c..e522827 100644
--- a/chrome/browser/notifications/notification_permission_browsertest.cc
+++ b/chrome/browser/notifications/notification_permission_browsertest.cc
@@ -12,7 +12,6 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_mock_cert_verifier.h"
-#include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
@@ -98,45 +97,6 @@
         CONTENT_SETTING_ALLOW);
   }
 
-  std::string GetNotificationPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "getNotificationPermission()");
-  }
-
-  std::string GetServiceWorkerNotificationPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "getServiceWorkerNotificationPermission()");
-  }
-
-  std::string QueryNotificationPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "queryNotificationPermission()");
-  }
-
-  std::string QueryServiceWorkerNotificationPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "queryServiceWorkerNotificationPermission()");
-  }
-
-  std::string RequestNotificationPermission(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "requestNotificationPermission()");
-  }
-
-  std::string GetPushPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "getPushPermission()");
-  }
-
-  std::string GetServiceWorkerPushPermissionState(
-      const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "getServiceWorkerPushPermission()");
-  }
-
-  std::string RequestPushPermission(const content::ToRenderFrameHost& adapter) {
-    return RunJs(adapter, "requestPushPermission()");
-  }
-
   GURL TesterUrl() const {
     return https_server_->GetURL(kTesterHost, kTestFilePath);
   }
@@ -159,33 +119,25 @@
     // For now assume this is the only child iframe.
     EXPECT_FALSE(ChildFrameAt(parent_rfh, 0));
 
-    content::TestNavigationObserver navigation_observer(GetActiveWebContents());
-    EXPECT_TRUE(ExecJs(
-        parent_rfh,
-        content::JsReplace("const iframe = document.createElement('iframe');"
-                           "iframe.id = 'child_iframe';"
-                           "iframe.src = $1;"
-                           "document.body.appendChild(iframe);",
-                           iframe_src)));
-    navigation_observer.Wait();
-    EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
-    EXPECT_EQ(net::OK, navigation_observer.last_net_error_code());
-    EXPECT_EQ(iframe_src, navigation_observer.last_navigation_url());
+    EXPECT_EQ("iframe loaded",
+              EvalJs(parent_rfh, content::JsReplace(R"(
+                new Promise((resolve, reject) => {
+                  const iframe = document.createElement('iframe');
+                  iframe.id = 'child_iframe';
+                  iframe.src = $1;
+                  iframe.onload = _ => { resolve('iframe loaded') };
+                  iframe.onerror = e => { reject(e) };
+                  document.body.appendChild(iframe);
+                }))",
+                                                    iframe_src)));
 
     content::RenderFrameHost* iframe = ChildFrameAt(parent_rfh, 0);
     EXPECT_TRUE(iframe);
+    EXPECT_EQ(iframe_src, iframe->GetLastCommittedURL());
     return iframe;
   }
 
  private:
-  std::string RunJs(const content::ToRenderFrameHost& adapter,
-                    const std::string& js) {
-    std::string result;
-    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
-        adapter.render_frame_host(), js, &result));
-    return result;
-  }
-
   const base::FilePath server_root_{FILE_PATH_LITERAL("chrome/test/data")};
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   content::ContentMockCertVerifier mock_cert_verifier_;
@@ -204,20 +156,21 @@
   GrantNotificationPermissionForTest(TesterUrl());
 
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), TesterUrl()));
-  EXPECT_EQ("granted", GetNotificationPermissionState(GetActiveWebContents()));
-  EXPECT_EQ("granted", GetServiceWorkerNotificationPermissionState(
-                           GetActiveWebContents()));
+  content::RenderFrameHost* main_frame = GetActiveWebContents()->GetMainFrame();
+  EXPECT_EQ("granted", EvalJs(main_frame, "getNotificationPermission()"));
+  EXPECT_EQ("granted",
+            EvalJs(main_frame, "getServiceWorkerNotificationPermission()"));
 
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), EmbedderUrl()));
-  EXPECT_EQ("default", GetNotificationPermissionState(GetActiveWebContents()));
+  main_frame = GetActiveWebContents()->GetMainFrame();
+  EXPECT_EQ("default", EvalJs(main_frame, "getNotificationPermission()"));
 
-  content::RenderFrameHost* iframe =
-      CreateChildIframe(GetActiveWebContents()->GetMainFrame(), TesterUrl());
-  EXPECT_EQ(TesterUrl(), iframe->GetLastCommittedURL());
-  EXPECT_EQ("granted", GetNotificationPermissionState(iframe));
-  EXPECT_EQ("granted", GetServiceWorkerNotificationPermissionState(iframe));
-  EXPECT_EQ("granted", GetPushPermissionState(iframe));
-  EXPECT_EQ("granted", GetServiceWorkerPushPermissionState(iframe));
+  content::RenderFrameHost* iframe = CreateChildIframe(main_frame, TesterUrl());
+  EXPECT_EQ("granted", EvalJs(iframe, "getNotificationPermission()"));
+  EXPECT_EQ("granted",
+            EvalJs(iframe, "getServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("granted", EvalJs(iframe, "getPushPermission()"));
+  EXPECT_EQ("granted", EvalJs(iframe, "getServiceWorkerPushPermission()"));
 }
 
 // Tests that iframes not using their normal StoragePartition don't have
@@ -230,39 +183,43 @@
   // Verify that TesterUrl() has notification/push permission.
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), TesterUrl()));
   content::RenderFrameHost* main_frame = GetActiveWebContents()->GetMainFrame();
-  EXPECT_EQ("granted", GetNotificationPermissionState(main_frame));
-  EXPECT_EQ("granted", GetServiceWorkerNotificationPermissionState(main_frame));
-  EXPECT_EQ("granted", QueryNotificationPermissionState(main_frame));
+  EXPECT_EQ("granted", EvalJs(main_frame, "getNotificationPermission()"));
   EXPECT_EQ("granted",
-            QueryServiceWorkerNotificationPermissionState(main_frame));
-  EXPECT_EQ("granted", GetPushPermissionState(main_frame));
-  EXPECT_EQ("granted", GetServiceWorkerPushPermissionState(main_frame));
+            EvalJs(main_frame, "getServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("granted", EvalJs(main_frame, "queryNotificationPermission()"));
+  EXPECT_EQ("granted",
+            EvalJs(main_frame, "queryServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("granted", EvalJs(main_frame, "getPushPermission()"));
+  EXPECT_EQ("granted", EvalJs(main_frame, "getServiceWorkerPushPermission()"));
 
   // Load a site that uses a dedicated StoragePartition and verify that it has
   // default notification/push permissions.
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), IsolatedEmbedderUrl()));
   main_frame = GetActiveWebContents()->GetMainFrame();
-  EXPECT_EQ("default", GetNotificationPermissionState(main_frame));
-  EXPECT_EQ("denied", GetServiceWorkerNotificationPermissionState(main_frame));
-  EXPECT_EQ("prompt", QueryNotificationPermissionState(main_frame));
+  EXPECT_EQ("default", EvalJs(main_frame, "getNotificationPermission()"));
+  EXPECT_EQ("denied",
+            EvalJs(main_frame, "getServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("prompt", EvalJs(main_frame, "queryNotificationPermission()"));
   EXPECT_EQ("prompt",
-            QueryServiceWorkerNotificationPermissionState(main_frame));
-  EXPECT_EQ("prompt", GetPushPermissionState(main_frame));
-  EXPECT_EQ("prompt", GetServiceWorkerPushPermissionState(main_frame));
+            EvalJs(main_frame, "queryServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("prompt", EvalJs(main_frame, "getPushPermission()"));
+  EXPECT_EQ("prompt", EvalJs(main_frame, "getServiceWorkerPushPermission()"));
 
   // Load TesterUrl() in an iframe inside the dedicated StoragePartition page.
   // Even though TesterUrl() has notification/push permission when in a main
   // frame, it shouldn't when it's embedded in a different StoragePartition.
-  content::RenderFrameHost* iframe =
-      CreateChildIframe(GetActiveWebContents()->GetMainFrame(), TesterUrl());
-  EXPECT_EQ(TesterUrl(), iframe->GetLastCommittedURL());
-  EXPECT_EQ("denied", GetNotificationPermissionState(iframe));
-  EXPECT_EQ("denied", GetServiceWorkerNotificationPermissionState(iframe));
-  EXPECT_EQ("denied", QueryNotificationPermissionState(iframe));
-  EXPECT_EQ("denied", QueryServiceWorkerNotificationPermissionState(iframe));
-  EXPECT_EQ("denied", RequestNotificationPermission(iframe));
-  EXPECT_EQ("denied", GetPushPermissionState(iframe));
-  EXPECT_EQ("denied", GetServiceWorkerPushPermissionState(iframe));
-  EXPECT_EQ("NotAllowedError: Registration failed - permission denied",
-            RequestPushPermission(iframe));
+  content::RenderFrameHost* iframe = CreateChildIframe(main_frame, TesterUrl());
+  EXPECT_EQ("denied", EvalJs(iframe, "getNotificationPermission()"));
+  EXPECT_EQ("denied",
+            EvalJs(iframe, "getServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("denied", EvalJs(iframe, "queryNotificationPermission()"));
+  EXPECT_EQ("denied",
+            EvalJs(iframe, "queryServiceWorkerNotificationPermission()"));
+  EXPECT_EQ("denied", EvalJs(iframe, "requestNotificationPermission()"));
+  EXPECT_EQ("denied", EvalJs(iframe, "getPushPermission()"));
+  EXPECT_EQ("denied", EvalJs(iframe, "getServiceWorkerPushPermission()"));
+  EXPECT_EQ(
+      "a JavaScript error: \"NotAllowedError: "
+      "Registration failed - permission denied\"\n",
+      EvalJs(iframe, "requestPushPermission()").error);
 }
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/largest_contentful_paint_paint_preview.html b/chrome/browser/page_load_metrics/integration_tests/data/largest_contentful_paint_paint_preview.html
new file mode 100644
index 0000000..f233fc4
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/largest_contentful_paint_paint_preview.html
@@ -0,0 +1,76 @@
+<script src="resources/testharness.js"></script>
+<script>
+// Tell testharness.js to not wait for 'real' tests; we only want
+// testharness.js for its assertion helpers.
+setup({'output': false});
+</script>
+
+<script>
+  // 'AsyncBuffer' serves as a helper to buffer LCP reports asynchronously.
+  class AsyncBuffer {
+    constructor() {
+      // 'pending' is an array that will buffer entries reported through the
+      // PerformanceObserver and can be collected with 'pop'.
+      this.pending = [];
+
+      // 'resolve_fn' is a reference to the 'resolve' function of a
+      // Promise that blocks for new entries to arrive via 'push()'. Calling
+      // the function resolves the promise and unblocks calls to 'pop()'.
+      this.resolve_fn = null;
+    }
+
+    // Concatenates the given 'entries' list to this AsyncBuffer.
+    push(entries) {
+      if (entries.length == 0) {
+        throw new Error("Must not push an empty list of entries!");
+      }
+      this.pending = this.pending.concat(entries);
+
+      // If there are calls to 'pop' that are blocked waiting for items, signal
+      // that they can continue.
+      if (this.resolve_fn != null) {
+        this.resolve_fn();
+        this.resolve_fn = null;
+      }
+    }
+
+    // Takes the current pending entries from this AsyncBuffer. If there are no
+    // entries queued already, this will block until some show up.
+    async pop() {
+      if (this.pending.length == 0) {
+        // Need to instantiate a promise to block on. The next call to 'push'
+        // will resolve the promise once it has queued the entries.
+        await new Promise(resolve => {
+          this.resolve_fn = resolve;
+        });
+      }
+      assert_true(this.pending.length > 0);
+
+      const result = this.pending;
+      this.pending = [];
+      return result;
+    }
+  }
+
+  const buffer = new AsyncBuffer();
+  const po = new PerformanceObserver(entryList => {
+    buffer.push(entryList.getEntries());
+  });
+  po.observe({type: 'largest-contentful-paint', buffered: true});
+
+  const block_for_next_lcp = async () => {
+    const seen_events = await buffer.pop();
+    assert_equals(seen_events.length, 1);
+    return seen_events[0].size;
+  };
+
+  const trigger_repaint_and_block_for_next_lcp = async () => {
+    let new_div = document.createElement("div");
+    document.getElementById('firstDiv').appendChild(new_div);
+    new_div.innerText = "Medium text";
+    return await block_for_next_lcp();
+  };
+</script>
+
+<div id="firstDiv" style='width: 600px; height: 10000px'>Short text</div>
+<div style='width: 600px; height: 500px'>Loooooooooooong offscreen text that shouldn't count for LCP computation</div>
diff --git a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
index d88c3c4..943a13f 100644
--- a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
+++ b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
@@ -14,6 +14,7 @@
 #include "cc/base/switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
+#include "components/paint_preview/buildflags/buildflags.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/hit_test_region_observer.h"
@@ -25,6 +26,12 @@
 #endif
 #include "ui/events/test/event_generator.h"
 
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW)
+// `gn check` doesn't recognize that this is included conditionally, with the
+// same condition as the dependencies.
+#include "components/paint_preview/browser/paint_preview_client.h"  // nogncheck
+#endif
+
 using trace_analyzer::Query;
 using trace_analyzer::TraceAnalyzer;
 using trace_analyzer::TraceEvent;
@@ -160,6 +167,57 @@
   EXPECT_EQ(EvalJs(sub, "test_step_2()").value.GetString(), "green-16x16.png");
 }
 
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW)
+IN_PROC_BROWSER_TEST_F(MetricIntegrationTest,
+                       LargestContentfulPaintPaintPreview) {
+  Start();
+  StartTracing({"loading"});
+  Load("/largest_contentful_paint_paint_preview.html");
+
+  content::EvalJsResult lcp_before_paint_preview =
+      EvalJs(web_contents(), "block_for_next_lcp()");
+  EXPECT_EQ("", lcp_before_paint_preview.error);
+
+  paint_preview::PaintPreviewClient::CreateForWebContents(
+      web_contents());  // Is a singleton.
+  auto* client =
+      paint_preview::PaintPreviewClient::FromWebContents(web_contents());
+
+  paint_preview::PaintPreviewClient::PaintPreviewParams params(
+      paint_preview::RecordingPersistence::kMemoryBuffer);
+  params.inner.clip_rect = gfx::Rect(0, 0, 1, 1);
+  params.inner.is_main_frame = true;
+  params.inner.capture_links = false;
+  params.inner.max_capture_size = 50 * 1024 * 1024;
+  params.inner.max_decoded_image_size_bytes = 50 * 1024 * 1024;
+  params.inner.skip_accelerated_content = true;
+
+  base::RunLoop run_loop;
+  client->CapturePaintPreview(
+      params, web_contents()->GetMainFrame(),
+      base::BindOnce(
+          [](base::OnceClosure callback, base::UnguessableToken,
+             paint_preview::mojom::PaintPreviewStatus,
+             std::unique_ptr<paint_preview::CaptureResult>) {
+            std::move(callback).Run();
+          },
+          run_loop.QuitClosure()));
+  run_loop.Run();
+
+  content::EvalJsResult lcp_after_paint_preview =
+      EvalJs(web_contents(), "trigger_repaint_and_block_for_next_lcp()");
+  EXPECT_EQ("", lcp_after_paint_preview.error);
+
+  // When PaintPreview creates new LCP candidates, we compare the short text and
+  // the long text here, which will fail. But in order to consistently get the
+  // new LCP candidate in that case, we always add a medium text in
+  // `trigger_repaint_and_block_for_next_lcp`. So use a soft comparison here
+  // that would permit the medium text, but not the long text.
+  EXPECT_LT(lcp_after_paint_preview.value.GetDouble(),
+            2 * lcp_before_paint_preview.value.GetDouble());
+}
+#endif
+
 class PageViewportInLCPTest : public MetricIntegrationTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 1b9df39..6a43ac2 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -30,9 +30,9 @@
 #include "chrome/browser/policy/file_selection_dialogs_policy_handler.h"
 #include "chrome/browser/policy/homepage_location_policy_handler.h"
 #include "chrome/browser/policy/javascript_policy_handler.h"
-#include "chrome/browser/policy/network_prediction_policy_handler.h"
 #include "chrome/browser/policy/webhid_device_policy_handler.h"
 #include "chrome/browser/policy/webusb_allow_devices_for_urls_policy_handler.h"
+#include "chrome/browser/prefetch/pref_names.h"
 #include "chrome/browser/profiles/force_safe_search_policy_handler.h"
 #include "chrome/browser/profiles/force_youtube_safety_mode_policy_handler.h"
 #include "chrome/browser/profiles/guest_mode_policy_handler.h"
@@ -221,6 +221,8 @@
   { key::kPopupsBlockedForUrls,
     prefs::kManagedPopupsBlockedForUrls,
     base::Value::Type::LIST },
+  { key::kNetworkPredictionOptions, prefetch::prefs::kNetworkPredictionOptions,
+    base::Value::Type::INTEGER },
 #if BUILDFLAG(ENABLE_PRINTING)
   { key::kPrintingEnabled,
     prefs::kPrintingEnabled,
@@ -311,7 +313,7 @@
   { key::kAutoOpenAllowedForURLs,
     prefs::kDownloadAllowedURLsForOpenByPolicy,
     base::Value::Type::LIST },
-    { key::kAutoplayAllowed,
+  { key::kAutoplayAllowed,
     prefs::kAutoplayAllowed,
     base::Value::Type::BOOLEAN },
   { key::kAutoplayAllowlist,
@@ -875,9 +877,6 @@
   { key::kWebUsbBlockedForUrls,
     prefs::kManagedWebUsbBlockedForUrls,
     base::Value::Type::LIST },
-  { key::kTabFreezingEnabled,
-    prefs::kTabFreezingEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kCoalesceH2ConnectionsWithClientCertificatesForHosts,
     prefs::kH2ClientCertCoalescingHosts,
     base::Value::Type::LIST },
@@ -1931,7 +1930,6 @@
       std::make_unique<WebUsbAllowDevicesForUrlsPolicyHandler>(chrome_schema));
   handlers->AddHandler(std::make_unique<FileSelectionDialogsPolicyHandler>());
   handlers->AddHandler(std::make_unique<JavascriptPolicyHandler>());
-  handlers->AddHandler(std::make_unique<NetworkPredictionPolicyHandler>());
   handlers->AddHandler(std::make_unique<SimplePolicyHandler>(
       key::kWindowOcclusionEnabled,
       policy::policy_prefs::kNativeWindowOcclusionEnabled,
diff --git a/chrome/browser/policy/network_prediction_policy_handler.cc b/chrome/browser/policy/network_prediction_policy_handler.cc
deleted file mode 100644
index cdd1adb..0000000
--- a/chrome/browser/policy/network_prediction_policy_handler.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/policy/network_prediction_policy_handler.h"
-
-#include "base/values.h"
-#include "chrome/browser/prefetch/pref_names.h"
-#include "chrome/browser/prefetch/prefetch_prefs.h"
-#include "chrome/common/pref_names.h"
-#include "components/policy/core/browser/policy_error_map.h"
-#include "components/policy/core/common/policy_map.h"
-#include "components/policy/policy_constants.h"
-#include "components/prefs/pref_value_map.h"
-#include "components/strings/grit/components_strings.h"
-
-namespace policy {
-
-bool NetworkPredictionPolicyHandler::CheckPolicySettings(
-    const PolicyMap& policies,
-    PolicyErrorMap* errors) {
-  // It is safe to use `GetValueUnsafe()` because type checking is performed
-  // before the value is used.
-  // Deprecated boolean preference.
-  const base::Value* network_prediction_enabled =
-      policies.GetValueUnsafe(key::kDnsPrefetchingEnabled);
-  // New enumerated preference.
-  const base::Value* network_prediction_options =
-      policies.GetValueUnsafe(key::kNetworkPredictionOptions);
-
-  if (network_prediction_enabled && !network_prediction_enabled->is_bool()) {
-    errors->AddError(key::kDnsPrefetchingEnabled, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::Type::BOOLEAN));
-  }
-
-  if (network_prediction_options && !network_prediction_options->is_int()) {
-    errors->AddError(key::kNetworkPredictionOptions, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::Type::INTEGER));
-  }
-
-  if (network_prediction_enabled && network_prediction_options) {
-    errors->AddError(key::kDnsPrefetchingEnabled,
-                     IDS_POLICY_OVERRIDDEN,
-                     key::kNetworkPredictionOptions);
-  }
-
-  return true;
-}
-
-void NetworkPredictionPolicyHandler::ApplyPolicySettings(
-    const PolicyMap& policies,
-    PrefValueMap* prefs) {
-  const base::Value* network_prediction_options = policies.GetValue(
-      key::kNetworkPredictionOptions, base::Value::Type::INTEGER);
-  if (network_prediction_options) {
-    prefs->SetInteger(prefetch::prefs::kNetworkPredictionOptions,
-                      network_prediction_options->GetInt());
-    return;
-  }
-
-  // Observe deprecated policy setting for compatibility.
-  const base::Value* network_prediction_enabled = policies.GetValue(
-      key::kDnsPrefetchingEnabled, base::Value::Type::BOOLEAN);
-  if (network_prediction_enabled) {
-    prefetch::NetworkPredictionOptions setting =
-        network_prediction_enabled->GetBool()
-            ? prefetch::NetworkPredictionOptions::kWifiOnlyDeprecated
-            : prefetch::NetworkPredictionOptions::kDisabled;
-    prefs->SetInteger(prefetch::prefs::kNetworkPredictionOptions,
-                      static_cast<int>(setting));
-  }
-}
-
-}  // namespace policy
diff --git a/chrome/browser/policy/network_prediction_policy_handler.h b/chrome/browser/policy/network_prediction_policy_handler.h
deleted file mode 100644
index e81fe587..0000000
--- a/chrome/browser/policy/network_prediction_policy_handler.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_POLICY_NETWORK_PREDICTION_POLICY_HANDLER_H_
-#define CHROME_BROWSER_POLICY_NETWORK_PREDICTION_POLICY_HANDLER_H_
-
-#include "components/policy/core/browser/configuration_policy_handler.h"
-
-class PrefValueMap;
-
-namespace policy {
-
-class PolicyErrorMap;
-class PolicyMap;
-
-// Handles NetworkPrediction policies.
-class NetworkPredictionPolicyHandler : public ConfigurationPolicyHandler {
- public:
-  NetworkPredictionPolicyHandler() = default;
-  NetworkPredictionPolicyHandler(const NetworkPredictionPolicyHandler&) =
-      delete;
-  NetworkPredictionPolicyHandler& operator=(
-      const NetworkPredictionPolicyHandler&) = delete;
-  ~NetworkPredictionPolicyHandler() override = default;
-
-  // ConfigurationPolicyHandler methods:
-  bool CheckPolicySettings(const PolicyMap& policies,
-                           PolicyErrorMap* errors) override;
-  void ApplyPolicySettings(const PolicyMap& policies,
-                           PrefValueMap* prefs) override;
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_POLICY_NETWORK_PREDICTION_POLICY_HANDLER_H_
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc
index b429fd5..6321bd2d 100644
--- a/chrome/browser/policy/profile_policy_connector.cc
+++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -24,7 +24,6 @@
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/configuration_policy_provider.h"
-#include "components/policy/core/common/legacy_chrome_policy_migrator.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
@@ -236,13 +235,6 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  migrators.push_back(std::make_unique<LegacyChromePolicyMigrator>(
-      policy::key::kDeviceUserWhitelist,  // nocheck
-      policy::key::kDeviceUserAllowlist));
-  migrators.push_back(std::make_unique<LegacyChromePolicyMigrator>(
-      policy::key::kNativePrintersBulkConfiguration,
-      policy::key::kPrintersBulkConfiguration));
-
   ConfigurationPolicyProvider* user_policy_delegate_candidate =
       configuration_policy_provider ? configuration_policy_provider
                                     : special_user_policy_provider_.get();
diff --git a/chrome/browser/policy/test/network_prediction_policy_browsertest.cc b/chrome/browser/policy/test/network_prediction_policy_browsertest.cc
index a8dfec8..dbdd343 100644
--- a/chrome/browser/policy/test/network_prediction_policy_browsertest.cc
+++ b/chrome/browser/policy/test/network_prediction_policy_browsertest.cc
@@ -20,23 +20,39 @@
   // Enabled by default.
   EXPECT_TRUE(prefetch::IsSomePreloadingEnabled(*prefs));
 
-  // Disable by old, deprecated policy.
+  // Disabled by policy.
   PolicyMap policies;
-  policies.Set(key::kDnsPrefetchingEnabled, POLICY_LEVEL_MANDATORY,
-               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
+  policies.Set(key::kNetworkPredictionOptions, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::Value(static_cast<int>(
+                   prefetch::NetworkPredictionOptions::kDisabled)),
                nullptr);
   UpdateProviderPolicy(policies);
-
   EXPECT_FALSE(prefetch::IsSomePreloadingEnabled(*prefs));
 
-  // Enabled by new policy, this should override old one.
+  // Enabled by policy.
   policies.Set(key::kNetworkPredictionOptions, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                base::Value(static_cast<int>(
                    prefetch::NetworkPredictionOptions::kStandard)),
                nullptr);
   UpdateProviderPolicy(policies);
+  EXPECT_TRUE(prefetch::IsSomePreloadingEnabled(*prefs));
 
+  policies.Set(key::kNetworkPredictionOptions, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::Value(static_cast<int>(
+                   prefetch::NetworkPredictionOptions::kWifiOnlyDeprecated)),
+               nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_TRUE(prefetch::IsSomePreloadingEnabled(*prefs));
+
+  policies.Set(key::kNetworkPredictionOptions, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::Value(static_cast<int>(
+                   prefetch::NetworkPredictionOptions::kExtended)),
+               nullptr);
+  UpdateProviderPolicy(policies);
   EXPECT_TRUE(prefetch::IsSomePreloadingEnabled(*prefs));
 }
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 8cdf56f..10ed54b 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -745,6 +745,11 @@
 extern const char kAccountIdMigrationState[] = "account_id_migration_state";
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Deprecated 05/2022.
+const char kColorModeThemed[] = "ash.dark_mode.color_mode_themed";
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -973,6 +978,10 @@
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   registry->RegisterIntegerPref(kAccountIdMigrationState, 0);
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  registry->RegisterBooleanPref(kColorModeThemed, true);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 }  // namespace
@@ -1638,6 +1647,11 @@
   local_state->ClearPref(kStabilityRendererLaunchCount);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
+  // Added 05/2022.
+#if !BUILDFLAG(IS_ANDROID)
+  local_state->ClearPref(prefs::kTabFreezingEnabled);
+#endif
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
 
@@ -1902,6 +1916,11 @@
   profile_prefs->ClearPref(kAccountIdMigrationState);
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Added 05/2022.
+  profile_prefs->ClearPref(kColorModeThemed);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc
index 2fa38c2..5f0264a 100644
--- a/chrome/browser/printing/print_browsertest.cc
+++ b/chrome/browser/printing/print_browsertest.cc
@@ -311,6 +311,8 @@
   // PrintPreviewUI::TestDelegate:
   void DidRenderPreviewPage(content::WebContents* preview_dialog) override {
     ++rendered_page_count_;
+    DVLOG(2) << "Rendered preview page " << rendered_page_count_
+             << " of a total expected " << expected_rendered_page_count_;
     CHECK_LE(rendered_page_count_, expected_rendered_page_count_);
     if (rendered_page_count_ == expected_rendered_page_count_ && run_loop_) {
       run_loop_->Quit();
@@ -3061,6 +3063,49 @@
   EXPECT_TRUE(stop_invoked());
 }
 
+// TODO(crbug.com/1326580):  Enable once multipage printing doesn't get stuck
+// because of insufficient rendered preview pages due to forced N-up.
+IN_PROC_BROWSER_TEST_P(PrintBackendPrintBrowserTestService,
+                       DISABLED_StartPrintingMultipage) {
+  AddPrinter("printer1");
+  SetPrinterNameForSubsequentContexts("printer1");
+
+  ASSERT_TRUE(embedded_test_server()->Started());
+  GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+  SetUpPrintViewManager(web_contents);
+
+  // The test will succeed to start the print job, render 3 pages of document
+  // content, and complete with document done.  Wait for a call to `Stop()` to
+  // ensure print job wrap-up finished cleanly before completing the test.
+  // This results in a total of 6 expected calls for Windows GDI printing, or
+  // 4 expected calls for all other cases.
+#if BUILDFLAG(IS_WIN)
+  // TODO(crbug.com/1008222)  Include Windows coverage of
+  // RenderPrintedDocument() once XPS print pipeline is added.
+  SetNumExpectedMessages(/*num=*/6);
+#else
+  SetNumExpectedMessages(/*num=*/4);
+#endif
+  PrintAfterPreviewIsReadyAndLoaded();
+
+  EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
+#if BUILDFLAG(IS_WIN)
+  // TODO(crbug.com/1008222)  Include Windows coverage of
+  // RenderPrintedDocument() once XPS print pipeline is added.
+  EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
+  EXPECT_EQ(render_printed_page_count(), 3);
+#else
+  EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
+#endif
+  EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
+  EXPECT_TRUE(stop_invoked());
+}
+
 IN_PROC_BROWSER_TEST_P(PrintBackendPrintBrowserTestService,
                        StartPrintingSpoolingSharedMemoryError) {
   AddPrinter("printer1");
diff --git a/chrome/browser/privacy_budget/identifiability_study_state.cc b/chrome/browser/privacy_budget/identifiability_study_state.cc
index 15f8d98..fff1ed9 100644
--- a/chrome/browser/privacy_budget/identifiability_study_state.cc
+++ b/chrome/browser/privacy_budget/identifiability_study_state.cc
@@ -80,10 +80,6 @@
   if (LIKELY(!settings_.enabled()))
     return false;
 
-  // We always record surfaces of type zero.
-  if (surface.GetType() == blink::IdentifiableSurface::Type::kReservedInternal)
-    return true;
-
   if (base::Contains(active_surfaces_, surface))
     return true;
 
@@ -581,12 +577,6 @@
           blink::IdentifiableSurface::Type::kMeasuredSurface)) {
     return false;
   }
-
-  if (surface.GetType() ==
-      blink::IdentifiableSurface::Type::kReservedInternal) {
-    return false;
-  }
-
   return surface_encounters_.IsNewEncounter(source_id,
                                             surface.ToUkmMetricHash());
 }
diff --git a/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc b/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
index 10ec320..edc27fe 100644
--- a/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
+++ b/chrome/browser/privacy_budget/identifiability_study_state_unittest.cc
@@ -723,90 +723,6 @@
   EXPECT_LE(selected_surfaces, kMaxSelectedSurfaces);
 }
 
-TEST(IdentifiabilityStudyStateStandaloneTest,
-     AlwaysSampleReservedInternalRandom) {
-  test::ScopedPrivacyBudgetConfig::Parameters parameters;
-  parameters.active_surface_budget = kTestingActiveSurfaceBudget;
-  parameters.expected_surface_count = 20;
-  parameters.allowed_random_types = {
-      blink::IdentifiableSurface::Type::kWebFeature};
-  test::ScopedPrivacyBudgetConfig config(parameters);
-
-  TestingPrefServiceSimple pref_service;
-  prefs::RegisterPrivacyBudgetPrefs(pref_service.registry());
-  test_utils::InspectableIdentifiabilityStudyState state(&pref_service);
-
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsCrossOriginFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsCrossSiteFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsMainFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_NavigationSourceId))));
-}
-
-TEST(IdentifiabilityStudyStateStandaloneTest,
-     AlwaysSampleReservedInternalBlock) {
-  const int kTestGroupCount = 80;
-  const int kSurfacesInGroup = 40;
-
-  test::ScopedPrivacyBudgetConfig::Parameters parameters;
-  for (int group_index = 0; group_index < kTestGroupCount; ++group_index) {
-    parameters.blocks.emplace_back(
-        CreateSurfaceList(kSurfacesInGroup, kSurfacesInGroup * group_index));
-  }
-
-  parameters.block_weights.assign(kTestGroupCount, 1.0);
-  parameters.active_surface_budget = kSurfacesInGroup;
-  test::ScopedPrivacyBudgetConfig config(parameters);
-
-  TestingPrefServiceSimple pref_service;
-  prefs::RegisterPrivacyBudgetPrefs(pref_service.registry());
-  test_utils::InspectableIdentifiabilityStudyState state(&pref_service);
-
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsCrossOriginFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsCrossSiteFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_IsMainFrame))));
-  EXPECT_TRUE(
-      state.ShouldRecordSurface(blink::IdentifiableSurface::FromTypeAndToken(
-          blink::IdentifiableSurface::Type::kReservedInternal,
-          blink::IdentifiableToken(
-              blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                  kDocumentCreated_NavigationSourceId))));
-}
-
 TEST(IdentifiabilityStudyStateStandaloneTest, NoAllowedTypes) {
   constexpr auto kExpectedSurfaceCount = 20;
 
diff --git a/chrome/browser/privacy_budget/privacy_budget_browsertest.cc b/chrome/browser/privacy_budget/privacy_budget_browsertest.cc
index e4e47f0..47bd50c 100644
--- a/chrome/browser/privacy_budget/privacy_budget_browsertest.cc
+++ b/chrome/browser/privacy_budget/privacy_budget_browsertest.cc
@@ -245,22 +245,14 @@
 
   auto merged_entries = recorder().GetMergedEntriesByName(
       ukm::builders::Identifiability::kEntryName);
-  // There are 2 entries here (3 on Android) generated by the two navigations
-  // from the test. All entries contain the document created event surfaces of
-  // type zero, but only one entry contains the web feature metrics.
-  ASSERT_GE(merged_entries.size(), 2u);
+  // Shouldn't be more than one source here. If this changes, then we'd need to
+  // adjust this test to deal.
+  ASSERT_EQ(1u, merged_entries.size());
 
-  // We collect all metrics together and check that there is one that contains
-  // the web feature metrics.
-  auto it = merged_entries.begin();
-  auto metrics = it->second->metrics;
-
-  for (auto& it : merged_entries) {
-    metrics.insert(it.second->metrics.begin(), it.second->metrics.end());
-  }
-
+  // All of the following features should be included in the list of returned
+  // metrics here. The exact values depend on the test host.
   EXPECT_THAT(
-      metrics,
+      merged_entries.begin()->second->metrics,
       IsSupersetOf({
           Key(HashFeature(
               blink::mojom::WebFeature::kV8Screen_Height_AttributeGetter)),
@@ -276,75 +268,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PrivacyBudgetBrowserTestWithTestRecorder,
-                       EveryNavigationRecordsDocumentCreatedMetrics) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  content::DOMMessageQueue messages(web_contents());
-  base::RunLoop run_loop;
-
-  recorder().SetOnAddEntryCallback(ukm::builders::Identifiability::kEntryName,
-                                   run_loop.QuitClosure());
-
-  ASSERT_TRUE(content::NavigateToURL(
-      web_contents(), embedded_test_server()->GetURL(
-                          "/privacy_budget/samples_screen_attributes.html")));
-
-  // The document calls a bunch of instrumented functions and sends a message
-  // back to the test. Receipt of the message indicates that the script
-  // successfully completed.
-  std::string screen_scrape;
-  ASSERT_TRUE(messages.WaitForMessage(&screen_scrape));
-
-  // The contents of the received message isn't used for anything other than
-  // diagnostics.
-  SCOPED_TRACE(screen_scrape);
-
-  // Navigating away from the test page causes the document to be unloaded. That
-  // will cause any buffered metrics to be flushed.
-  content::NavigateToURLBlockUntilNavigationsComplete(web_contents(),
-                                                      GURL("about:blank"), 1);
-
-  // Wait for the metrics to come down the pipe.
-  run_loop.Run();
-
-  auto merged_entries = recorder().GetMergedEntriesByName(
-      ukm::builders::Identifiability::kEntryName);
-  // There are 2 entries here (3 on Android) generated by the two navigations
-  // from the test. All entries contain the document created event surfaces of
-  // type zero.
-  ASSERT_GE(merged_entries.size(), 2u);
-
-  // Each entry in merged_entries (except the initial navigation to about:blank)
-  // corresponds to a committed navigation and they all should contain the
-  // document created metrics.
-  for (auto& it : merged_entries) {
-    EXPECT_THAT(it.second->metrics,
-                IsSupersetOf({
-                    Key(blink::IdentifiableSurface::FromTypeAndToken(
-                            blink::IdentifiableSurface::Type::kReservedInternal,
-                            blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                                kDocumentCreated_IsMainFrame)
-                            .ToUkmMetricHash()),
-                    Key(blink::IdentifiableSurface::FromTypeAndToken(
-                            blink::IdentifiableSurface::Type::kReservedInternal,
-                            blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                                kDocumentCreated_IsCrossOriginFrame)
-                            .ToUkmMetricHash()),
-                    Key(blink::IdentifiableSurface::FromTypeAndToken(
-                            blink::IdentifiableSurface::Type::kReservedInternal,
-                            blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                                kDocumentCreated_IsCrossSiteFrame)
-                            .ToUkmMetricHash()),
-                    Key(blink::IdentifiableSurface::FromTypeAndToken(
-                            blink::IdentifiableSurface::Type::kReservedInternal,
-                            blink::IdentifiableSurface::ReservedSurfaceMetrics::
-                                kDocumentCreated_NavigationSourceId)
-                            .ToUkmMetricHash()),
-                }));
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(PrivacyBudgetBrowserTestWithTestRecorder,
                        CallsCanvasToBlob) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -376,25 +299,14 @@
 
   auto merged_entries = recorder().GetMergedEntriesByName(
       ukm::builders::Identifiability::kEntryName);
-  // There are 2 entries here (3 on android) generated by the two navigations
-  // from the test. All entries contain the document created event surfaces of
-  // type zero, but only one entry contains the canvas readback metric.
-  ASSERT_GE(merged_entries.size(), 2u);
+  // Shouldn't be more than one source here. If this changes, then we'd need to
+  // adjust this test to deal.
+  ASSERT_EQ(1u, merged_entries.size());
 
   // toBlob() is called on a context-less canvas, hence -1, which is the value
   // of blink::CanvasRenderingContext::CanvasRenderingAPI::kUnknown.
   constexpr uint64_t input_digest = -1;
-
-  // We collect all metrics together and check that there is one that contains
-  // the canvas readback metrics.
-  auto it = merged_entries.begin();
-  auto metrics = it->second->metrics;
-
-  for (auto& it : merged_entries) {
-    metrics.insert(it.second->metrics.begin(), it.second->metrics.end());
-  }
-
-  EXPECT_THAT(metrics,
+  EXPECT_THAT(merged_entries.begin()->second->metrics,
               IsSupersetOf({
                   Key(blink::IdentifiableSurface::FromTypeAndToken(
                           blink::IdentifiableSurface::Type::kCanvasReadback,
@@ -442,25 +354,17 @@
 
   auto merged_entries = recorder().GetMergedEntriesByName(
       ukm::builders::Identifiability::kEntryName);
-  // There are 2 entries here (3 on Android) generated by the two navigations
-  // from the test. All entries contain the document created event surfaces of
-  // type zero, but only one entry contains the canvas readback metric.
-  ASSERT_GE(merged_entries.size(), 2u);
+  // Shouldn't be more than one source here. If this changes, then we'd need to
+  // adjust this test to deal.
+  ASSERT_EQ(1u, merged_entries.size());
+
+  auto& metrics = merged_entries.begin()->second->metrics;
 
   // (kCanvasReadback | input_digest << kTypeBits) = one of the merged_entries
   // If the value of the relevant merged entry changes, input_digest needs to
   // change. The new input_digest can be calculated by:
-  // new_input_digest = new_ukm_entry >> kTypeBits;
+  // new_input_digest = new_ukm_entry >> kTypeBits
   constexpr uint64_t input_digest = UINT64_C(33457614533296512);
-  // We collect all metrics together and check that there is one that contains
-  // the canvas readback metrics.
-  auto it = merged_entries.begin();
-  auto metrics = it->second->metrics;
-
-  for (auto& it : merged_entries) {
-    metrics.insert(it.second->metrics.begin(), it.second->metrics.end());
-  }
-
   EXPECT_THAT(metrics,
               IsSupersetOf({
                   Key(blink::IdentifiableSurface::FromTypeAndToken(
@@ -526,6 +430,7 @@
 }
 
 namespace {
+
 class PrivacyBudgetGroupConfigBrowserTest : public PlatformBrowserTest {
  public:
   PrivacyBudgetGroupConfigBrowserTest() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index e4b5a1b..6cc4f90 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -36,6 +36,7 @@
   "background/chromevox_state.js",
   "background/command_handler_interface.js",
   "background/event_source.js",
+  "background/focus_bounds.js",
   "background/logging/log_store.js",
   "background/output/output.js",
   "background/output/output_ancestry_info.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 6cb17d57..de77444 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -136,7 +136,7 @@
     // There's nothing to be updated in this case.
     if ((!newRange && !this.currentRange_) ||
         (newRange && !newRange.isValid())) {
-      ChromeVoxState.instance.setFocusBounds([]);
+      FocusBounds.set([]);
       return;
     }
 
@@ -147,7 +147,7 @@
         observer => observer.onCurrentRangeChanged(newRange, opt_fromEditing));
 
     if (!this.currentRange_) {
-      ChromeVoxState.instance.setFocusBounds([]);
+      FocusBounds.set([]);
       return;
     }
 
@@ -444,4 +444,4 @@
 }
 
 InstanceChecker.closeExtraInstances();
-new Background();
+ChromeVoxState.instance = new Background();
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 8cec4e28..0ed3d12 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -3120,14 +3120,12 @@
 TEST_F('ChromeVoxBackgroundTest', 'VolumeChanges', async function() {
   const mockFeedback = this.createMockFeedback();
   await this.runWithLoadedTree('<p>test</p>');
-  const bounds = ChromeVoxState.instance.getFocusBounds();
+  const bounds = FocusBounds.get();
   mockFeedback.call(press(KeyCode.VOLUME_UP))
       .expectSpeech('Volume', 'Slider', /\d+%/)
       .call(() => {
         // The bounds should not have changed.
-        assertEquals(
-            JSON.stringify(bounds),
-            JSON.stringify(ChromeVoxState.instance.getFocusBounds()));
+        assertEquals(JSON.stringify(bounds), JSON.stringify(FocusBounds.get()));
       })
       .replay();
 });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
index eb2038cb9..393d33d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -30,41 +30,11 @@
   onCurrentRangeChanged(range, opt_fromEditing) {}
 };
 
-/**
- * ChromeVox state object.
- * @constructor
- */
-ChromeVoxState = function() {
-  if (ChromeVoxState.instance) {
-    throw 'Trying to create two instances of singleton ChromeVoxState.';
-  }
-  ChromeVoxState.instance = this;
-
-  /** @private {!Array<!chrome.accessibilityPrivate.ScreenRect>} */
-  this.focusBounds_ = [];
-};
-
-/**
- * @type {ChromeVoxState}
- */
-ChromeVoxState.instance;
-
-/**
- * Holds the un-composite tts object.
- * @type {Object}
- */
-ChromeVoxState.backgroundTts;
-
-/**
- * @type {boolean}
- */
-ChromeVoxState.isReadingContinuously;
-
-ChromeVoxState.prototype = {
+ChromeVoxState = class {
   /** @return {cursors.Range} */
   get currentRange() {
     return this.getCurrentRange();
-  },
+  }
 
   /**
    * @return {cursors.Range} The current range.
@@ -72,33 +42,38 @@
    */
   getCurrentRange() {
     return null;
-  },
+  }
 
   /** @return {cursors.Range} */
   get pageSel() {
     return null;
-  },
+  }
 
   /**
    * Return the current range, but focus recovery is not applied to it.
    * @return {cursors.Range} The current range.
+   * @abstract
    */
-  getCurrentRangeWithoutRecovery: goog.abstractMethod,
+  getCurrentRangeWithoutRecovery() {}
 
   /**
    * @param {cursors.Range} newRange The new range.
    * @param {boolean=} opt_fromEditing
+   * @abstract
    */
-  setCurrentRange: goog.abstractMethod,
+  setCurrentRange(newRange, opt_fromEditing) {}
 
-  /** @param {cursors.Range} */
-  set pageSel(newPageSel) {},
+  /**
+   * @param {cursors.Range}
+   * @abstract
+   */
+  set pageSel(newPageSel) {}
 
   /** @return {number} */
-  get typingEcho() {},
+  get typingEcho() {}
 
   /** @param {number} newTypingEcho */
-  set typingEcho(newTypingEcho) {},
+  set typingEcho(newTypingEcho) {}
 
   /**
    * Navigate to the given range - it both sets the range and outputs it.
@@ -107,49 +82,45 @@
    * @param {Object=} opt_speechProps Speech properties.
    * @param {boolean=} opt_skipSettingSelection If true, does not set
    *     the selection, otherwise it does by default.
+   * @abstract
    */
-  navigateToRange: goog.abstractMethod,
+  navigateToRange(range, opt_focus, opt_speechProps, opt_skipSettingSelection) {
+  }
 
   /**
    * Restores the last valid ChromeVox range.
+   * @abstract
    */
-  restoreLastValidRangeIfNeeded: goog.abstractMethod,
+  restoreLastValidRangeIfNeeded() {}
 
   /**
    * Handles a braille command.
    * @param {!BrailleKeyEvent} evt
    * @param {!NavBraille} content
    * @return {boolean} True if evt was processed.
+   * @abstract
    */
-  onBrailleKeyEvent: goog.abstractMethod,
-
-  /**
-   * Gets the bounds of the focus ring.
-   * @return {Array<chrome.accessibilityPrivate.ScreenRect>}
-   */
-  getFocusBounds() {
-    return this.focusBounds_;
-  },
-
-  /**
-   * Sets the bounds of the focus ring.
-   * @param {!Array<!chrome.accessibilityPrivate.ScreenRect>} bounds
-   */
-  setFocusBounds(bounds) {
-    this.focusBounds_ = bounds;
-    chrome.accessibilityPrivate.setFocusRings([{
-      rects: bounds,
-      type: chrome.accessibilityPrivate.FocusType.GLOW,
-      color: constants.FOCUS_COLOR
-    }]);
-  },
+  onBrailleKeyEvent(evt, content) {}
 
   /**
    * Forces the reading of the next change to the clipboard.
+   * @abstract
    */
-  readNextClipboardDataChange: goog.abstractMethod,
+  readNextClipboardDataChange() {}
 };
 
+/** @type {ChromeVoxState} */
+ChromeVoxState.instance;
+
+/**
+ * Holds the un-composite tts object.
+ * @type {Object}
+ */
+ChromeVoxState.backgroundTts;
+
+/** @type {boolean} */
+ChromeVoxState.isReadingContinuously;
+
 /** @type {!Array<ChromeVoxStateObserver>} */
 ChromeVoxState.observers = [];
 
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 9bfb20b..3cad74e2 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -1550,5 +1550,6 @@
 CommandHandlerInterface.instance = new CommandHandler();
 
 BridgeHelper.registerHandler(
-    BridgeTargets.COMMAND_HANDLER, BridgeActions.ON_COMMAND,
+    BridgeConstants.CommandHandler.TARGET,
+    BridgeConstants.CommandHandler.Action.ON_COMMAND,
     (command) => CommandHandlerInterface.instance.onCommand(command));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
index 987ec4e..575d0c6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
@@ -42,5 +42,5 @@
     EventSourceType.NONE;
 
 BridgeHelper.registerHandler(
-    BridgeTargets.EVENT_SOURCE_STATE, BridgeActions.GET,
-    () => EventSourceState.get());
+    BridgeConstants.EventSourceState.TARGET,
+    BridgeConstants.EventSourceState.Action.GET, () => EventSourceState.get());
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_bounds.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_bounds.js
new file mode 100644
index 0000000..911aadce
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_bounds.js
@@ -0,0 +1,30 @@
+// 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 Stores the current focus bounds and manages setting the focus
+ * ring location.
+ */
+
+goog.provide('FocusBounds');
+
+FocusBounds = {
+  /** @return {!Array<!chrome.accessibilityPrivate.ScreenRect>} */
+  get() {
+    return FocusBounds.current_;
+  },
+
+  /** @param {!Array<!chrome.accessibilityPrivate.ScreenRect>} bounds */
+  set(bounds) {
+    FocusBounds.current_ = bounds;
+    chrome.accessibilityPrivate.setFocusRings([{
+      rects: bounds,
+      type: chrome.accessibilityPrivate.FocusType.GLOW,
+      color: constants.FOCUS_COLOR
+    }]);
+  }
+};
+
+/** @private {!Array<!chrome.accessibilityPrivate.ScreenRect>} */
+FocusBounds.current_ = [];
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index a197b19..9f36869 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -21,6 +21,7 @@
 goog.require('CommandHandlerInterface');
 goog.require('EventSourceState');
 goog.require('ExtraCellsSpan');
+goog.require('FocusBounds');
 goog.require('KeyCode');
 goog.require('KeySequence');
 goog.require('LibLouis');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index f718379b..430a4b1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -12,6 +12,7 @@
 goog.require('AutomationTreeWalker');
 goog.require('ChromeVox');
 goog.require('EventSourceState');
+goog.require('FocusBounds');
 goog.require('LocaleOutputHelper');
 goog.require('LogStore');
 goog.require('NavBraille');
@@ -596,7 +597,7 @@
 
     // Display.
     if (this.speechCategory_ !== TtsCategory.LIVE && this.drawFocusRing_) {
-      ChromeVoxState.instance.setFocusBounds(this.locations_);
+      FocusBounds.set(this.locations_);
     }
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
index 31fcc33b..0c3e945 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
@@ -1321,9 +1321,7 @@
   const site = `<button></button>`;
   const root = await this.runWithLoadedTree(site);
   let called = false;
-  ChromeVoxState.instance.setFocusBounds = this.newCallback(() => {
-    called = true;
-  });
+  FocusBounds.set = this.newCallback(() => called = true);
 
   const button = root.find({role: RoleType.BUTTON});
 
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 12532a1d..30cd5f1 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
@@ -223,15 +223,15 @@
   onLocationChanged(evt) {
     const cur = ChromeVoxState.instance.currentRange;
     if (!cur || !cur.isValid()) {
-      if (ChromeVoxState.instance.getFocusBounds().length) {
-        ChromeVoxState.instance.setFocusBounds([]);
+      if (FocusBounds.get().length) {
+        FocusBounds.set([]);
       }
       return;
     }
 
     // Rather than trying to figure out if the current range falls somewhere
     // in |evt.target|, just update it if our cached bounds don't match.
-    const oldFocusBounds = ChromeVoxState.instance.getFocusBounds();
+    const oldFocusBounds = FocusBounds.get();
     const startRect = cur.start.node.location;
     const endRect = cur.end.node.location;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
index a101d22..9570c62 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
@@ -125,7 +125,8 @@
    */
   async onCommand(command) {
     return BridgeHelper.sendMessage(
-        BridgeTargets.COMMAND_HANDLER, BridgeActions.ON_COMMAND, command);
+        BridgeConstants.CommandHandler.TARGET,
+        BridgeConstants.CommandHandler.Action.ON_COMMAND, command);
   },
 };
 
@@ -136,7 +137,8 @@
    */
   async get() {
     return BridgeHelper.sendMessage(
-        BridgeTargets.EVENT_SOURCE_STATE, BridgeActions.GET);
+        BridgeConstants.EventSourceState.TARGET,
+        BridgeConstants.EventSourceState.Action.GET);
   },
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js
index c0c62b2..82d4d91 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js
@@ -16,8 +16,6 @@
   CHROMEVOX_BACKGROUND: 'ChromeVoxBackground',
   CHROMEVOX_PREFS: 'ChromeVoxPrefs',
   CHROMEVOX_STATE: 'ChromeVoxState',
-  COMMAND_HANDLER: 'CommandHandler',
-  EVENT_SOURCE_STATE: 'EventSourceState',
   EVENT_STREAM_LOGGER: 'EventStreamLogger',
   GESTURE_COMMAND_HANDLER: 'GestureCommandHandler',
   LOG_STORE: 'LogStore',
@@ -50,6 +48,24 @@
       SET_ENABLED: 'setEnabled',
     },
   },
+
+  CommandHandler: {
+    /** @public {BridgeTarget} */
+    TARGET: 'CommandHandler',
+    /** @enum {string} */
+    Action: {
+      ON_COMMAND: 'onCommand',
+    },
+  },
+
+  EventSourceState: {
+    /** @public {BridgeTarget} */
+    TARGET: 'EventSourceState',
+    /** @enum {string} */
+    Action: {
+      GET: 'get',
+    },
+  },
 };
 
 /**
@@ -65,7 +81,6 @@
   DESTROY: 'destroy',
   DESTROY_I_SEARCH: 'destroyISearch',
   FOCUS_TAB: 'focusTab',
-  GET: 'get',
   GET_ACTIONS_FOR_CURRENT_NODE: 'getActionsForCurrentNode',
   GET_CURRENT_VOICE: 'getCurrentVoice',
   GET_LOGS: 'getLogs',
@@ -74,7 +89,6 @@
   INCREMENTAL_SEARCH: 'incrementalSearch',
   NODE_MENU_CALLBACK: 'nodeMenuCallback',
   NOTIFY_EVENT_STREAM_FILTER_CHANGED: 'notifyEventStreamFilterChanged',
-  ON_COMMAND: 'onCommand',
   ON_CURRENT_RANGE_CHANGED: 'onCurrentRangeChanged',
   PERFORM_CUSTOM_ACTION_ON_CURRENT_NODE: 'performCustomActionOnCurrentNode',
   PERFORM_STANDARD_ACTION_ON_CURRENT_NODE: 'performStandardActionOnCurrentNode',
@@ -90,6 +104,8 @@
  * The action that the message is requesting be performed.
  * @typedef {BridgeActions |
  *           BridgeConstants.BrailleBackground.Action |
- *           BridgeConstants.BrailleCommandHandler.Action}
+ *           BridgeConstants.BrailleCommandHandler.Action |
+ *           BridgeConstants.CommandHandler.Action |
+ *           BridgeConstants.EventSourceState.Action}
  */
 BridgeAction;
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index 02e3fee..482f737 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -284,8 +284,6 @@
     "screens/common/parental_handoff.js",
     "screens/common/pin_setup.html",
     "screens/common/pin_setup.js",
-    "screens/common/recommend_apps_old.html",
-    "screens/common/recommend_apps_old.js",
     "screens/common/recommend_apps.html",
     "screens/common/recommend_apps.js",
     "screens/common/saml_confirm_password.html",
@@ -449,7 +447,6 @@
     "screens/common/os_install.m.js",
     "screens/common/parental_handoff.m.js",
     "screens/common/pin_setup.m.js",
-    "screens/common/recommend_apps_old.m.js",
     "screens/common/recommend_apps.m.js",
     "screens/common/saml_confirm_password.m.js",
     "screens/common/signin_fatal_error.m.js",
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index d572939..398b3e6 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -1209,49 +1209,6 @@
         },
       ],
     },
-    // TODO(crbug.com/1261902): Remove.
-    {
-      id: 'recommend-apps-old',
-      kind: ScreenKind.NORMAL,
-      handledSteps: 'list',
-      // Known issue: reset() does not clear list of apps, so loadAppList
-      // will append apps instead of replacing.
-      states: [
-        {
-          id: '2-apps',
-          trigger: (screen) => {
-            screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_OLD_CONTENT);
-            screen.loadAppList([
-              {
-                name: 'Test app 1',
-                package_name: 'test1.app',
-              },
-              {
-                name: 'Test app 2 with some really long name',
-                package_name: 'test2.app',
-              },
-            ]);
-          },
-        },
-        {
-          id: '21-apps',
-          trigger: (screen) => {
-            // There can be up to 21 apps: see recommend_apps_fetcher_impl
-            screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_OLD_CONTENT);
-            const apps = [];
-            for (let i = 1; i <= 21; i++) {
-              apps.push({
-                name: 'Test app ' + i,
-                package_name: 'app.test' + i,
-              });
-            }
-            screen.loadAppList(apps);
-          },
-        },
-      ],
-    },
     {
       id: 'recommend-apps',
       kind: ScreenKind.NORMAL,
@@ -1263,7 +1220,7 @@
           id: '2-apps',
           trigger: (screen) => {
             screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_CONTENT);
+            screen.setWebview(RECOMMENDED_APPS_OLD_CONTENT);
             screen.loadAppList([
               {
                 name: 'Test app 1',
@@ -1281,7 +1238,7 @@
           trigger: (screen) => {
             // There can be up to 21 apps: see recommend_apps_fetcher_impl
             screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_CONTENT);
+            screen.setWebview(RECOMMENDED_APPS_OLD_CONTENT);
             const apps = [];
             for (let i = 1; i <= 21; i++) {
               apps.push({
diff --git a/chrome/browser/resources/chromeos/login/debug/debug_util.js b/chrome/browser/resources/chromeos/login/debug/debug_util.js
index 615304b6..1ffea53 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug_util.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug_util.js
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 // TODO(crbug.com/1229130) - Remove this global namespace pollution.
-// TODO(crbug.com/1261902): Remove from the place it will be moved to after
-// crbug.com/1229130.
+// TODO(crbug.com/1261902): Remove once crbug.com/1229130 is tackled.
 window.RECOMMENDED_APPS_OLD_CONTENT = `
   // <include src="../../arc_support/recommend_app_old_list_view.html">
   `;
diff --git a/chrome/browser/resources/chromeos/login/screens.js b/chrome/browser/resources/chromeos/login/screens.js
index 99ce1b9e..1c262ccb 100644
--- a/chrome/browser/resources/chromeos/login/screens.js
+++ b/chrome/browser/resources/chromeos/login/screens.js
@@ -32,7 +32,6 @@
 import './screens/common/os_trial.m.js';
 import './screens/common/parental_handoff.m.js';
 import './screens/common/pin_setup.m.js';
-import './screens/common/recommend_apps_old.m.js'; // TODO(crbug.com/1261902): Remove.
 import './screens/common/recommend_apps.m.js';
 import './screens/common/saml_confirm_password.m.js';
 import './screens/common/signin_fatal_error.m.js';
@@ -42,7 +41,6 @@
 import './screens/common/tpm_error.m.js';
 import './screens/common/user_creation.m.js';
 import './screens/common/wrong_hwid.m.js';
-
 // SCREENS USED DURING THE LOGIN FLOW
 import './screens/login/active_directory_password_change.m.js';
 import './screens/login/encryption_migration.m.js';
@@ -51,7 +49,6 @@
 import './screens/login/management_transition.m.js';
 import './screens/login/offline_login.m.js';
 import './screens/login/update_required_card.m.js';
-
 // SCREENS USED DURING THE OOBE FLOW
 import './screens/oobe/auto_enrollment_check.m.js';
 import './screens/oobe/demo_preferences.m.js';
@@ -95,7 +92,6 @@
    {tag: 'os-trial-element', id: 'os-trial', condition: 'isOsInstallAllowed'},
    {tag: 'parental-handoff-element', id: 'parental-handoff'},
    {tag: 'pin-setup-element', id: 'pin-setup'},
-   {tag: 'recommend-apps-old-element', id: 'recommend-apps-old'}, // TODO(crbug.com/1261902): Remove.
    {tag: 'recommend-apps-element', id: 'recommend-apps'},
    {tag: 'saml-confirm-password-element', id: 'saml-confirm-password'},
    {tag: 'signin-fatal-error-element', id: 'signin-fatal-error'},
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index 1e8946d..76a0ed2 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -39,7 +39,6 @@
     ":parental_handoff_module",
     ":pin_setup_module",
     ":recommend_apps_module",
-    ":recommend_apps_old_module",
     ":saml_confirm_password_module",
     ":signin_fatal_error_module",
     ":smart_privacy_protection_module",
@@ -89,7 +88,6 @@
     ":parental_handoff.m",
     ":pin_setup.m",
     ":recommend_apps.m",
-    ":recommend_apps_old.m",
     ":saml_confirm_password.m",
     ":signin_fatal_error.m",
     ":smart_privacy_protection.m",
@@ -518,22 +516,6 @@
   extra_deps = [ ":pin_setup_module" ]
 }
 
-js_library("recommend_apps_old.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.m.js" ]
-  deps = [
-    "../../components:oobe_types.m",
-    "../../components/behaviors:login_screen_behavior.m",
-    "../../components/behaviors:multi_step_behavior.m",
-    "../../components/behaviors:oobe_dialog_host_behavior.m",
-    "../../components/behaviors:oobe_i18n_behavior.m",
-    "../../components/buttons:oobe_text_button.m",
-    "../../components/dialogs:oobe_adaptive_dialog.m",
-    "../../components/dialogs:oobe_loading_dialog.m",
-  ]
-  externs_list = [ "$externs_path/webview_tag.js" ]
-  extra_deps = [ ":recommend_apps_old_module" ]
-}
-
 js_library("recommend_apps.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.m.js" ]
   deps = [
@@ -545,6 +527,7 @@
     "../../components/buttons:oobe_text_button.m",
     "../../components/dialogs:oobe_adaptive_dialog.m",
     "../../components/dialogs:oobe_loading_dialog.m",
+    "//ui/webui/resources/js:load_time_data.m",
   ]
   externs_list = [ "$externs_path/webview_tag.js" ]
   extra_deps = [ ":recommend_apps_module" ]
@@ -876,14 +859,6 @@
   namespace_rewrites = oobe_namespace_rewrites
 }
 
-polymer_modulizer("recommend_apps_old") {
-  js_file = "recommend_apps_old.js"
-  html_file = "recommend_apps_old.html"
-  html_type = "dom-module"
-  auto_imports = oobe_auto_imports
-  namespace_rewrites = oobe_namespace_rewrites
-}
-
 polymer_modulizer("recommend_apps") {
   js_file = "recommend_apps.js"
   html_file = "recommend_apps.html"
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
index 00d91c8..977e163 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
@@ -125,22 +125,25 @@
 
     const appListView = this.$.appView;
     appListView.addEventListener('contentload', () => {
-      appListView.executeScript({file: 'recommend_app_list_view.js'}, () => {
-        appListView.contentWindow.postMessage('initialMessage', '*');
+      appListView.executeScript(
+          {file: 'recommend_app_old_list_view.js'}, () => {
+            appListView.contentWindow.postMessage('initialMessage', '*');
 
-        appList.forEach(function(app_data, index) {
-          const app = /** @type {OobeTypes.RecommendedAppsExpectedAppData} */ (app_data);
-          const generateItemScript = 'generateContents("' + app.icon + '", "' +
-              app.name + '", "' + app.package_name + '");';
-          const generateContents = {code: generateItemScript};
-          appListView.executeScript(generateContents);
-        });
+            appList.forEach(function(app_data, index) {
+              const app =
+                  /** @type {OobeTypes.RecommendedAppsExpectedAppData} */ (
+                      app_data);
+              const generateItemScript = 'generateContents("' + app.icon +
+                  '", "' + app.name + '", "' + app.package_name + '");';
+              const generateContents = {code: generateItemScript};
+              appListView.executeScript(generateContents);
+            });
 
-        const getNumOfSelectedAppsScript = 'sendNumberOfSelectedApps();';
-        appListView.executeScript({code: getNumOfSelectedAppsScript});
+            const getNumOfSelectedAppsScript = 'sendNumberOfSelectedApps();';
+            appListView.executeScript({code: getNumOfSelectedAppsScript});
 
-        this.onFullyLoaded_();
-      });
+            this.onFullyLoaded_();
+          });
     });
   }
 
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.html b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.html
deleted file mode 100644
index 3a988d7f..0000000
--- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.html
+++ /dev/null
@@ -1,75 +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. -->
-
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-
-<link rel="import" href="../../display_manager.html">
-<link rel="import" href="../../components/display_manager_types.html">
-<link rel="import" href="../../components/behaviors/login_screen_behavior.html">
-<link rel="import" href="../../components/behaviors/multi_step_behavior.html">
-<link rel="import" href="../../components/behaviors/oobe_dialog_host_behavior.html">
-<link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html">
-<link rel="import" href="../../components/buttons/oobe_text_button.html">
-<link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html">
-<link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html">
-<link rel="import" href="../../components/oobe_types.html">
-
-<dom-module id="recommend-apps-old-element">
-  <template>
-    <style include="oobe-dialog-host-styles action-link">
-      #skipButton {
-        color: rgba(0, 0, 0, 0.54);
-        padding-inline-end: 6px;
-      }
-
-      #app-list-view-container {
-        display: flex;
-        flex-direction: column;
-      }
-
-      #selectAllButton {
-        display: flex;
-        justify-content: flex-end;
-        margin-bottom: 16px;
-      }
-    </style>
-    <oobe-loading-dialog id="loadingDialog" for-step="loading"
-        title-key="recommendAppsLoading">
-      <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-    </oobe-loading-dialog>
-    <oobe-adaptive-dialog id="appsDialog" role="dialog"
-        aria-label$="[[i18nDynamic(locale, 'recommendAppsScreenTitle')]]"
-        no-footer-padding footer-shrinkable for-step="list">
-      <iron-icon src="chrome://oobe/playstore.svg" slot="icon">
-      </iron-icon>
-      <h1 slot="title">
-        [[i18nDynamic(locale, 'recommendAppsScreenTitle')]]
-      </h1>
-      <div slot="subtitle">
-        [[i18nDynamic(locale, 'recommendAppsScreenDescription', appCount_)]]
-      </div>
-      <div id="app-list-view-container" slot="content">
-        <div id="selectAllButton">
-          <a class="oobe-local-link focus-on-show" is="action-link"
-              on-click="onSelectAll_">
-            [[i18nDynamic(locale, 'recommendAppsSelectAll')]]
-          </a>
-        </div>
-        <webview id="appView" on-message="onMessage_"></webview>
-      </div>
-      <div slot="bottom-buttons">
-        <oobe-text-button id="skipButton"
-            text-key="recommendAppsSkip" on-click="onSkip_" border>
-        </oobe-text-button>
-        <oobe-text-button id="installButton" on-click="onInstall_" inverse
-            text-key="recommendAppsInstall"
-            disabled="[[!canProceed_(appsSelected_)]]">
-        </oobe-text-button>
-      </div>
-    </oobe-adaptive-dialog>
-  </template>
-  <script src="recommend_apps_old.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.js b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.js
deleted file mode 100644
index b7b1ad61..0000000
--- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps_old.js
+++ /dev/null
@@ -1,209 +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.
-
-/**
- * @fileoverview Polymer element for displaying material design Recommend Apps
- * screen.
- */
-
-/* #js_imports_placeholder */
-
-/**
- * UI mode for the dialog.
- * @enum {string}
- */
-const RecommendAppsOldUiState = {
-  LOADING: 'loading',
-  LIST: 'list',
-};
-
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {LoginScreenBehaviorInterface}
- * @implements {MultiStepBehaviorInterface}
- */
-const RecommendAppsOldElementBase = Polymer.mixinBehaviors(
-    [
-      OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior,
-      MultiStepBehavior
-    ],
-    Polymer.Element);
-
-/**
- * @typedef {{
- *   appsDialog:  OobeAdaptiveDialogElement,
- *   appView:  WebView,
- *   installButton:  OobeTextButton,
- * }}
- */
-RecommendAppsOldElementBase.$;
-
-/**
- * @polymer
- */
-class RecommendAppsOldElement extends RecommendAppsOldElementBase {
-  static get is() {
-    return 'recommend-apps-old-element';
-  }
-
-  /* #html_template_placeholder */
-
-  static get properties() {
-    return {
-      appCount_: {
-        type: Number,
-        value: 0,
-      },
-
-      appsSelected_: {
-        type: Number,
-        value: 0,
-      },
-    };
-  }
-
-  constructor() {
-    super();
-    this.initialized_ = false;
-  }
-
-  get EXTERNAL_API() {
-    return ['setWebview', 'loadAppList'];
-  }
-
-  get UI_STEPS() {
-    return RecommendAppsOldUiState;
-  }
-
-  ready() {
-    super.ready();
-    this.initializeLoginScreen('RecommendAppsOldScreen');
-    window.addEventListener('message', this.onMessage_.bind(this));
-  }
-
-  /**
-   * Resets screen to initial state.
-   * Currently is used for debugging purposes only.
-   */
-  reset() {
-    this.setUIStep(RecommendAppsOldUiState.LOADING);
-    this.appCount_ = 0;
-    this.appsSelected_ = 0;
-  }
-
-  /**
-   * Returns the control which should receive initial focus.
-   */
-  get defaultControl() {
-    return this.$.appsDialog;
-  }
-
-  defaultUIStep() {
-    return RecommendAppsOldUiState.LOADING;
-  }
-
-  /**
-   * Initial UI State for screen
-   */
-  getOobeUIInitialState() {
-    return OOBE_UI_STATE.ONBOARDING;
-  }
-
-  setWebview(contents) {
-    cr.ui.login.invokePolymerMethod(this.$.appsDialog, 'onBeforeShow');
-    this.$.appView.src =
-        'data:text/html;charset=utf-8,' + encodeURIComponent(contents);
-  }
-
-  /**
-   * Generates the contents in the webview.
-   * It is assumed that |loadAppList| is called only once after |setWebview|.
-   */
-  loadAppList(appList) {
-    this.appCount_ = appList.length;
-
-    const appListView = this.$.appView;
-    appListView.addEventListener('contentload', () => {
-      appListView.executeScript(
-          {file: 'recommend_app_old_list_view.js'}, () => {
-            appListView.contentWindow.postMessage('initialMessage', '*');
-
-            appList.forEach(function(app_data, index) {
-              const app =
-                  /** @type {OobeTypes.RecommendedAppsExpectedAppData} */ (
-                      app_data);
-              const generateItemScript = 'generateContents("' + app.icon +
-                  '", "' + app.name + '", "' + app.package_name + '");';
-              const generateContents = {code: generateItemScript};
-              appListView.executeScript(generateContents);
-            });
-
-            const getNumOfSelectedAppsScript = 'sendNumberOfSelectedApps();';
-            appListView.executeScript({code: getNumOfSelectedAppsScript});
-
-            this.onFullyLoaded_();
-          });
-    });
-  }
-
-  /**
-   * Handles event when contents in the webview is generated.
-   */
-  onFullyLoaded_() {
-    const appListView = this.$.appView;
-    appListView.executeScript({code: 'getHeight();'}, function(result) {
-      appListView.setAttribute('style', 'height: ' + result + 'px');
-    });
-    this.setUIStep(RecommendAppsOldUiState.LIST);
-    this.$.installButton.focus();
-  }
-
-  /**
-   * Handles Skip button click.
-   */
-  onSkip_() {
-    chrome.send('recommendAppsSkip');
-  }
-
-  /**
-   * Handles Install button click.
-   */
-  onInstall_() {
-    // Only start installation if there are apps to install.
-    if (this.appsSelected_ > 0) {
-      const appListView = this.$.appView;
-      appListView.executeScript(
-          {code: 'getSelectedPackages();'}, function(result) {
-            chrome.send('recommendAppsInstall', [result[0]]);
-          });
-    }
-  }
-
-  /**
-   * Handles the message sent from the WebView.
-   * @param {Event} event
-   */
-  onMessage_(event) {
-    const data =
-        /** @type {OobeTypes.RecommendedAppsSelectionEventData} */ (event.data);
-    if (data.type && (data.type === 'NUM_OF_SELECTED_APPS')) {
-      this.appsSelected_ = data.numOfSelected;
-    }
-  }
-
-  /**
-   * Handles Select all button click.
-   */
-  onSelectAll_() {
-    const appListView = this.$.appView;
-    appListView.executeScript({code: 'selectAll();'});
-  }
-
-  canProceed_(appsSelected) {
-    return appsSelected > 0;
-  }
-}
-
-customElements.define(RecommendAppsOldElement.is, RecommendAppsOldElement);
diff --git a/chrome/browser/resources/chromeos/login/structure/components_common.html b/chrome/browser/resources/chromeos/login/structure/components_common.html
index 28e45d77..af10a23 100644
--- a/chrome/browser/resources/chromeos/login/structure/components_common.html
+++ b/chrome/browser/resources/chromeos/login/structure/components_common.html
@@ -32,8 +32,6 @@
 <link rel="import" href="/screens/common/oobe_reset.html">
 <link rel="import" href="/screens/common/parental_handoff.html">
 <link rel="import" href="/screens/common/pin_setup.html">
-<!-- TODO(crbug.com/1261902): Remove. -->
-<link rel="import" href="/screens/common/recommend_apps_old.html">
 <link rel="import" href="/screens/common/recommend_apps.html">
 <link rel="import" href="/screens/common/saml_confirm_password.html">
 <link rel="import" href="/screens/common/signin_fatal_error.html">
diff --git a/chrome/browser/resources/chromeos/login/structure/screens_common.html b/chrome/browser/resources/chromeos/login/structure/screens_common.html
index 9d8f899..5fd4cbb 100644
--- a/chrome/browser/resources/chromeos/login/structure/screens_common.html
+++ b/chrome/browser/resources/chromeos/login/structure/screens_common.html
@@ -39,9 +39,6 @@
 </sync-consent-element>
 <fingerprint-setup-element id="fingerprint-setup" class="step hidden" hidden>
 </fingerprint-setup-element>
-<!-- TODO(crbug.com/1261902): Remove. -->
-<recommend-apps-old-element id="recommend-apps-old" class="step hidden" hidden>
-</recommend-apps-old-element>
 <recommend-apps-element id="recommend-apps" class="step hidden" hidden>
 </recommend-apps-element>
 <app-downloading-element id="app-downloading" class="step hidden" hidden>
diff --git a/chrome/browser/resources/chromeos/login/test_api/test_api.js b/chrome/browser/resources/chromeos/login/test_api/test_api.js
index cfce043..f89fbc7 100644
--- a/chrome/browser/resources/chromeos/login/test_api/test_api.js
+++ b/chrome/browser/resources/chromeos/login/test_api/test_api.js
@@ -254,6 +254,15 @@
 class SyncScreenTester extends ScreenElementApi {
   constructor() {
     super('sync-consent');
+    this.loadedStep = new PolymerElementApi(this, '#syncConsentOverviewDialog');
+  }
+
+  /**
+   * Returns if the Sync Consent Screen is ready for test interaction.
+   * @return {boolean}
+   */
+  isReadyForTesting() {
+    return this.isVisible() && this.loadedStep.isVisible();
   }
 }
 
diff --git a/chrome/browser/resources/default_apps/external_extensions.json b/chrome/browser/resources/default_apps/external_extensions.json
index a6a44c0..6c3660f1 100644
--- a/chrome/browser/resources/default_apps/external_extensions.json
+++ b/chrome/browser/resources/default_apps/external_extensions.json
@@ -1,36 +1,6 @@
 // Dictionary of default apps to install into new profiles. They will be
 // dynamically downloaded and installed from CWS on profile creation.
 {
-  // YouTube
-  "blpcfgokakmgnkcojhhkbfbldkacnbeo" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsNonGSuite"
-  },
-  // GMail
-  "pjkljhegncpnkpknbcohdijeoejaedia" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsGSuite"
-  },
-  // Google Drive
-  "apdfllckaahabafndbhieahigkjlhalf" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsGSuite"
-  },
-  // Google Docs
-  "aohghmighlieiainnegkcijnfilokake" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsGSuite"
-  },
-  // Google Sheets
-  "aapocclcgogkmnckokdopfmhonfmgoek" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsGSuite"
-  },
-  // Google Slides
-  "felcaaldnbdncclmgdcncolpebgiejap" : {
-    "external_update_url": "https://clients2.google.com/service/update2/crx",
-    "web_app_migration_flag": "MigrateDefaultChromeAppToWebAppsGSuite"
-  },
   // Drive extension
   "ghbmnnjooekpmoecnnnilnnbdlolhkhi" : {
     "external_update_url": "https://clients2.google.com/service/update2/crx"
diff --git a/chrome/browser/resources/settings/autofill_page/password_list_item.html b/chrome/browser/resources/settings/autofill_page/password_list_item.html
index d73dd37..78dfcab 100644
--- a/chrome/browser/resources/settings/autofill_page/password_list_item.html
+++ b/chrome/browser/resources/settings/autofill_page/password_list_item.html
@@ -32,9 +32,16 @@
         text-overflow: ellipsis;
       }
 
+      :host([should-show-subpage-button_]) {
+        padding-inline-end: var(--password-list-item-padding-inline-end);
+        padding-inline-start: var(--password-list-item-padding-inline-start);
+      }
+
       :host([should-show-subpage-button_]:hover) {
         background-color: var(--cr-hover-background-color);
         cursor: pointer;
+        --cr-icon-button-hover-background-color: transparent;
+        --cr-icon-button-active-background-color: transparent;
       }
 
       :host([should-show-subpage-button_]) #username,
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
index 3efe8ff..ee64c50 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
@@ -23,6 +23,12 @@
   #googleGIcon {
     margin-inline-end: 16px;
   }
+
+  :host([is-password-notes-enabled_]) .passwords-wrapper {
+    padding: 0;
+    --password-list-item-padding-inline-end: var(--cr-section-padding);
+    --password-list-item-padding-inline-start: var(--cr-section-indent-padding);
+  }
 </style>
 <div id="moveMultiplePasswordsBanner" on-click="onMoveMultiplePasswordsTap_"
     class="cr-row two-line"
@@ -45,44 +51,54 @@
 </div>
 <passwords-list-handler id="passwordsListHandler" allow-move-to-account-option
     is-account-store-user saved-passwords="[[savedPasswords]]">
-  <div slot="body" class="list-frame">
-    <div class="list-item column-header" aria-hidden="true">
-        $i18n{deviceOnlyPasswordsHeading}
+  <div slot="body">
+    <div class="list-frame">
+      <div class="list-item column-header" aria-hidden="true">
+          $i18n{deviceOnlyPasswordsHeading}
+      </div>
     </div>
-    <iron-list id="deviceOnlyPasswordList" preserve-focus
-        items="[[getFilteredPasswords_(deviceOnlyPasswords_,filter)]]"
-        class="cr-separators list-with-header"
-        scroll-target="[[subpageScrollTarget]]" risk-selection>
-      <template>
-        <password-list-item entry="[[item]]"
-            tabindex$="[[tabIndex]]" focus-row-index="[[index]]"
-            first$="[[!index]]" iron-list-tab-index="[[tabIndex]]"
-            last-focused="{{lastFocused_}}" list-blurred="{{listBlurred_}}">
-        </password-list-item>
-      </template>
-    </iron-list>
-    <div id="noDeviceOnlyPasswordsLabel" class="list-item"
-        hidden$="[[isNonEmpty_(deviceOnlyPasswords_)]]">
+    <div class="list-frame passwords-wrapper">
+      <iron-list id="deviceOnlyPasswordList" preserve-focus
+          items="[[getFilteredPasswords_(deviceOnlyPasswords_,filter)]]"
+          class="cr-separators list-with-header"
+          scroll-target="[[subpageScrollTarget]]" risk-selection>
+        <template>
+          <password-list-item entry="[[item]]"
+              tabindex$="[[tabIndex]]" focus-row-index="[[index]]"
+              first$="[[!index]]" iron-list-tab-index="[[tabIndex]]"
+              last-focused="{{lastFocused_}}" list-blurred="{{listBlurred_}}">
+          </password-list-item>
+        </template>
+      </iron-list>
+    </div>
+    <div class="list-frame">
+      <div id="noDeviceOnlyPasswordsLabel" class="list-item"
+          hidden$="[[isNonEmpty_(deviceOnlyPasswords_)]]">
+          $i18n{noPasswordsFound}
+      </div>
+      <div class="list-item column-header" aria-hidden="true">
+          $i18n{deviceAndAccountPasswordsHeading}
+      </div>
+    </div>
+    <div class="list-frame passwords-wrapper">
+      <iron-list id="deviceAndAccountPasswordList" preserve-focus
+          items="[[getFilteredPasswords_(deviceAndAccountPasswords_,filter)]]"
+          class="cr-separators list-with-header"
+          scroll-target="[[subpageScrollTarget]]" risk-selection>
+        <template>
+          <password-list-item entry="[[item]]"
+              tabindex$="[[tabIndex]]" focus-row-index="[[index]]"
+              first$="[[!index]]" iron-list-tab-index="[[tabIndex]]"
+              last-focused="{{lastFocused_}}" list-blurred="{{listBlurred_}}">
+          </password-list-item>
+        </template>
+      </iron-list>
+    </div>
+    <div class="list-frame">
+      <div id="noDeviceAndAccountPasswordsLabel" class="list-item"
+          hidden$="[[isNonEmpty_(deviceAndAccountPasswords_)]]">
         $i18n{noPasswordsFound}
-    </div>
-    <div class="list-item column-header" aria-hidden="true">
-        $i18n{deviceAndAccountPasswordsHeading}
-    </div>
-    <iron-list id="deviceAndAccountPasswordList" preserve-focus
-        items="[[getFilteredPasswords_(deviceAndAccountPasswords_,filter)]]"
-        class="cr-separators list-with-header"
-        scroll-target="[[subpageScrollTarget]]" risk-selection>
-      <template>
-        <password-list-item entry="[[item]]"
-            tabindex$="[[tabIndex]]" focus-row-index="[[index]]"
-            first$="[[!index]]" iron-list-tab-index="[[tabIndex]]"
-            last-focused="{{lastFocused_}}" list-blurred="{{listBlurred_}}">
-        </password-list-item>
-      </template>
-    </iron-list>
-    <div id="noDeviceAndAccountPasswordsLabel" class="list-item"
-        hidden$="[[isNonEmpty_(deviceAndAccountPasswords_)]]">
-      $i18n{noPasswordsFound}
+      </div>
     </div>
   </div>
 </passwords-list-handler>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
index fa8f49c..2d1f7fa 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.ts
@@ -192,6 +192,14 @@
         type: String,
         value: '',
       },
+
+      isPasswordNotesEnabled_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('enablePasswordNotes');
+        },
+        reflectToAttribute: true,
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index a5d2fe10..0b270c8 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -78,6 +78,12 @@
       #devicePasswordsLinkLabel {
         flex-grow: 1;
       }
+
+      :host([is-password-notes-enabled_]) #passwordsWrapper {
+        padding: 0;
+        --password-list-item-padding-inline-end: var(--cr-section-padding);
+        --password-list-item-padding-inline-start: var(--cr-section-indent-padding);
+      }
    </style>
     <div class="cr-row first cr-secondary-text"
         hidden$="[[!isUnifiedPasswordManagerEnabled_]]">
@@ -188,71 +194,75 @@
 </if>
         is-account-store-user="[[isAccountStoreUser_]]"
         saved-passwords="[[savedPasswords]]">
-      <div slot="body" class="list-frame">
-        <div hidden$="[[!eligibleForAccountStorage_]]"
-            id="accountStorageButtonsContainer">
-          <div class="cr-row first two-line list-item"
-              id="accountStorageOptInButtonsContainer">
-            <settings-avatar-icon id="profileIcon"></settings-avatar-icon>
-            <div class="flex cr-padded-text">
-              <div id="accountStorageOptInBody"
+      <div slot="body">
+        <div class="list-frame">
+          <div hidden$="[[!eligibleForAccountStorage_]]"
+              id="accountStorageButtonsContainer">
+            <div class="cr-row first two-line list-item"
+                id="accountStorageOptInButtonsContainer">
+              <settings-avatar-icon id="profileIcon"></settings-avatar-icon>
+              <div class="flex cr-padded-text">
+                <div id="accountStorageOptInBody"
+                    hidden$="[[isOptedInForAccountStorage_]]">
+                  $i18nRaw{optInAccountStorageBody}
+                </div>
+                <div id="accountStorageOptOutBody"
+                   hidden$="[[!isOptedInForAccountStorage_]]">
+                  $i18nRaw{optOutAccountStorageBody}
+                </div>
+                <div id="accountEmail" class="secondary">[[profileEmail_]]</div>
+              </div>
+              <cr-button on-click="onOptIn_" id="optInToAccountStorageButton"
                   hidden$="[[isOptedInForAccountStorage_]]">
-                $i18nRaw{optInAccountStorageBody}
-              </div>
-              <div id="accountStorageOptOutBody"
+                $i18n{optInAccountStorageLabel}
+              </cr-button>
+              <cr-button on-click="onOptOut_" id="optOutOfAccountStorageButton"
                   hidden$="[[!isOptedInForAccountStorage_]]">
-                $i18nRaw{optOutAccountStorageBody}
+                $i18n{optOutAccountStorageLabel}
+              </cr-button>
+            </div>
+            <div id="devicePasswordsLink" class="cr-row two-line list-item"
+                hidden$="[[!shouldShowDevicePasswordsLink_]]"
+                on-click="onDevicePasswordsLinkClicked_">
+              <iron-icon id="devicePasswordsLinkIcon" icon="cr:computer">
+              </iron-icon>
+              <div id="devicePasswordsLinkLabel">
+                  $i18n{devicePasswordsLinkLabel}
               </div>
-              <div id="accountEmail" class="secondary">[[profileEmail_]]</div>
+              <cr-icon-button iron-icon="cr:arrow-right"
+                  aria-labelledby="devicePasswordsLinkLabel"
+                  aria-roledescription="$i18n{subpageArrowRoleDescription}">
+              </cr-icon-button>
             </div>
-            <cr-button on-click="onOptIn_" id="optInToAccountStorageButton"
-                hidden$="[[isOptedInForAccountStorage_]]">
-              $i18n{optInAccountStorageLabel}
-            </cr-button>
-            <cr-button on-click="onOptOut_" id="optOutOfAccountStorageButton"
-                hidden$="[[!isOptedInForAccountStorage_]]">
-              $i18n{optOutAccountStorageLabel}
-            </cr-button>
           </div>
-          <div id="devicePasswordsLink" class="cr-row two-line list-item"
-              hidden$="[[!shouldShowDevicePasswordsLink_]]"
-              on-click="onDevicePasswordsLinkClicked_">
-            <iron-icon id="devicePasswordsLinkIcon" icon="cr:computer">
-            </iron-icon>
-            <div id="devicePasswordsLinkLabel">
-                $i18n{devicePasswordsLinkLabel}
+          <div id="savedPasswordsHeaders" class="list-item column-header"
+              hidden$="[[!hasSavedPasswords_]]" aria-hidden="true">
+            <div class="website-column">$i18n{editPasswordWebsiteLabel}</div>
+            <div class="username-column">
+              $i18n{editPasswordUsernameLabel}
             </div>
-            <cr-icon-button iron-icon="cr:arrow-right"
-                aria-labelledby="devicePasswordsLinkLabel"
-                aria-roledescription="$i18n{subpageArrowRoleDescription}">
-            </cr-icon-button>
+            <div class="password-column">
+              $i18n{editPasswordPasswordLabel}
+            </div>
           </div>
         </div>
-        <div id="savedPasswordsHeaders" class="list-item column-header"
-            hidden$="[[!hasSavedPasswords_]]" aria-hidden="true">
-          <div class="website-column">$i18n{editPasswordWebsiteLabel}</div>
-          <div class="username-column">
-            $i18n{editPasswordUsernameLabel}
-          </div>
-          <div class="password-column">
-            $i18n{editPasswordPasswordLabel}
-          </div>
-        </div>
-        <div class="cr-separators list-with-header">
-          <template id="passwordList" is="dom-repeat" items="[[savedPasswords]]"
-              filter="[[passwordFilter_(filter)]]"
-              rendered-item-count="{{shownPasswordsCount_::dom-change}}">
-            <password-list-item entry="[[item]]"
+        <div id="passwordsWrapper" class="list-frame">
+          <div class="cr-separators list-with-header">
+            <template id="passwordList" is="dom-repeat" 
+                items="[[savedPasswords]]" filter="[[passwordFilter_(filter)]]"
+                rendered-item-count="{{shownPasswordsCount_::dom-change}}">
+              <password-list-item entry="[[item]]"
 <if expr="chromeos_ash or chromeos_lacros">
-                token-request-manager="[[tokenRequestManager]]"
+                  token-request-manager="[[tokenRequestManager]]"
 </if>
-               >
-            </password-list-item>
-          </template>
-        </div>
-        <div id="noPasswordsLabel" class="list-item"
-            hidden$="[[hasSavedPasswords_]]">
-          $i18n{noPasswordsFound}
+                >
+              </password-list-item>
+            </template>
+          </div>
+          <div id="noPasswordsLabel" class="list-item"
+              hidden$="[[hasSavedPasswords_]]">
+            $i18n{noPasswordsFound}
+          </div>
         </div>
       </div>
     </passwords-list-handler>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
index 463ecd2..1343186 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
@@ -244,6 +244,14 @@
             'eligibleForAccountStorage_)',
       },
 
+      isPasswordNotesEnabled_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('enablePasswordNotes');
+        },
+        reflectToAttribute: true,
+      },
+
       isUnifiedPasswordManagerEnabled_: {
         type: Boolean,
         value() {
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index 331d7fa..f1c7e43 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -19,7 +19,6 @@
 import '../../settings_page/settings_animated_pages.js';
 import '../../settings_page/settings_subpage.js';
 import '../../settings_shared_css.js';
-import '../parental_controls_page/parental_controls_page.js';
 import './account_manager.js';
 import './fingerprint_list.js';
 import './lock_screen.js';
@@ -43,6 +42,7 @@
 import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js';
 import {OSPageVisibility} from '../os_page_visibility.js';
 import {routes} from '../os_route.js';
+import {SettingsParentalControlsPageElement} from '../parental_controls_page/parental_controls_page.js';
 import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
 
 import {Account, AccountManagerBrowserProxyImpl} from './account_manager_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
index 310deba..0afc2d1 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
@@ -2,68 +2,78 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
-import '//resources/cr_elements/cr_icons_css.m.js';
-import '//resources/cr_elements/hidden_style_css.m.js';
-import '//resources/cr_elements/icons.m.js';
-import '//resources/cr_elements/shared_vars_css.m.js';
-import '//resources/polymer/v3_0/iron-media-query/iron-media-query.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_icons_css.m.js';
+import 'chrome://resources/cr_elements/hidden_style_css.m.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
 import '../os_settings_search_box/os_settings_search_box.js';
-import '//resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
 
-import {assert, assertNotReached} from '//resources/js/assert.m.js';
-import {loadTimeData} from '//resources/js/load_time_data.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  // Not "toolbar" because element names must contain a hyphen.
-  is: 'os-toolbar',
+/** @polymer */
+class OsToolbarElement extends PolymerElement {
+  static get is() {
+    return 'os-toolbar';
+  }
 
-  properties: {
-    // Value is proxied through to cr-toolbar-search-field. When true,
-    // the search field will show a processing spinner.
-    spinnerActive: Boolean,
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    // Controls whether the menu button is shown at the start of the menu.
-    showMenu: {type: Boolean, value: false},
+  static get properties() {
+    return {
+      // Value is proxied through to cr-toolbar-search-field. When true,
+      // the search field will show a processing spinner.
+      spinnerActive: Boolean,
 
-    // Controls whether the search field is shown.
-    showSearch: {type: Boolean, value: true},
+      // Controls whether the menu button is shown at the start of the menu.
+      showMenu: {type: Boolean, value: false},
 
-    // True when the toolbar is displaying in narrow mode.
-    narrow: {
-      type: Boolean,
-      value: false,
-      reflectToAttribute: true,
-    },
+      // Controls whether the search field is shown.
+      showSearch: {type: Boolean, value: true},
 
-    /**
-     * True when the toolbar is displaying in an extremely narrow mode that the
-     * viewport may cutoff an OsSettingsSearchBox with a specific px width.
-     * @private
-     */
-    isSearchBoxCutoff_: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
+      // True when the toolbar is displaying in narrow mode.
+      narrow: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
 
-    /** @private */
-    showingSearch_: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
-  },
+      /**
+       * True when the toolbar is displaying in an extremely narrow mode that
+       * the viewport may cutoff an OsSettingsSearchBox with a specific px
+       * width.
+       * @private
+       */
+      isSearchBoxCutoff_: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
+
+      /** @private */
+      showingSearch_: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
+    };
+  }
 
   /** @return {?CrToolbarSearchFieldElement} */
   getSearchField() {
     return /** @type {?CrToolbarSearchFieldElement} */ (
         this.shadowRoot.querySelector('os-settings-search-box')
-            .$$('cr-toolbar-search-field'));
-  },
+            .shadowRoot.querySelector('cr-toolbar-search-field'));
+  }
 
   /** @private */
   onMenuTap_() {
-    this.fire('os-toolbar-menu-tap');
-  },
-});
+    const event =
+        new CustomEvent('os-toolbar-menu-tap', {bubbles: true, composed: true});
+    this.dispatchEvent(event);
+  }
+}
+
+customElements.define(OsToolbarElement.is, OsToolbarElement);
diff --git a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.js b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.js
index f70f55a..60179d18 100644
--- a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_browser_proxy.js
@@ -2,14 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
-
 /**
  * @fileoverview
  * Browser Proxy for Parental Controls functions.
  */
 
-  /** @interface */
+/** @interface */
 export class ParentalControlsBrowserProxy {
   /**
    * Shows the Add Supervsion dialog.
@@ -35,6 +33,17 @@
   launchFamilyLinkSettings() {
     chrome.send('launchFamilyLinkSettings');
   }
+
+  /** @return {!ParentalControlsBrowserProxy} */
+  static getInstance() {
+    return instance || (instance = new ParentalControlsBrowserProxyImpl());
+  }
+
+  /** @param {!ParentalControlsBrowserProxy} obj */
+  static setInstance(obj) {
+    instance = obj;
+  }
 }
 
-addSingletonGetter(ParentalControlsBrowserProxyImpl);
+/** @type {?ParentalControlsBrowserProxy} */
+let instance = null;
diff --git a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
index 9b0b443..7dd5387b 100644
--- a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
+++ b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
@@ -7,66 +7,83 @@
  * Settings page for managing Parental Controls features.
  */
 
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/icons.m.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/icons.m.js';
 import '../../settings_page/settings_animated_pages.js';
 import '../../settings_page/settings_subpage.js';
 import '../../settings_shared_css.js';
 
-import {addWebUIListener, removeWebUIListener, sendWithPromise, WebUIListener} from '//resources/js/cr.m.js';
-import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
-import {routes} from '../os_route.js';
 
 import {ParentalControlsBrowserProxy, ParentalControlsBrowserProxyImpl} from './parental_controls_browser_proxy.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-parental-controls-page',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ */
+const SettingsParentalControlsPageElementBase =
+    mixinBehaviors([I18nBehavior], PolymerElement);
 
-  behaviors: [
-    I18nBehavior,
-  ],
+/** @polymer */
+export class SettingsParentalControlsPageElement extends
+    SettingsParentalControlsPageElementBase {
+  static get is() {
+    return 'settings-parental-controls-page';
+  }
 
-  properties: {
-    /** @private */
-    isChild_: {
-      type: Boolean,
-      value() {
-        return loadTimeData.getBoolean('isChild');
-      }
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @private */
-    online_: {
-      type: Boolean,
-      value() {
-        return navigator.onLine;
-      }
-    },
-  },
+  static get properties() {
+    return {
+      /** @private */
+      isChild_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('isChild');
+        }
+      },
+
+      /** @private */
+      online_: {
+        type: Boolean,
+        value() {
+          return navigator.onLine;
+        }
+      },
+    };
+  }
 
   /** @override */
-  created() {
+  constructor() {
+    super();
+
+    /** @private {!ParentalControlsBrowserProxy} */
     this.browserProxy_ = ParentalControlsBrowserProxyImpl.getInstance();
-  },
+  }
 
   /** @override */
   ready() {
+    super.ready();
+
     // Set up online/offline listeners.
     window.addEventListener('offline', this.onOffline_.bind(this));
     window.addEventListener('online', this.onOnline_.bind(this));
-  },
+  }
 
   /**
    * Returns the setup parental controls CrButtonElement.
    * @return {?CrButtonElement}
    */
   getSetupButton() {
-    return /** @type {?CrButtonElement} */ (this.$$('#setupButton'));
-  },
+    return /** @type {?CrButtonElement} */ (
+        this.shadowRoot.querySelector('#setupButton'));
+  }
 
   /**
    * Updates the UI when the device goes offline.
@@ -74,7 +91,7 @@
    */
   onOffline_() {
     this.online_ = false;
-  },
+  }
 
   /**
    * Updates the UI when the device comes online.
@@ -82,7 +99,7 @@
    */
   onOnline_() {
     this.online_ = true;
-  },
+  }
 
   /**
    * @return {string} Returns the string to display in the main
@@ -95,17 +112,21 @@
     } else {
       return this.i18n('parentalControlsPageConnectToInternetLabel');
     }
-  },
+  }
 
   /** @private */
   handleSetupButtonClick_(event) {
     event.stopPropagation();
     this.browserProxy_.showAddSupervisionDialog();
-  },
+  }
 
   /** @private */
   handleFamilyLinkButtonClick_(event) {
     event.stopPropagation();
     this.browserProxy_.launchFamilyLinkSettings();
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsParentalControlsPageElement.is,
+    SettingsParentalControlsPageElement);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
index 428e90e..07c6c600 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
@@ -16,7 +16,6 @@
 import androidx.appcompat.content.res.AppCompatResources;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.ui.widget.ChromeImageView;
 
 import java.util.Calendar;
@@ -29,8 +28,8 @@
 public class DevicePickerBottomSheetAdapter extends BaseAdapter {
     private final List<TargetDeviceInfo> mTargetDevices;
 
-    public DevicePickerBottomSheetAdapter(Profile profile) {
-        mTargetDevices = SendTabToSelfAndroidBridge.getAllTargetDeviceInfos(profile);
+    public DevicePickerBottomSheetAdapter(List<TargetDeviceInfo> targetDevices) {
+        mTargetDevices = targetDevices;
     }
 
     @Override
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
index f7e1fa1..cfc2e6c4 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
@@ -21,7 +21,6 @@
 import android.widget.TextView;
 
 import org.chromium.base.IntentUtils;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -43,9 +42,8 @@
 
 /**
  * Bottom sheet content to display a list of devices a user can send a tab to after they have
- * chosen to share it with themselves through the SendTabToSelfFeature. If the user is signed-out
- * or no target devices are available, a prompt will be shown indicating to the user that
- * they must sign in to use the feature.
+ * chosen to share it with themselves through the send-tab-to-self feature.
+ * TODO(crbug.com/1219434): Make this and other helper UI bits package-private.
  */
 public class DevicePickerBottomSheetContent implements BottomSheetContent, OnItemClickListener {
     private final Context mContext;
@@ -59,12 +57,12 @@
 
     private static final int ACCOUNT_AVATAR_SIZE_DP = 24;
 
-    public DevicePickerBottomSheetContent(
-            Context context, String url, String title, BottomSheetController controller) {
+    public DevicePickerBottomSheetContent(Context context, String url, String title,
+            BottomSheetController controller, List<TargetDeviceInfo> targetDevices) {
         mContext = context;
         mController = controller;
         mProfile = Profile.getLastUsedRegularProfile();
-        mAdapter = new DevicePickerBottomSheetAdapter(mProfile);
+        mAdapter = new DevicePickerBottomSheetAdapter(targetDevices);
         mUrl = url;
         mTitle = title;
 
@@ -80,22 +78,6 @@
     }
 
     private void createContentView() {
-        List<TargetDeviceInfo> targetDeviceList =
-                SendTabToSelfAndroidBridge.getAllTargetDeviceInfos(mProfile);
-
-        // First check if sharing is unavailable, e.g. because there are no target devices. If so,
-        // show send_tab_to_self_feature_unavailable_prompt.
-        if (targetDeviceList.isEmpty()) {
-            mContentView = (ViewGroup) LayoutInflater.from(mContext).inflate(
-                    R.layout.send_tab_to_self_feature_unavailable_prompt, null);
-            mToolbarView.setVisibility(View.GONE);
-            // TODO(crbug.com/1298185): This is cumulating both signed-out and single device users.
-            // Those should be recorded separately instead.
-            RecordUserAction.record("SharingHubAndroid.SendTabToSelf.NoTargetDevices");
-            return;
-        }
-
-        // Sharing is available.
         mContentView = (ViewGroup) LayoutInflater.from(mContext).inflate(
                 R.layout.send_tab_to_self_device_picker_list, null);
         ListView listView = mContentView.findViewById(R.id.device_picker_list);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NoTargetDeviceBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NoTargetDeviceBottomSheetContent.java
new file mode 100644
index 0000000..dcc845c
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NoTargetDeviceBottomSheetContent.java
@@ -0,0 +1,90 @@
+// 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.share.send_tab_to_self;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.R;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+
+/** Content shown if the send-tab-to-self feature is ready but there are no target devices. */
+public class NoTargetDeviceBottomSheetContent implements BottomSheetContent {
+    private final View mContentView;
+
+    public NoTargetDeviceBottomSheetContent(Context context) {
+        mContentView = (ViewGroup) LayoutInflater.from(context).inflate(
+                R.layout.send_tab_to_self_feature_unavailable_prompt, null);
+        // TODO(crbug.com/1298185): This is cumulating both signed-out and single device users.
+        // Those should be recorded separately instead.
+        RecordUserAction.record("SharingHubAndroid.SendTabToSelf.NoTargetDevices");
+    }
+
+    @Override
+    public View getContentView() {
+        return mContentView;
+    }
+
+    @Override
+    public View getToolbarView() {
+        return null;
+    }
+
+    @Override
+    public int getVerticalScrollOffset() {
+        return 0;
+    }
+
+    @Override
+    public void destroy() {}
+
+    @Override
+    public int getPriority() {
+        return BottomSheetContent.ContentPriority.HIGH;
+    }
+
+    @Override
+    public boolean swipeToDismissEnabled() {
+        // This ensures that the bottom sheet reappears after the first time. Otherwise, the
+        // second time that a user initiates a share, the bottom sheet does not re-appear.
+        return true;
+    }
+
+    @Override
+    public int getPeekHeight() {
+        // Return DISABLED to ensure that the entire bottom sheet is shown.
+        return BottomSheetContent.HeightMode.DISABLED;
+    }
+
+    @Override
+    public float getFullHeightRatio() {
+        // Return WRAP_CONTENT to have the bottom sheet only open as far as it needs to display the
+        // list of devices and nothing beyond that.
+        return BottomSheetContent.HeightMode.WRAP_CONTENT;
+    }
+
+    @Override
+    public int getSheetContentDescriptionStringId() {
+        return R.string.send_tab_to_self_content_description;
+    }
+
+    @Override
+    public int getSheetHalfHeightAccessibilityStringId() {
+        return R.string.send_tab_to_self_sheet_half_height;
+    }
+
+    @Override
+    public int getSheetFullHeightAccessibilityStringId() {
+        return R.string.send_tab_to_self_sheet_full_height;
+    }
+
+    @Override
+    public int getSheetClosedAccessibilityStringId() {
+        return R.string.send_tab_to_self_sheet_closed;
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
index d1efe6d..d8e4e65 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
@@ -150,8 +150,16 @@
     }
 
     private void showDeviceList() {
-        mController.requestShowContent(
-                new DevicePickerBottomSheetContent(mContext, mUrl, mTitle, mController), true);
+        List<TargetDeviceInfo> targetDevices = SendTabToSelfAndroidBridge.getAllTargetDeviceInfos(
+                Profile.getLastUsedRegularProfile());
+        if (targetDevices.isEmpty()) {
+            mController.requestShowContent(
+                    new NoTargetDeviceBottomSheetContent(mContext), /*animate=*/true);
+        } else {
+            mController.requestShowContent(new DevicePickerBottomSheetContent(mContext, mUrl,
+                                                   mTitle, mController, targetDevices),
+                    /*animate=*/true);
+        }
     }
 
     private boolean shouldOfferSignInPromo() {
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index 8a0a94f..b96c2a6 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -65,6 +65,7 @@
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NoTargetDeviceBottomSheetContent.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
diff --git a/chrome/browser/thumbnail/DEPS b/chrome/browser/thumbnail/DEPS
index 02be6da0..00f797e 100644
--- a/chrome/browser/thumbnail/DEPS
+++ b/chrome/browser/thumbnail/DEPS
@@ -26,6 +26,7 @@
   "+skia",
   "+testing/gmock",
   "+testing/gtest",
+  "+third_party/abseil-cpp/absl",
   "+third_party/android_opengl/etc1",
   "+third_party/skia/include",
   "+ui",
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 9dd1649..a6a926e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1796,6 +1796,7 @@
       "//components/services/app_service/public/mojom",
       "//components/ui_metrics",
       "//components/url_formatter",
+      "//components/url_param_filter/content",
       "//components/user_education/common",
       "//components/user_notes:features",
       "//components/vector_icons",
diff --git a/chrome/browser/ui/app_list/search/games/game_result.cc b/chrome/browser/ui/app_list/search/games/game_result.cc
index f32a138..7ce1e3a 100644
--- a/chrome/browser/ui/app_list/search/games/game_result.cc
+++ b/chrome/browser/ui/app_list/search/games/game_result.cc
@@ -97,9 +97,14 @@
   app_discovery_service->GetIcon(
       game.GetAppId(), dimension_, apps::ResultType::kGameSearchCatalog,
       base::BindOnce(&GameResult::OnIconLoaded, weak_factory_.GetWeakPtr()));
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->AddObserver(this);
 }
 
-GameResult::~GameResult() = default;
+GameResult::~GameResult() {
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->RemoveObserver(this);
+}
 
 void GameResult::Open(int event_flags) {
   // TODO(crbug.com/1305880): Add browser tests for the launch logic.
@@ -124,6 +129,11 @@
                             ui::DispositionFromEventFlags(event_flags));
 }
 
+void GameResult::OnColorModeChanged(bool dark_mode_enabled) {
+  if (uses_generic_icon_)
+    SetGenericIcon();
+}
+
 void GameResult::UpdateText(const apps::Result& game,
                             const std::u16string& query) {
   SetTitle(game.GetAppTitle());
@@ -169,6 +179,7 @@
 }
 
 void GameResult::SetGenericIcon() {
+  uses_generic_icon_ = true;
   const auto color = cros_styles::ResolveColor(
       cros_styles::ColorName::kIconColorPrimary, IsDarkModeEnabled(),
       /*use_debug_colors=*/false);
diff --git a/chrome/browser/ui/app_list/search/games/game_result.h b/chrome/browser/ui/app_list/search/games/game_result.h
index b52d1fb8..744deb3 100644
--- a/chrome/browser/ui/app_list/search/games/game_result.h
+++ b/chrome/browser/ui/app_list/search/games/game_result.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_RESULT_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_RESULT_H_
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/apps/app_discovery_service/app_discovery_util.h"
 #include "chrome/browser/apps/app_discovery_service/result.h"
@@ -25,7 +26,7 @@
 namespace app_list {
 
 // Search result for cloud gaming search.
-class GameResult : public ChromeSearchResult {
+class GameResult : public ChromeSearchResult, public ash::ColorModeObserver {
  public:
   GameResult(Profile* profile,
              AppListControllerDelegate* list_controller,
@@ -42,6 +43,8 @@
   void Open(int event_flags) override;
 
  private:
+  // ash::ColorModeObserver:
+  void OnColorModeChanged(bool dark_mode_enabled) override;
   void UpdateText(const apps::Result& game, const std::u16string& query);
   void OnIconLoaded(const gfx::ImageSkia& image, apps::DiscoveryError error);
   void SetGenericIcon();
@@ -53,6 +56,9 @@
   bool is_icon_masking_allowed_;
   const int dimension_;
 
+  // Whether this game result uses a generic backup icon.
+  bool uses_generic_icon_ = false;
+
   base::WeakPtrFactory<GameResult> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index fd7e280..4620cb1 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -6,6 +6,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
+#include "ash/public/cpp/style/color_provider.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -173,9 +174,15 @@
              ash::features::IsProductivityLauncherEnabled()) {
     InitializeButtonActions({ash::SearchResultActionType::kRemove});
   }
+
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->AddObserver(this);
 }
 
-OmniboxResult::~OmniboxResult() = default;
+OmniboxResult::~OmniboxResult() {
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->RemoveObserver(this);
+}
 
 void OmniboxResult::Open(int event_flags) {
   list_controller_->OpenURL(profile_, match_.destination_url, match_.transition,
@@ -255,6 +262,11 @@
   }
 }
 
+void OmniboxResult::OnColorModeChanged(bool dark_mode_enabled) {
+  if (uses_generic_icon_)
+    SetGenericIcon();
+}
+
 void OmniboxResult::UpdateIcon() {
   if (IsRichEntity()) {
     FetchRichEntityImage(match_.image_url);
@@ -274,6 +286,11 @@
     }
   }
 
+  SetGenericIcon();
+}
+
+void OmniboxResult::SetGenericIcon() {
+  uses_generic_icon_ = true;
   // If this is neither a rich entity nor eligible for a favicon, use either
   // the generic bookmark or another generic icon as appropriate.
   BookmarkModel* bookmark_model =
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index 849db88..1f4df7c 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
@@ -21,7 +22,9 @@
 
 namespace app_list {
 
-class OmniboxResult : public ChromeSearchResult, public BitmapFetcherDelegate {
+class OmniboxResult : public ChromeSearchResult,
+                      public BitmapFetcherDelegate,
+                      public ash::ColorModeObserver {
  public:
   OmniboxResult(Profile* profile,
                 AppListControllerDelegate* list_controller,
@@ -45,7 +48,12 @@
   int dedup_priority() const { return dedup_priority_; }
 
  private:
+  // ash::ColorModeObserver:
+  void OnColorModeChanged(bool dark_mode_enabled) override;
+
   void UpdateIcon();
+  // Creates a generic backup icon: used when rich icons are not available.
+  void SetGenericIcon();
   void UpdateTitleAndDetails();
 
   void Remove();
@@ -76,6 +84,8 @@
   AutocompleteMatch match_;
   const bool is_zero_suggestion_;
   std::unique_ptr<BitmapFetcher> bitmap_fetcher_;
+  // Whether this omnibox result uses a generic backup icon.
+  bool uses_generic_icon_ = false;
 
   base::WeakPtrFactory<OmniboxResult> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/app_list/search/open_tab_result.cc b/chrome/browser/ui/app_list/search/open_tab_result.cc
index 801963d..78411b3 100644
--- a/chrome/browser/ui/app_list/search/open_tab_result.cc
+++ b/chrome/browser/ui/app_list/search/open_tab_result.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/app_list/search/open_tab_result.h"
 
 #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
+#include "ash/public/cpp/style/color_provider.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
@@ -70,9 +71,14 @@
 
   UpdateText();
   UpdateIcon();
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->AddObserver(this);
 }
 
-OpenTabResult::~OpenTabResult() = default;
+OpenTabResult::~OpenTabResult() {
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->RemoveObserver(this);
+}
 
 void OpenTabResult::Open(int event_flags) {
   list_controller_->OpenURL(
@@ -85,6 +91,11 @@
   return drive_id_;
 }
 
+void OpenTabResult::OnColorModeChanged(bool dark_mode_enabled) {
+  if (uses_generic_icon_)
+    SetGenericIcon();
+}
+
 void OpenTabResult::UpdateText() {
   // URL results from the Omnibox have the page title stored in
   // `match.description`.
@@ -118,6 +129,11 @@
 
   // Otherwise, fall back to using a generic icon.
   // TODO(crbug.com/1293702): WIP. Decide on the right generic icon here.
+  SetGenericIcon();
+}
+
+void OpenTabResult::SetGenericIcon() {
+  uses_generic_icon_ = true;
   SetIcon(
       IconInfo(gfx::CreateVectorIcon(omnibox::kSwitchIcon, kSystemIconDimension,
                                      GetGenericIconColor()),
diff --git a/chrome/browser/ui/app_list/search/open_tab_result.h b/chrome/browser/ui/app_list/search/open_tab_result.h
index 87cd13d..a2f51807 100644
--- a/chrome/browser/ui/app_list/search/open_tab_result.h
+++ b/chrome/browser/ui/app_list/search/open_tab_result.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_OPEN_TAB_RESULT_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_OPEN_TAB_RESULT_H_
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chromeos/components/string_matching/tokenized_string.h"
@@ -18,7 +19,7 @@
 namespace app_list {
 
 // Open tab search results. This is produced by the OmniboxProvider.
-class OpenTabResult : public ChromeSearchResult {
+class OpenTabResult : public ChromeSearchResult, public ash::ColorModeObserver {
  public:
   OpenTabResult(Profile* profile,
                 AppListControllerDelegate* list_controller,
@@ -35,8 +36,13 @@
   absl::optional<std::string> DriveId() const override;
 
  private:
+  // ash::ColorModeObserver:
+  void OnColorModeChanged(bool dark_mode_enabled) override;
+
   void UpdateText();
   void UpdateIcon();
+  // Creates a generic backup icon: used when rich icons are not available.
+  void SetGenericIcon();
   void OnFaviconFetched(const gfx::Image& icon);
 
   Profile* profile_;
@@ -44,6 +50,8 @@
   FaviconCache* favicon_cache_;
   AutocompleteMatch match_;
   absl::optional<std::string> drive_id_;
+  // Whether this open tab result uses a generic backup icon.
+  bool uses_generic_icon_ = false;
 
   base::WeakPtrFactory<OpenTabResult> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc
index 53255c1..8908814 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chromeos/login/login_state/login_state.h"
@@ -287,11 +286,7 @@
   if (!is_enabled)
     CloseProjectorApp();
 
-  absl::optional<web_app::AppId> app_id = web_app::GetAppIdForSystemWebApp(
-      profile, ash::SystemWebAppType::PROJECTOR);
-  if (!app_id)
-    return;
-
   auto* web_app_provider = web_app::WebAppProvider::GetForWebApps(profile);
-  web_app_provider->sync_bridge().SetAppIsDisabled(app_id.value(), !is_enabled);
+  web_app_provider->sync_bridge().SetAppIsDisabled(
+      ash::kChromeUITrustedProjectorSwaAppId, !is_enabled);
 }
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.cc b/chrome/browser/ui/ash/test_wallpaper_controller.cc
index 53cd1fb..e0a8f06c 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.cc
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.cc
@@ -14,6 +14,7 @@
 #include "url/gurl.h"
 
 TestWallpaperController::TestWallpaperController() : id_cache_(0) {}
+
 TestWallpaperController::~TestWallpaperController() = default;
 
 void TestWallpaperController::ShowWallpaperImage(const gfx::ImageSkia& image) {
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
index e526308..651738c 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
@@ -34,6 +34,10 @@
 class ValueStore;
 }
 
+namespace {
+class WallpaperControllerClientImplTest;
+}
+
 // Handles chrome-side wallpaper control alongside the ash-side controller.
 class WallpaperControllerClientImpl
     : public ash::WallpaperControllerClient,
@@ -164,6 +168,7 @@
   void RecordWallpaperSourceUMA(const ash::WallpaperType type);
 
  private:
+  friend class WallpaperControllerClientImplTest;
   // Initialize the controller for this client and some wallpaper directories.
   void InitController();
 
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
index b10efdbc..96a6780 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
@@ -27,6 +30,14 @@
         std::move(fake_user_manager));
   }
 
+  void SetUp() override {
+    testing::Test::SetUp();
+    client_.InitForTesting(&controller_);
+  }
+
+  TestWallpaperController* controller() { return &controller_; }
+  WallpaperControllerClientImpl* client() { return &client_; }
+
   WallpaperControllerClientImplTest(const WallpaperControllerClientImplTest&) =
       delete;
   WallpaperControllerClientImplTest& operator=(
@@ -34,30 +45,33 @@
 
   ~WallpaperControllerClientImplTest() override = default;
 
+  void OnGooglePhotosDailyAlbumFetched(
+      const AccountId& account_id,
+      WallpaperControllerClientImpl::FetchGooglePhotosPhotoCallback callback,
+      ash::personalization_app::mojom::FetchGooglePhotosPhotosResponsePtr
+          response) {
+    client_.OnGooglePhotosDailyAlbumFetched(account_id, std::move(callback),
+                                            std::move(response));
+  }
+
  private:
   ScopedTestingLocalState local_state_;
   ash::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_;
+  TestWallpaperController controller_;
+  WallpaperControllerClientImpl client_;
 };
 
 TEST_F(WallpaperControllerClientImplTest, Construction) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   // Singleton was initialized.
-  EXPECT_EQ(&client, WallpaperControllerClientImpl::Get());
+  EXPECT_EQ(client(), WallpaperControllerClientImpl::Get());
 
   // Object was set as client.
-  EXPECT_TRUE(controller.was_client_set());
+  EXPECT_TRUE(controller()->was_client_set());
 }
 
 TEST_F(WallpaperControllerClientImplTest, MigrateCollectionIdFromValueStore) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   value_store::TestingValueStore value_store;
 
   // There is also a resume token and an enabled state, but that's not what is
@@ -66,17 +80,14 @@
   value_store.Set(0, kChromeAppDailyRefreshInfoKey, base::Value(json));
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, &value_store);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id,
+                                                        &value_store);
 
-  EXPECT_EQ("fun_collection", controller.collection_id());
+  EXPECT_EQ("fun_collection", controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest,
        MigrateCollectionIdFromValueStoreNoCollectionId) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   value_store::TestingValueStore value_store;
 
   // There is also a resume token and an enabled state, but that's not what is
@@ -85,32 +96,25 @@
   value_store.Set(0, kChromeAppDailyRefreshInfoKey, base::Value(json));
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, &value_store);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id,
+                                                        &value_store);
 
-  EXPECT_EQ(std::string(), controller.collection_id());
+  EXPECT_EQ(std::string(), controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest,
        MigrateCollectionIdFromValueStoreNoStore) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, nullptr);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id, nullptr);
 
-  EXPECT_EQ(std::string(), controller.collection_id());
+  EXPECT_EQ(std::string(), controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest,
        MigrateCollectionIdFromValueStoreNotOKStatusCode) {
   using StatusCode = value_store::ValueStore::StatusCode;
 
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   value_store::TestingValueStore value_store;
   value_store.set_status_code(StatusCode::OTHER_ERROR);
 
@@ -120,49 +124,84 @@
   value_store.Set(0, kChromeAppDailyRefreshInfoKey, base::Value(json));
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, &value_store);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id,
+                                                        &value_store);
 
-  EXPECT_EQ(std::string(), controller.collection_id());
+  EXPECT_EQ(std::string(), controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest,
        MigrateCollectionIdFromValueStoreNoPref) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   value_store::TestingValueStore value_store;
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, &value_store);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id,
+                                                        &value_store);
 
-  EXPECT_EQ(std::string(), controller.collection_id());
+  EXPECT_EQ(std::string(), controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest,
        MigrateCollectionIdFromValueStoreMalformedPref) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
-
   value_store::TestingValueStore value_store;
   std::string json("{");
   value_store.Set(0, kChromeAppDailyRefreshInfoKey, base::Value(json));
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("fake@test.com", "444444");
-  client.MigrateCollectionIdFromValueStoreForTesting(account_id, &value_store);
+  client()->MigrateCollectionIdFromValueStoreForTesting(account_id,
+                                                        &value_store);
 
-  EXPECT_EQ(std::string(), controller.collection_id());
+  EXPECT_EQ(std::string(), controller()->collection_id());
 }
 
 TEST_F(WallpaperControllerClientImplTest, IsWallpaperSyncEnabledNoProfile) {
-  TestWallpaperController controller;
-  WallpaperControllerClientImpl client;
-  client.InitForTesting(&controller);
   AccountId account_id =
       AccountId::FromUserEmailGaiaId("idontexist@test.com", "444444");
-  EXPECT_FALSE(
-      client.WallpaperControllerClientImpl::IsWallpaperSyncEnabled(account_id));
+  EXPECT_FALSE(client()->WallpaperControllerClientImpl::IsWallpaperSyncEnabled(
+      account_id));
+}
+
+TEST_F(WallpaperControllerClientImplTest, DailyGooglePhotosDoNotRepeat) {
+  using ash::personalization_app::mojom::GooglePhotosPhotoPtr;
+  AccountId account_id =
+      AccountId::FromUserEmailGaiaId("idontexist@test.com", "444444");
+
+  // Size big enough to have not hit the minimum cache size logic, but small
+  // enough to make sure we're very unlikely to pass if the logic is broken,
+  // since there's randomness involved here.
+  const int photos_in_album = 20;
+
+  // Create a fake response with fake photos (the id is the only parameter that
+  // matters here).
+  auto response =
+      ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse::New(
+          std::vector<GooglePhotosPhotoPtr>(), absl::nullopt);
+  for (int i = 0; i < photos_in_album; i++) {
+    response->photos->push_back(
+        ash::personalization_app::mojom::GooglePhotosPhoto::New(
+            "id" + base::NumberToString(i), /*dedup_key=*/absl::nullopt,
+            /*name=*/"", /*date=*/u"",
+            /*url=*/GURL(""), /*location=*/absl::nullopt));
+  }
+
+  std::deque<std::string> last_ten;
+  auto handle_photo = [&last_ten](GooglePhotosPhotoPtr photo, bool success) {
+    ASSERT_TRUE(success);
+
+    auto it = std::find(last_ten.begin(), last_ten.end(), photo->id);
+    EXPECT_TRUE(it == last_ten.end());
+
+    last_ten.push_back(photo->id);
+
+    if (last_ten.size() > 10)
+      last_ten.pop_front();
+  };
+
+  for (int i = 0; i < 20; i++) {
+    OnGooglePhotosDailyAlbumFetched(account_id,
+                                    base::BindLambdaForTesting(handle_photo),
+                                    response->Clone());
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/bookmarks/bookmark_editor.cc b/chrome/browser/ui/bookmarks/bookmark_editor.cc
index 6f0d42e..237b5ea 100644
--- a/chrome/browser/ui/bookmarks/bookmark_editor.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_editor.cc
@@ -6,7 +6,6 @@
 
 #include <stddef.h>
 
-#include "base/logging.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/ui/bookmarks/bookmark_editor.h b/chrome/browser/ui/bookmarks/bookmark_editor.h
index bede2a5..68f22ba 100644
--- a/chrome/browser/ui/bookmarks/bookmark_editor.h
+++ b/chrome/browser/ui/bookmarks/bookmark_editor.h
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -25,6 +26,9 @@
 // bookmark editor dialog.
 class BookmarkEditor {
  public:
+  // A callback which is run when clicking on the Save button in the editor.
+  using OnSaveCallback = base::OnceClosure;
+
   // An enumeration of the possible configurations offered.
   enum Configuration {
     // If Configuration is SHOW_TREE, a tree is shown allowing the user to
@@ -118,7 +122,8 @@
   static void Show(gfx::NativeWindow parent_window,
                    Profile* profile,
                    const EditDetails& details,
-                   Configuration configuration);
+                   Configuration configuration,
+                   OnSaveCallback on_save_callback = base::DoNothing());
 
   // Modifies a bookmark node (assuming that there's no magic that needs to be
   // done regarding moving from one folder to another).  If a new node is
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
index 9371105..eea04b9 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
@@ -7,6 +7,8 @@
 #include <iterator>
 #include <numeric>
 
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/strings/string_number_conversions.h"
@@ -14,6 +16,7 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
+#include "chrome/browser/ui/bookmarks/bookmark_stats.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
@@ -389,7 +392,13 @@
   GetURLsAndFoldersForOpenTabs(browser, &(details.bookmark_data.children));
   DCHECK(!details.bookmark_data.children.empty());
   BookmarkEditor::Show(browser->window()->GetNativeWindow(), profile, details,
-                       BookmarkEditor::SHOW_TREE);
+                       BookmarkEditor::SHOW_TREE,
+                       base::BindOnce(
+                           [](const Profile* profile) {
+                             // We record the profile that invoked this option.
+                             RecordBookmarksAdded(profile);
+                           },
+                           base::Unretained(profile)));
 }
 
 bool HasBookmarkURLs(const std::vector<const BookmarkNode*>& selection) {
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 706684d..b3338d65 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -493,6 +493,7 @@
       command_controller_(new chrome::BrowserCommandController(this)),
       window_has_shown_(false),
       user_title_(params.user_title),
+      picture_in_picture_window_title_(params.picture_in_picture_window_title),
       signin_view_controller_(this),
       breadcrumb_manager_browser_agent_(
           breadcrumbs::IsEnabled()
@@ -702,6 +703,9 @@
     bool include_app_name) const {
   if (!user_title_.empty())
     return base::UTF8ToUTF16(user_title_);
+  if (!picture_in_picture_window_title_.empty()) {
+    return base::UTF8ToUTF16(picture_in_picture_window_title_);
+  }
   return GetWindowTitleFromWebContents(
       include_app_name, tab_strip_model_->GetActiveWebContents());
 }
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 1f02e2e..87859f474 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -305,6 +305,9 @@
     // User-set title of this browser window, if there is one.
     std::string user_title;
 
+    // Title if this is a picture in picture browser window.
+    std::string picture_in_picture_window_title;
+
     // Only applied when not in forced app mode. True if the browser is
     // resizeable.
     bool can_resize = true;
@@ -1268,6 +1271,8 @@
 
   std::string user_title_;
 
+  std::string picture_in_picture_window_title_;
+
   // Controls both signin and sync consent.
   SigninViewController signin_view_controller_;
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index be961d1a..30cb644 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1127,8 +1127,7 @@
   base::RecordAction(UserMetricsAction("BookmarkAllTabs"));
   RecordBookmarkAllTabsWithTabsCount(browser->profile(),
                                      browser->tab_strip_model()->count());
-  // We record the profile that invoked this option.
-  RecordBookmarksAdded(browser->profile());
+
   chrome::ShowBookmarkAllTabsDialog(browser);
 }
 
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 7310322..0f338d0 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -40,12 +40,12 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/common/url_constants.h"
 #include "components/captive_portal/core/buildflags.h"
 #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
 #include "components/prefs/pref_service.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
 #include "content/public/browser/browser_url_handler.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
@@ -273,6 +273,8 @@
                                              profile, params.user_gesture);
         browser_params.trusted_source = params.trusted_source;
         browser_params.initial_bounds = params.window_bounds;
+        browser_params.picture_in_picture_window_title =
+            params.source_contents->GetLastCommittedURL().GetContent();
         return {Browser::Create(browser_params), -1};
       }
 #else   // !IS_CHROMEOS_LACROS
@@ -532,7 +534,10 @@
   }
 #endif
   url_param_filter::CrossOtrObserver::MaybeCreateForWebContents(
-      target_contents.get(), params);
+      target_contents.get(),
+      params.privacy_sensitivity ==
+          NavigateParams::PrivacySensitivity::CROSS_OTR,
+      params.started_from_context_menu, params.transition);
 
   return target_contents;
 }
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index d565ac22..8013de02 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -312,7 +312,7 @@
 };
 
 PopupSeparator::PopupSeparator(AutofillPopupBaseView* popup) : popup_(popup) {
-  SetPreferredHeight(views::MenuConfig::instance().separator_thickness);
+  SetPreferredLength(views::MenuConfig::instance().separator_thickness);
   // Add some spacing between the previous item and the separator.
   // If the feature AutofillVisualImprovementsForSuggestionUi is enabled, also
   // add a padding after the separator.
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
index 53398b22..1968e25 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
@@ -112,7 +112,7 @@
   auto* icon_view_ptr = AddChildView(std::make_unique<IconView>(icon_to_show));
 
   auto separator = std::make_unique<views::Separator>();
-  separator->SetPreferredHeight(kSeparatorHeight);
+  separator->SetPreferredLength(kSeparatorHeight);
   auto* separator_ptr = AddChildView(std::move(separator));
 
   auto title_label = std::make_unique<views::Label>(
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 90d0e06..c381be3 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -431,7 +431,7 @@
 
     SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
         0, kLeadingPadding, 0, kPaddingWidth - kLeadingPadding)));
-    SetPreferredHeight(gfx::kFaviconSize);
+    SetPreferredLength(gfx::kFaviconSize);
   }
   ButtonSeparatorView(const ButtonSeparatorView&) = delete;
   ButtonSeparatorView& operator=(const ButtonSeparatorView&) = delete;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index 765e38f..2d7bb09 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -52,12 +52,14 @@
     Profile* profile,
     const BookmarkNode* parent,
     const EditDetails& details,
-    BookmarkEditor::Configuration configuration)
+    BookmarkEditor::Configuration configuration,
+    BookmarkEditor::OnSaveCallback on_save_callback)
     : profile_(profile),
       parent_(parent),
       details_(details),
       bb_model_(BookmarkModelFactory::GetForBrowserContext(profile)),
-      show_tree_(configuration == SHOW_TREE) {
+      show_tree_(configuration == SHOW_TREE),
+      on_save_callback_(std::move(on_save_callback)) {
   DCHECK(profile);
   DCHECK(bb_model_);
   DCHECK(bb_model_->client()->CanBeEditedByUser(parent));
@@ -466,24 +468,27 @@
   if (!show_tree_) {
     BookmarkEditor::ApplyEditsWithNoFolderChange(
         bb_model_, parent_, details_, new_title, new_url);
-    return;
+  } else {
+    // Create the new folders and update the titles.
+    const BookmarkNode* new_parent = nullptr;
+    ApplyNameChangesAndCreateNewFolders(
+        bb_model_->root_node(), tree_model_->GetRoot(), parent, &new_parent);
+
+    BookmarkEditor::ApplyEditsWithPossibleFolderChange(
+        bb_model_, new_parent, details_, new_title, new_url);
+
+    BookmarkExpandedStateTracker::Nodes expanded_nodes;
+    UpdateExpandedNodes(tree_model_->GetRoot(), &expanded_nodes);
+    bb_model_->expanded_state_tracker()->SetExpandedNodes(expanded_nodes);
+
+    // Remove the folders that were removed. This has to be done after all the
+    // other changes have been committed.
+    bookmarks::DeleteBookmarkFolders(bb_model_, deletes_);
   }
 
-  // Create the new folders and update the titles.
-  const BookmarkNode* new_parent = nullptr;
-  ApplyNameChangesAndCreateNewFolders(
-      bb_model_->root_node(), tree_model_->GetRoot(), parent, &new_parent);
-
-  BookmarkEditor::ApplyEditsWithPossibleFolderChange(
-      bb_model_, new_parent, details_, new_title, new_url);
-
-  BookmarkExpandedStateTracker::Nodes expanded_nodes;
-  UpdateExpandedNodes(tree_model_->GetRoot(), &expanded_nodes);
-  bb_model_->expanded_state_tracker()->SetExpandedNodes(expanded_nodes);
-
-  // Remove the folders that were removed. This has to be done after all the
-  // other changes have been committed.
-  bookmarks::DeleteBookmarkFolders(bb_model_, deletes_);
+  // Once all required bookmarks updates have been called, call the configured
+  // callback.
+  std::move(on_save_callback_).Run();
 }
 
 void BookmarkEditorView::ApplyNameChangesAndCreateNewFolders(
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
index cbc13bf..249acff 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -75,7 +75,8 @@
   BookmarkEditorView(Profile* profile,
                      const bookmarks::BookmarkNode* parent,
                      const EditDetails& details,
-                     BookmarkEditor::Configuration configuration);
+                     BookmarkEditor::Configuration configuration,
+                     BookmarkEditor::OnSaveCallback on_save_callback);
   BookmarkEditorView(const BookmarkEditorView&) = delete;
   BookmarkEditorView& operator=(const BookmarkEditorView&) = delete;
   ~BookmarkEditorView() override;
@@ -264,6 +265,10 @@
 
   // List of deleted bookmark folders.
   std::vector<int64_t> deletes_;
+
+  // Any extra logic that should be run after the save button is clicked,
+  // defined by the caller of BookmarkEditor::Show.
+  BookmarkEditor::OnSaveCallback on_save_callback_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_EDITOR_VIEW_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
index 44ca463..98e9c91 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "base/test/mock_callback.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_profile.h"
@@ -18,7 +19,9 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -64,12 +67,14 @@
     return editor_->tree_model_.get();
   }
 
-  void CreateEditor(Profile* profile,
-                    const BookmarkNode* parent,
-                    const BookmarkEditor::EditDetails& details,
-                    BookmarkEditor::Configuration configuration) {
-    editor_ = std::make_unique<BookmarkEditorView>(profile, parent, details,
-                                                   configuration);
+  void CreateEditor(
+      Profile* profile,
+      const BookmarkNode* parent,
+      const BookmarkEditor::EditDetails& details,
+      BookmarkEditor::Configuration configuration,
+      BookmarkEditor::OnSaveCallback on_save_callback = base::DoNothing()) {
+    editor_ = std::make_unique<BookmarkEditorView>(
+        profile, parent, details, configuration, std::move(on_save_callback));
   }
 
   void SetTitleText(const std::u16string& title) {
@@ -605,3 +610,16 @@
   // Confirm the bottom right of the focus ring is also visible.
   EXPECT_TRUE(scroll_view->GetVisibleRect().Contains(bottom_right));
 }
+
+// Test the on_save_callback is called if defined
+TEST_F(BookmarkEditorViewTest, OnSaveCallbackRunsOnSaveIfDefined) {
+  UNCALLED_MOCK_CALLBACK(BookmarkEditor::OnSaveCallback, on_save_callback);
+
+  CreateEditor(profile_.get(), nullptr,
+               BookmarkEditor::EditDetails::EditNode(GetNode("a")),
+               BookmarkEditorView::SHOW_TREE, on_save_callback.Get());
+
+  EXPECT_CALL_IN_SCOPE(
+      on_save_callback, Run,
+      ApplyEdits(editor_tree_model()->GetRoot()->children()[1].get()));
+}
diff --git a/chrome/browser/ui/views/browser_dialogs_views.cc b/chrome/browser/ui/views/browser_dialogs_views.cc
index 0b92a3a8..e6f5e4e3 100644
--- a/chrome/browser/ui/views/browser_dialogs_views.cc
+++ b/chrome/browser/ui/views/browser_dialogs_views.cc
@@ -36,9 +36,11 @@
 void BookmarkEditor::Show(gfx::NativeWindow parent_window,
                           Profile* profile,
                           const EditDetails& details,
-                          Configuration configuration) {
+                          Configuration configuration,
+                          OnSaveCallback on_save_callback) {
   auto editor = std::make_unique<BookmarkEditorView>(
-      profile, details.parent_node, details, configuration);
+      profile, details.parent_node, details, configuration,
+      std::move(on_save_callback));
   editor->Show(parent_window);
   editor.release();  // BookmarkEditorView is self-deleting
 }
diff --git a/chrome/browser/ui/views/extensions/blocked_action_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/blocked_action_dialog_view_browsertest.cc
index a58cc11c..b75bfedd 100644
--- a/chrome/browser/ui/views/extensions/blocked_action_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/blocked_action_dialog_view_browsertest.cc
@@ -2,40 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
-#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h"
 #include "content/public/test/browser_test.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
 
-class BlockedActionDialogViewBrowserTest : public DialogBrowserTest {
+class BlockedActionDialogViewBrowserTest : public ExtensionsDialogBrowserTest {
  public:
-  BlockedActionDialogViewBrowserTest() = default;
-  BlockedActionDialogViewBrowserTest(
-      const BlockedActionDialogViewBrowserTest&) = delete;
-  const BlockedActionDialogViewBrowserTest& operator=(
-      const BlockedActionDialogViewBrowserTest&) = delete;
-  ~BlockedActionDialogViewBrowserTest() override = default;
-
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    auto extension = InstallExtension();
+    auto extension = InstallExtension("Extension");
     extensions::ShowBlockedActionDialog(browser(), extension->id(),
                                         base::DoNothing());
   }
-
-  scoped_refptr<const extensions::Extension> InstallExtension() {
-    scoped_refptr<const extensions::Extension> extension(
-        extensions::ExtensionBuilder("Extension").Build());
-    extensions::ExtensionSystem::Get(browser()->profile())
-        ->extension_service()
-        ->AddExtension(extension.get());
-    return extension;
-  }
 };
 
 IN_PROC_BROWSER_TEST_F(BlockedActionDialogViewBrowserTest, InvokeUi) {
diff --git a/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.cc
new file mode 100644
index 0000000..a2fdd3b
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.cc
@@ -0,0 +1,30 @@
+// 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/extensions/extensions_dialogs_browsertest.h"
+
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions_toolbar_container.h"
+
+scoped_refptr<const extensions::Extension>
+ExtensionsDialogBrowserTest::InstallExtension(const std::string& name) {
+  scoped_refptr<const extensions::Extension> extension(
+      extensions::ExtensionBuilder(name).Build());
+  extensions::ExtensionSystem::Get(browser()->profile())
+      ->extension_service()
+      ->AddExtension(extension.get());
+  return extension;
+}
+
+ExtensionsToolbarContainer*
+ExtensionsDialogBrowserTest::extensions_container() {
+  return BrowserView::GetBrowserViewForBrowser(browser())
+      ->toolbar()
+      ->extensions_container();
+}
diff --git a/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h b/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h
new file mode 100644
index 0000000..63e11be
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_DIALOGS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_DIALOGS_BROWSERTEST_H_
+
+#include <string>
+#include "base/memory/scoped_refptr.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+
+namespace extensions {
+class Extension;
+}
+
+class ExtensionsToolbarContainer;
+
+class ExtensionsDialogBrowserTest : public DialogBrowserTest {
+ public:
+  ExtensionsDialogBrowserTest() = default;
+  ExtensionsDialogBrowserTest(const ExtensionsDialogBrowserTest&) = delete;
+  const ExtensionsDialogBrowserTest& operator=(
+      const ExtensionsDialogBrowserTest&) = delete;
+  ~ExtensionsDialogBrowserTest() override = default;
+
+  scoped_refptr<const extensions::Extension> InstallExtension(
+      const std::string& name);
+
+  ExtensionsToolbarContainer* extensions_container();
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_DIALOGS_BROWSERTEST_H_
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc
index b07700f1..f9115e3 100644
--- a/chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc
@@ -4,55 +4,27 @@
 
 #include "chrome/browser/ui/views/extensions/extensions_request_access_button_hover_card.h"
 
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/extensions/extensions_dialogs_browsertest.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "content/public/test/browser_test.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
 
 class ExtensionsRequestAccessButtonHoverCardBrowserTest
-    : public DialogBrowserTest {
+    : public ExtensionsDialogBrowserTest {
  public:
-  ExtensionsRequestAccessButtonHoverCardBrowserTest() = default;
-  ExtensionsRequestAccessButtonHoverCardBrowserTest(
-      const ExtensionsRequestAccessButtonHoverCardBrowserTest&) = delete;
-  const ExtensionsRequestAccessButtonHoverCardBrowserTest& operator=(
-      const ExtensionsRequestAccessButtonHoverCardBrowserTest&) = delete;
-  ~ExtensionsRequestAccessButtonHoverCardBrowserTest() override = default;
-
-  ExtensionsToolbarContainer* extensions_container() {
-    return BrowserView::GetBrowserViewForBrowser(browser())
-        ->toolbar()
-        ->extensions_container();
-  }
-
   ExtensionsRequestAccessButton* request_access_button() {
     return extensions_container()
         ->GetExtensionsToolbarControls()
         ->request_access_button_for_testing();
   }
 
-  scoped_refptr<const extensions::Extension> InstallExtension() {
-    scoped_refptr<const extensions::Extension> extension(
-        extensions::ExtensionBuilder("Extension").Build());
-    extensions::ExtensionSystem::Get(browser()->profile())
-        ->extension_service()
-        ->AddExtension(extension.get());
-    return extension;
-  }
-
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
     // Install extension so the extensions toolbar container, which will display
     // the request access button, is visible.
-    InstallExtension();
+    InstallExtension("Extension");
     EXPECT_TRUE(extensions_container()->GetVisible());
 
     // Pretend an extension is requesting access.
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc
index 6e1064d..e54eea4 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -229,7 +229,7 @@
         parent_->AddChildView(std::make_unique<views::Separator>());
     separator->SetFocusBehavior(
         LocationBarBubbleDelegateView::FocusBehavior::NEVER);
-    separator->SetPreferredHeight(views::style::GetLineHeight(
+    separator->SetPreferredLength(views::style::GetLineHeight(
         views::style::CONTEXT_MENU, views::style::STYLE_SECONDARY));
     separator->SetCanProcessEventsWithinSubtree(false);
   }
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.cc
index fe009be..a6ac872 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.cc
@@ -78,6 +78,8 @@
 
 }  // namespace
 
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kPrivacySandboxLearnMoreTextForTesting);
+
 // static
 void ShowPrivacySandboxNoticeBubble(Browser* browser) {
   auto bubble_delegate_unique =
@@ -94,13 +96,16 @@
                        IDR_PRIVACY_SANDBOX_CONFIRMATION_BANNER)),
                    ui::ImageModel::FromImageSkia(*bundle.GetImageSkiaNamed(
                        IDR_PRIVACY_SANDBOX_CONFIRMATION_BANNER_DARK)))
-          .AddBodyText(ui::DialogModelLabel::CreateWithLink(
-              IDS_PRIVACY_SANDBOX_BUBBLE_NOTICE_DESCRIPTION,
-              ui::DialogModelLabel::Link(
-                  IDS_PRIVACY_SANDBOX_BUBBLE_NOTICE_DESCRIPTION_ESTIMATES_INTERESTS_LINK,
-                  base::BindRepeating(&PrivacySandboxNoticeBubbleModelDelegate::
-                                          OnLearnMoreLinkPressed,
-                                      base::Unretained(bubble_delegate)))))
+          .AddBodyText(
+              ui::DialogModelLabel::CreateWithLink(
+                  IDS_PRIVACY_SANDBOX_BUBBLE_NOTICE_DESCRIPTION,
+                  ui::DialogModelLabel::Link(
+                      IDS_PRIVACY_SANDBOX_BUBBLE_NOTICE_DESCRIPTION_ESTIMATES_INTERESTS_LINK,
+                      base::BindRepeating(
+                          &PrivacySandboxNoticeBubbleModelDelegate::
+                              OnLearnMoreLinkPressed,
+                          base::Unretained(bubble_delegate)))),
+              kPrivacySandboxLearnMoreTextForTesting)
           .AddOkButton(
               base::BindRepeating(
                   &PrivacySandboxNoticeBubbleModelDelegate::OnOkButtonPressed,
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.h b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.h
index 58e37bb..1cc8d1e 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.h
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble.h
@@ -5,7 +5,11 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_BUBBLE_H_
 #define CHROME_BROWSER_UI_VIEWS_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_BUBBLE_H_
 
+#include "ui/base/interaction/element_identifier.h"
+
 static constexpr char kPrivacySandboxNoticeBubbleName[] =
     "PrivacySandboxNoticeBubble";
 
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kPrivacySandboxLearnMoreTextForTesting);
+
 #endif  // CHROME_BROWSER_UI_VIEWS_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_BUBBLE_H_
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble_browsertest.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble_browsertest.cc
index 73dc469..2887e67 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble_browsertest.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_notice_bubble_browsertest.cc
@@ -24,6 +24,7 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/styled_label.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
@@ -86,6 +87,11 @@
     testing::Mock::VerifyAndClearExpectations(mock_service());
   }
 
+  ui::TrackedElement* GetElement(ui::ElementIdentifier id) {
+    return ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+        id, browser()->window()->GetElementContext());
+  }
+
  private:
   raw_ptr<MockPrivacySandboxService> mock_service_;
   base::test::ScopedFeatureList feature_list_;
@@ -187,7 +193,7 @@
 // TODO(crbug.com/1321587, crbug.com/1327190): Update how the link is accessed
 // after the API for DialogModel is added.
 IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeBubbleBrowserTest,
-                       DISABLED_OpenLearnMoreNotice) {
+                       OpenLearnMoreNotice) {
   EXPECT_CALL(
       *mock_service(),
       PromptActionOccurred(PrivacySandboxService::PromptAction::kNoticeShown));
@@ -200,7 +206,11 @@
           PrivacySandboxService::PromptAction::kNoticeClosedNoInteraction))
       .Times(0);
 
-  auto* bubble = ShowBubble(browser());
-  // TODO(crbug.com/1321587): Get the link from the bubble and click on it.
-  VerifyBubbleWasClosed(bubble);
+  ShowBubble(browser());
+  auto* view = GetElement(kPrivacySandboxLearnMoreTextForTesting)
+                   ->AsA<views::TrackedElementViews>()
+                   ->view();
+  static_cast<views::StyledLabel*>(view)->ClickLinkForTesting();
+  // TODO(crbug.com/1321587): Bubble should be closed. Figure out why opening
+  // settings page doesn't take over the focus (only in tests).
 }
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
index 5f93e3e0..7629c276 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
@@ -52,6 +52,8 @@
   friend class SidePanelCoordinatorTest;
   FRIEND_TEST_ALL_PREFIXES(UserNoteUICoordinatorTest,
                            ShowEmptyUserNoteSidePanel);
+  FRIEND_TEST_ALL_PREFIXES(UserNoteUICoordinatorTest,
+                           PopulateUserNoteSidePanel);
 
   views::View* GetContentView();
   SidePanelEntry* GetEntryForId(SidePanelEntry::Id entry_id);
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
index f75a0c03..402c77b 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
@@ -19,6 +19,7 @@
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/view_class_properties.h"
 
 namespace {
 // Compares two UserNoteInstances by their rect's origin, which represents their
@@ -35,6 +36,9 @@
 
 }  // namespace
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(UserNoteUICoordinator,
+                                      kScrollViewElementIdForTesting);
+
 UserNoteUICoordinator::UserNoteUICoordinator(Browser* browser)
     : BrowserUserData<UserNoteUICoordinator>(*browser), browser_(browser) {
   browser_->tab_strip_model()->AddObserver(this);
@@ -61,12 +65,21 @@
 }
 
 void UserNoteUICoordinator::OnNoteCreationCancelled(
-    const base::UnguessableToken& id) {
+    const base::UnguessableToken& id,
+    UserNoteView* user_note_view) {
+  scroll_contents_view_->RemoveChildView(user_note_view);
   auto* service =
       user_notes::UserNoteServiceFactory::GetForContext(browser_->profile());
   service->OnNoteCreationCancelled(id);
 }
 
+void UserNoteUICoordinator::OnNoteUpdated(const base::UnguessableToken& id,
+                                          const std::string& note_content) {
+  auto* service =
+      user_notes::UserNoteServiceFactory::GetForContext(browser_->profile());
+  service->OnNoteUpdated(id, note_content);
+}
+
 void UserNoteUICoordinator::FocusNote(const std::string& guid) {
   // TODO(cheickcisse): Implement FocusNote, which will be called by
   // UserNoteService to inform, inform the user note side panel to scroll the
@@ -179,14 +192,14 @@
   // [| ...                     |]
 
   auto root_view = std::make_unique<views::View>();
-  root_view->SetID(kUserNoteUIViewId);
   root_view->SetLayoutManager(std::make_unique<views::FillLayout>());
 
   auto* scroll_view =
       root_view->AddChildView(std::make_unique<views::ScrollView>());
-  scroll_view->SetID(kUserNoteScrollViewId);
   scroll_view->SetHorizontalScrollBarMode(
       views::ScrollView::ScrollBarMode::kDisabled);
+  scroll_view->SetProperty(views::kElementIdentifierKey,
+                           kScrollViewElementIdForTesting);
   // Setting clip height is necessary to make ScrollView take into account its
   // contents' size. Using zeroes doesn't prevent it from scrolling and sizing
   // correctly.
@@ -194,7 +207,6 @@
 
   scroll_contents_view_ =
       scroll_view->SetContents(std::make_unique<views::View>());
-  scroll_contents_view_->SetID(kUserNoteScrollContentsViewId);
 
   constexpr int edge_margin = 16;
   constexpr int vertical_padding = 16;
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.h b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.h
index 2ab1b0ac..cb4c473 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser_user_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/user_notes/interfaces/user_notes_ui.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/views/view.h"
 
 namespace user_notes {
@@ -16,6 +17,7 @@
 }
 
 class SidePanelRegistry;
+class UserNoteView;
 
 class UserNoteUICoordinator : public user_notes::UserNotesUI,
                               public TabStripModelObserver,
@@ -27,14 +29,15 @@
   UserNoteUICoordinator& operator=(const UserNoteUICoordinator&) = delete;
   ~UserNoteUICoordinator() override;
 
-  static constexpr int kUserNoteUIViewId = 172;
-  static constexpr int kUserNoteScrollViewId = 173;
-  static constexpr int kUserNoteScrollContentsViewId = 174;
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kScrollViewElementIdForTesting);
 
   void CreateAndRegisterEntry(SidePanelRegistry* global_registry);
   void OnNoteCreationDone(const base::UnguessableToken& id,
                           const std::string& note_content);
-  void OnNoteCreationCancelled(const base::UnguessableToken& id);
+  void OnNoteCreationCancelled(const base::UnguessableToken& id,
+                               UserNoteView* user_note_view);
+  void OnNoteUpdated(const base::UnguessableToken& id,
+                     const std::string& note_content);
 
   // UserNoteUI overrides
   void FocusNote(const std::string& guid) override;
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
index c48e9a0f..0ef4c718 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
@@ -12,18 +12,20 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_entry.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
 #include "chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.h"
+#include "chrome/browser/ui/views/side_panel/user_note/user_note_view.h"
 #include "chrome/browser/user_notes/user_note_service_factory.h"
 #include "components/user_notes/browser/user_note_manager.h"
 #include "components/user_notes/model/user_note_model_test_utils.h"
 #include "components/user_notes/user_notes_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/interaction/element_tracker_views.h"
+#include "ui/views/view_utils.h"
 
 class UserNoteUICoordinatorTest : public TestWithBrowserView {
  public:
   void SetUp() override {
-    base::test::ScopedFeatureList features;
-    features.InitWithFeatures(
+    scoped_feature_list_.InitWithFeatures(
         {user_notes::kUserNotes, features::kUnifiedSidePanel}, {});
 
     TestWithBrowserView::SetUp();
@@ -62,10 +64,31 @@
         user_notes::GetTestUserNotePageTarget());
   }
 
+  void AddUserNote(user_notes::UserNoteManager* manager, size_t index) {
+    auto note = CreateUserNote(note_ids_[index]);
+    auto safe_ref = note->GetSafeRef();
+    service_->model_map_.emplace(note_ids_[index], std::move(note));
+    manager->AddNoteInstance(std::make_unique<user_notes::UserNoteInstance>(
+        safe_ref, manager, note_rects_[index]));
+  }
+
+  views::ScrollView* GetUserNoteScrollView() {
+    auto* scroll_view = views::AsViewClass<views::ScrollView>(
+        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
+            UserNoteUICoordinator::kScrollViewElementIdForTesting,
+            browser_view()->GetElementContext()));
+    EXPECT_TRUE(scroll_view);
+    EXPECT_TRUE(scroll_view->contents());
+    return scroll_view;
+  }
+
  protected:
   raw_ptr<SidePanelCoordinator> coordinator_;
   raw_ptr<user_notes::UserNoteService> service_;
   raw_ptr<UserNoteUICoordinator> user_note_ui_coordinator_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::vector<base::UnguessableToken> note_ids_;
+  std::vector<gfx::Rect> note_rects_;
 };
 
 TEST_F(UserNoteUICoordinatorTest, ShowEmptyUserNoteSidePanel) {
@@ -78,16 +101,233 @@
   SidePanelEntry::Id entry_id = coordinator_->GetLastActiveEntryId().value();
   EXPECT_EQ(entry_id, SidePanelEntry::Id::kUserNote);
 
-  constexpr int kSidePanelContentWrapperViewId = 43;
-  views::View* user_note_coordinator_view =
-      coordinator_->GetContentView()
-          ->GetViewByID(kSidePanelContentWrapperViewId)
-          ->GetViewByID(UserNoteUICoordinator::kUserNoteUIViewId);
-  EXPECT_TRUE(user_note_coordinator_view);
-  views::ScrollView* user_note_scroll_view =
-      static_cast<views::ScrollView*>(user_note_coordinator_view->GetViewByID(
-          UserNoteUICoordinator::kUserNoteScrollViewId));
-  EXPECT_TRUE(user_note_scroll_view);
-  EXPECT_TRUE(user_note_scroll_view->contents());
-  EXPECT_EQ(user_note_scroll_view->contents()->children().size(), 0u);
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 0u);
+}
+
+TEST_F(UserNoteUICoordinatorTest, PopulateUserNoteSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,10)
+  // note3 = rect(0,0,0,20)
+  for (size_t i = 0; i < 3; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, 10 * i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  size_t index = 0;
+  for (auto* child_view : scroll_view->contents()->children()) {
+    UserNoteView* user_note_view = views::AsViewClass<UserNoteView>(child_view);
+    // Verify that the notes added to the service are displayed in the user note
+    // side panel.
+    EXPECT_EQ(user_note_view->UserNoteId(), note_ids_[index]);
+    index++;
+  }
+}
+
+TEST_F(UserNoteUICoordinatorTest, AddNoteMiddleUserSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,10)
+  for (size_t i = 0; i < 2; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, 10 * i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 2u);
+
+  // Add a note to the service:
+  // note3 = rect(0,0,0,5)
+  note_ids_.emplace_back(base::UnguessableToken::Create());
+  note_rects_.emplace_back(gfx::Rect(0, 0, 0, 5));
+  size_t index = note_ids_.size() - 1;
+  AddUserNote(manager, index);
+
+  user_note_ui_coordinator_->Invalidate();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  UserNoteView* middle_user_note_view = views::AsViewClass<UserNoteView>(
+      scroll_view->contents()->children().at(1));
+  // Verify that note3 is the middle note in the side panel.
+  EXPECT_EQ(middle_user_note_view->UserNoteId(), note_ids_[index]);
+}
+
+TEST_F(UserNoteUICoordinatorTest, AddNoteEndUserSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,1)
+  // note3 = rect(0,0,0,2)
+  for (size_t i = 0; i < 3; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  // Add a note to the service:
+  // note3 = rect(0,0,0,3)
+  note_ids_.emplace_back(base::UnguessableToken::Create());
+  note_rects_.emplace_back(gfx::Rect(0, 0, 0, 3));
+  size_t index = note_ids_.size() - 1;
+  AddUserNote(manager, index);
+
+  user_note_ui_coordinator_->Invalidate();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 4u);
+
+  UserNoteView* last_user_note_view = views::AsViewClass<UserNoteView>(
+      scroll_view->contents()->children().at(index));
+  // Verify that note3 is the last note in the side panel.
+  EXPECT_EQ(last_user_note_view->UserNoteId(), note_ids_[index]);
+}
+
+TEST_F(UserNoteUICoordinatorTest, RemoveMiddleUserSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,1)
+  // note3 = rect(0,0,0,2)
+  for (size_t i = 0; i < 3; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  // Remove note2 from the service.
+  manager->RemoveNote(note_ids_[1]);
+  user_note_ui_coordinator_->Invalidate();
+
+  EXPECT_EQ(scroll_view->contents()->children().size(), 2u);
+
+  for (auto* child_view : scroll_view->contents()->children()) {
+    UserNoteView* user_note_view = views::AsViewClass<UserNoteView>(child_view);
+    // Verify that note2 has been removed from the side panel.
+    EXPECT_NE(user_note_view->UserNoteId(), note_ids_[1]);
+  }
+}
+
+TEST_F(UserNoteUICoordinatorTest, RemoveEndUserSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,1)
+  // note3 = rect(0,0,0,2)
+  for (size_t i = 0; i < 3; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  // Remove note3 from the service.
+  size_t index = note_ids_.size() - 1;
+  manager->RemoveNote(note_ids_[index]);
+  user_note_ui_coordinator_->Invalidate();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 2u);
+
+  for (auto* child_view : scroll_view->contents()->children()) {
+    UserNoteView* user_note_view = views::AsViewClass<UserNoteView>(child_view);
+    // Verify that note3 has been removed from the side panel.
+    EXPECT_NE(user_note_view->UserNoteId(), note_ids_[index]);
+  }
+}
+
+TEST_F(UserNoteUICoordinatorTest, RemoveAllNoteUserSidePanel) {
+  coordinator_->Toggle();
+  EXPECT_TRUE(browser_view()->right_aligned_side_panel()->GetVisible());
+
+  user_notes::UserNoteManager* manager =
+      user_notes::UserNoteManager::GetForPage(
+          browser_view()->GetActiveWebContents()->GetPrimaryPage());
+  EXPECT_TRUE(manager);
+
+  // Add 3 notes with the following rects:
+  // note1 = rect(0,0,0,0)
+  // note2 = rect(0,0,0,1)
+  // note3 = rect(0,0,0,2)
+  for (size_t i = 0; i < 3; ++i) {
+    note_ids_.emplace_back(base::UnguessableToken::Create());
+    note_rects_.emplace_back(gfx::Rect(0, 0, 0, i));
+    AddUserNote(manager, i);
+  }
+
+  coordinator_->Show(SidePanelEntry::Id::kUserNote);
+  user_note_ui_coordinator_->Invalidate();
+
+  auto* scroll_view = GetUserNoteScrollView();
+  EXPECT_EQ(scroll_view->contents()->children().size(), 3u);
+
+  // Remove all notes from the service.
+  for (base::UnguessableToken id : note_ids_) {
+    manager->RemoveNote(id);
+  }
+
+  user_note_ui_coordinator_->Invalidate();
+  // Verify that the user note side panel is empty.
+  EXPECT_EQ(scroll_view->contents()->children().size(), 0u);
 }
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_view.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_view.cc
index aa271fc..da8da4de 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_view.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_view.cc
@@ -33,11 +33,13 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/layout/layout_types.h"
+#include "ui/views/view_utils.h"
 
 namespace {
 
 constexpr int kUserNoteDateViewId = 180;
 constexpr int kUserNoteMenuButtonViewId = 181;
+constexpr int kUserNoteQuoteViewId = 182;
 
 // Layout structure:
 //
@@ -125,6 +127,7 @@
   user_note_quote->SetMultiLine(true);
   user_note_quote->SetMaxLines(user_note_quote_max_lines);
   user_note_quote->SetElideBehavior(gfx::ELIDE_TAIL);
+  user_note_quote->SetID(kUserNoteQuoteViewId);
   // User note quote layout
   const views::FlexSpecification text_flex(
       views::LayoutOrientation::kVertical,
@@ -148,10 +151,11 @@
 
 // Layout structure:
 //
-// [    cancel add]  <--- button container
+// [    cancel (add or save)]  <--- button container
 std::unique_ptr<views::View> CreateButtonsView(
     base::RepeatingClosure cancel_callback,
-    base::RepeatingClosure add_callback) {
+    base::RepeatingClosure action_callback,
+    UserNoteView::State state) {
   auto button_container = std::make_unique<views::View>();
 
   // Cancel button
@@ -162,12 +166,14 @@
   cancel_button->SetEnabledTextColors(SK_ColorBLUE);
   button_container->AddChildView(std::move(cancel_button));
 
-  // Add button
-  auto add_button = std::make_unique<views::MdTextButton>(
-      add_callback, l10n_util::GetStringUTF16(IDS_ADD));
-  add_button->SetCustomPadding(button_padding);
-  add_button->SetProminent(true);
-  button_container->AddChildView(std::move(add_button));
+  // Action button
+  auto action_button = std::make_unique<views::MdTextButton>(
+      action_callback,
+      l10n_util::GetStringUTF16(
+          state == UserNoteView::State::kCreating ? IDS_ADD : IDS_SAVE));
+  action_button->SetCustomPadding(button_padding);
+  action_button->SetProminent(true);
+  button_container->AddChildView(std::move(action_button));
 
   // Button container layout
   ChromeLayoutProvider* const chrome_layout_provider =
@@ -226,7 +232,10 @@
 UserNoteView::UserNoteView(UserNoteUICoordinator* coordinator,
                            user_notes::UserNoteInstance* user_note_instance,
                            UserNoteView::State state)
-    : user_note_instance_(user_note_instance), coordinator_(coordinator) {
+    : user_note_instance_(user_note_instance),
+      coordinator_(coordinator),
+      id_(user_note_instance->model().id()),
+      rect_(user_note_instance->rect()) {
   // Creates an empty view if the user note instance or or user note model is
   // null.
   if (user_note_instance_ == nullptr)
@@ -237,10 +246,8 @@
   const int corner_radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
       views::Emphasis::kHigh);
   const int rounded_border_thickness = 1;
-  const SkColor border_color = UserNoteView::State::kCreating == state ||
-                                       UserNoteView::State::kEditing == state
-                                   ? SK_ColorBLUE
-                                   : SK_ColorLTGRAY;
+  const SkColor border_color =
+      UserNoteView::State::kCreating == state ? SK_ColorBLUE : SK_ColorLTGRAY;
 
   SetLayoutManager(std::make_unique<views::FlexLayout>())
       ->SetOrientation(views::LayoutOrientation::kVertical)
@@ -262,7 +269,7 @@
                                              const std::string quote) {
   if (user_note_header_) {
     user_note_header_->SetVisible(true);
-    views::Label* date_view = static_cast<views::Label*>(
+    views::Label* date_view = views::AsViewClass<views::Label>(
         user_note_header_->GetViewByID(kUserNoteDateViewId));
     date_view->SetText(ConvertDate(date));
   } else {
@@ -287,7 +294,8 @@
   }
 }
 
-void UserNoteView::SetCreatingOrEditState(const std::string content) {
+void UserNoteView::SetCreatingOrEditState(const std::string content,
+                                          UserNoteView::State state) {
   if (text_area_) {
     text_area_->SetVisible(true);
   } else {
@@ -301,10 +309,13 @@
     button_container_->SetVisible(true);
   } else {
     button_container_ = AddChildView(CreateButtonsView(
-        base::BindRepeating(&UserNoteView::OnCancelNewUserNote,
+        base::BindRepeating(&UserNoteView::OnCancelUserNote,
+                            base::Unretained(this), state),
+        base::BindRepeating(state == UserNoteView::State::kCreating
+                                ? &UserNoteView::OnAddUserNote
+                                : &UserNoteView::OnSaveUserNote,
                             base::Unretained(this)),
-        base::BindRepeating(&UserNoteView::OnAddUserNote,
-                            base::Unretained(this))));
+        state));
   }
 }
 
@@ -317,13 +328,13 @@
       SetDefaultOrDetachedState(date, content, /*quote =*/std::string());
       break;
     case UserNoteView::State::kCreating:
-      SetCreatingOrEditState(/*content =*/std::string());
+      SetCreatingOrEditState(/*content =*/std::string(), state);
       break;
-    case UserNoteView::State::kOrphaned:
+    case UserNoteView::State::kDetached:
       SetDefaultOrDetachedState(date, content, quote);
       break;
     case UserNoteView::State::kEditing:
-      SetCreatingOrEditState(content);
+      SetCreatingOrEditState(content, state);
       break;
     default:
       SetBorder(nullptr);
@@ -365,8 +376,22 @@
   dialog_model_ = nullptr;
 }
 
-void UserNoteView::OnCancelNewUserNote() {
-  coordinator_->OnNoteCreationCancelled(UserNoteId());
+void UserNoteView::OnCancelUserNote(UserNoteView::State state) {
+  if (state == UserNoteView::State::kCreating) {
+    coordinator_->OnNoteCreationCancelled(UserNoteId(), this);
+    return;
+  }
+
+  // Hide editing state
+  GetBorder()->set_color(SK_ColorLTGRAY);
+  text_area_->SetVisible(false);
+  button_container_->SetVisible(false);
+
+  // Show default/detached state
+  user_note_body_->SetVisible(false);
+  if (user_note_quote_)
+    user_note_quote_->SetVisible(false);
+  user_note_header_->SetVisible(false);
 }
 
 void UserNoteView::OnAddUserNote() {
@@ -388,7 +413,20 @@
 }
 
 void UserNoteView::OnEditUserNote(int event_flags) {
-  // TODO(cheickcisse): Edit existing note.
+  const std::u16string note_content = user_note_body_->GetText();
+
+  // Hide default/detached state
+  GetBorder()->set_color(SK_ColorBLUE);
+  user_note_body_->SetVisible(false);
+  user_note_header_->SetVisible(false);
+  if (user_note_quote_) {
+    user_note_quote_->SetVisible(false);
+
+    // Show editing state
+    CreateOrUpdateNoteView(UserNoteView::State::kEditing, base::Time(),
+                           base::UTF16ToUTF8(note_content),
+                           /*quote =*/std::string());
+  }
 }
 
 void UserNoteView::OnDeleteUserNote(int event_flags) {
@@ -398,3 +436,28 @@
 void UserNoteView::OnLearnUserNote(int event_flags) {
   // TODO(cheickcisse): Learn more about user note.
 }
+
+void UserNoteView::OnSaveUserNote() {
+  const std::u16string note_content = text_area_->GetText();
+  const std::u16string note_quote =
+      user_note_quote_
+          ? views::AsViewClass<views::Label>(
+                user_note_quote_->GetViewByID(kUserNoteQuoteViewId))
+                ->GetText()
+          : std::u16string();
+  base::Time date = base::Time::Now();
+
+  // Hide editing state
+  GetBorder()->set_color(SK_ColorLTGRAY);
+  text_area_->SetVisible(false);
+  button_container_->SetVisible(false);
+
+  // Show default state
+  CreateOrUpdateNoteView(
+      user_note_quote_ ? UserNoteView::State::kDetached
+                       : UserNoteView::State::kDefault,
+      date, base::UTF16ToUTF8(note_content),
+      !note_quote.empty() ? base::UTF16ToUTF8(note_quote) : std::string());
+
+  coordinator_->OnNoteUpdated(UserNoteId(), base::UTF16ToUTF8(note_content));
+}
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_view.h b/chrome/browser/ui/views/side_panel/user_note/user_note_view.h
index 2faf20b..aa8f2d9b 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_view.h
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_view.h
@@ -42,9 +42,9 @@
     kCreating,
     // State that will display an existing user note to be edited.
     kEditing,
-    // State that will display an orphan user note (note without a highlight in
-    // the page).
-    kOrphaned
+    // State that will display a detached user note (note without a highlight
+    // on the page).
+    kDetached
   };
 
   explicit UserNoteView(
@@ -55,41 +55,40 @@
   UserNoteView& operator=(const UserNoteView&) = delete;
   ~UserNoteView() override;
 
-  const base::UnguessableToken& UserNoteId() {
-    return user_note_instance_ != nullptr ? user_note_instance_->model().id()
-                                          : base::UnguessableToken::Null();
-  }
+  const base::UnguessableToken& UserNoteId() { return id_; }
 
-  const gfx::Rect& user_note_rect() const {
-    return user_note_instance_->rect();
-  }
+  const gfx::Rect& user_note_rect() const { return rect_; }
 
  private:
   void CreateOrUpdateNoteView(UserNoteView::State state,
                               base::Time date,
                               const std::string content,
                               const std::string quote);
-  void OnCancelNewUserNote();
+  void OnCancelUserNote(UserNoteView::State state);
   void OnAddUserNote();
+  void OnSaveUserNote();
+  void OnOpenMenu();
+  void OnMenuClosed();
   void OnEditUserNote(int event_flags);
   void OnDeleteUserNote(int event_flags);
   void OnLearnUserNote(int event_flags);
-  void OnOpenMenu();
-  void OnMenuClosed();
-  void SetCreatingOrEditState(const std::string content);
+  void SetCreatingOrEditState(const std::string content,
+                              UserNoteView::State state);
   void SetDefaultOrDetachedState(base::Time date,
                                  const std::string content,
                                  const std::string quote);
 
   raw_ptr<user_notes::UserNoteInstance> user_note_instance_;
-  raw_ptr<views::Textarea> text_area_;
-  raw_ptr<views::View> button_container_;
-  raw_ptr<views::Label> user_note_body_;
-  raw_ptr<views::View> user_note_header_;
-  raw_ptr<views::View> user_note_quote_;
-  raw_ptr<UserNoteUICoordinator> coordinator_;
+  raw_ptr<views::Textarea> text_area_ = nullptr;
+  raw_ptr<views::View> button_container_ = nullptr;
+  raw_ptr<views::Label> user_note_body_ = nullptr;
+  raw_ptr<views::View> user_note_header_ = nullptr;
+  raw_ptr<views::View> user_note_quote_ = nullptr;
+  raw_ptr<UserNoteUICoordinator> coordinator_ = nullptr;
   std::unique_ptr<views::MenuRunner> menu_runner_;
   std::unique_ptr<ui::MenuModel> dialog_model_;
+  base::UnguessableToken id_;
+  gfx::Rect rect_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_USER_NOTE_USER_NOTE_VIEW_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 962e354..a3c8509 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -243,17 +243,9 @@
   helper_.CheckWindowCreated();
 }
 
-// TODO(https://crbug.com/1311979): Re-enable this test
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24 \
-  DISABLED_WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24
-#else
-#define MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24 \
-  WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24
-#endif
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24) {
+    WebAppIntegration_29SiteA_11SiteA_7SiteA_51SiteA_12SiteA_35SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc
index 3398c117f..2a5818e 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc
@@ -9,6 +9,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/json/json_writer.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/profile_auth_data.h"
@@ -365,6 +366,18 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
+  // Check that notification source is related to this dialog's web contents.
+  // Otherwise we might falsely react to notifications from chrome tabs which
+  // are open in the user's active session. We use NavigationController objects
+  // for comparison because `LoginHandler` uses them as the source of
+  // proxy-related notifications.
+  if (!base::Contains(webui()->GetWebContents()->GetInnerWebContents(), source,
+                      [](content::WebContents* wc) {
+                        return content::Source(&wc->GetController());
+                      })) {
+    return;
+  }
+
   switch (type) {
     case chrome::NOTIFICATION_AUTH_NEEDED: {
       is_proxy_auth_in_progress_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index ea12f8b..561203a 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -171,7 +171,8 @@
 constexpr char kLoginJSPath[] = "login.js";
 constexpr char kOobeJSPath[] = "oobe.js";
 constexpr char kProductLogoPath[] = "product-logo.png";
-// TODO(crbug.com/1261902): Remove.
+// TODO(crbug.com/1261902): Clean-up old implementation once feature is
+// launched.
 constexpr char kRecommendAppOldListViewJSPath[] =
     "recommend_app_old_list_view.js";
 constexpr char kRecommendAppListViewJSPath[] = "recommend_app_list_view.js";
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
index fcf4dfb..fb4021e 100644
--- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
@@ -120,7 +120,7 @@
 
 void RecommendAppsScreenHandler::OnLoadSuccess(base::Value app_list) {
   recommended_app_count_ =
-      static_cast<int>(app_list.GetListDeprecated().size());
+      app_list.is_list() ? static_cast<int>(app_list.GetList().size()) : 0;
   LoadAppListInUI(std::move(app_list));
 }
 
@@ -146,8 +146,8 @@
       features::IsOobeNewRecommendAppsEnabled()
           ? IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_HTML
           : IDR_ARC_SUPPORT_RECOMMEND_APP_OLD_LIST_VIEW_HTML);
-  CallJS("login.RecommendAppsOldScreen.setWebview", app_list_webview);
-  CallJS("login.RecommendAppsOldScreen.loadAppList", std::move(app_list));
+  CallJS("login.RecommendAppsScreen.setWebview", app_list_webview);
+  CallJS("login.RecommendAppsScreen.loadAppList", std::move(app_list));
 }
 
 void RecommendAppsScreenHandler::OnUserSkip() {
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
index 0fdf2e0..c301164 100644
--- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h
@@ -19,7 +19,7 @@
 // WebUI representation.
 class RecommendAppsScreenView {
  public:
-  constexpr static StaticOobeScreenId kScreenId{"recommend-apps-old"};
+  constexpr static StaticOobeScreenId kScreenId{"recommend-apps"};
 
   virtual ~RecommendAppsScreenView() = default;
 
diff --git a/chrome/browser/url_param_filter/DEPS b/chrome/browser/url_param_filter/DEPS
new file mode 100644
index 0000000..e2b754ab
--- /dev/null
+++ b/chrome/browser/url_param_filter/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/url_param_filter/core",
+]
diff --git a/chrome/browser/url_param_filter/url_param_classifications_loader_browsertest.cc b/chrome/browser/url_param_filter/url_param_classifications_loader_browsertest.cc
index 4ca454b..b5fd0ff 100644
--- a/chrome/browser/url_param_filter/url_param_classifications_loader_browsertest.cc
+++ b/chrome/browser/url_param_filter/url_param_classifications_loader_browsertest.cc
@@ -4,12 +4,12 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/component_updater/url_param_classification_component_installer.h"
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/test_launcher_utils.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/url_param_filter/url_param_filter_browsertest.cc b/chrome/browser/url_param_filter/url_param_filter_browsertest.cc
index 734c792..c52328b5 100644
--- a/chrome/browser/url_param_filter/url_param_filter_browsertest.cc
+++ b/chrome/browser/url_param_filter/url_param_filter_browsertest.cc
@@ -10,12 +10,12 @@
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/policy_constants.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -34,6 +34,8 @@
 constexpr static const char kFilteredParamCountMetricName[] =
     "Navigation.UrlParamFilter.FilteredParamCountExperimental";
 
+}  // namespace
+
 class ContextMenuIncognitoFilterDisabledBrowserTest
     : public InProcessBrowserTest {
  public:
@@ -917,6 +919,4 @@
   ASSERT_EQ(expected, tab->GetLastCommittedURL());
 }
 
-}  // namespace
-
 }  // namespace url_param_filter
diff --git a/chrome/browser/web_applications/web_app_data_retriever.cc b/chrome/browser/web_applications/web_app_data_retriever.cc
index 44220d4..8ecbfa41 100644
--- a/chrome/browser/web_applications/web_app_data_retriever.cc
+++ b/chrome/browser/web_applications/web_app_data_retriever.cc
@@ -39,6 +39,7 @@
 void WebAppDataRetriever::GetWebAppInstallInfo(
     content::WebContents* web_contents,
     GetWebAppInstallInfoCallback callback) {
+  DCHECK(!web_contents->IsBeingDestroyed());
   Observe(web_contents);
 
   // Concurrent calls are not allowed.
@@ -87,6 +88,7 @@
     content::WebContents* web_contents,
     bool bypass_service_worker_check,
     CheckInstallabilityCallback callback) {
+  DCHECK(!web_contents->IsBeingDestroyed());
   webapps::InstallableManager* installable_manager =
       webapps::InstallableManager::FromWebContents(web_contents);
   DCHECK(installable_manager);
@@ -115,6 +117,7 @@
                                    std::vector<GURL> icon_urls,
                                    bool skip_page_favicons,
                                    GetIconsCallback callback) {
+  DCHECK(!web_contents->IsBeingDestroyed());
   Observe(web_contents);
 
   // Concurrent calls are not allowed.
@@ -236,7 +239,7 @@
 }
 
 bool WebAppDataRetriever::ShouldStopRetrieval() const {
-  return !web_contents();
+  return !web_contents() || web_contents()->IsBeingDestroyed();
 }
 
 }  // namespace web_app
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3a4ca25..6a088ed0 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1653372000-369d2b350746f8de30ff0da2a9920fb5618ebce4.profdata
+chrome-linux-main-1653393368-182a44bff780721f596be205111fb888cbbc2d54.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 8b91df6..2530305 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1653372000-1fb762fde777b1a811cbad5b1ca2e7a823969ab2.profdata
+chrome-mac-arm-main-1653415098-14ed3c50df80b4d4570bddf7715e2090814bdd9b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 09f91110..11131d6 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1653372000-62b6aaf954233d89b297dbfb37a01c67c0246d1d.profdata
+chrome-mac-main-1653415098-b4a54b46c793c9267ad2d644efdf10a880cc7a74.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index da01270d..b55e71e1 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1653382738-742a60945714f64ef266750d3991ab1380356c5e.profdata
+chrome-win32-main-1653403745-f35eb8169ac9d084cffac5f6d0e91b6d7f94857f.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 31b0144..6f3e5c6 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1653382738-168df4e4d69e2d379efc58f09a14452741d8c013.profdata
+chrome-win64-main-1653403745-9de227c605c94c6a39022a81c782aaca275139c8.profdata
diff --git a/chrome/chrome_cleaner/DEPS b/chrome/chrome_cleaner/DEPS
index f69b764..034833c 100644
--- a/chrome/chrome_cleaner/DEPS
+++ b/chrome/chrome_cleaner/DEPS
@@ -9,6 +9,7 @@
   "+mojo/public",
   "+sandbox/win/src",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/protobuf/src/google/protobuf",
   "+url",
 ]
diff --git a/chrome/chrome_cleaner/README.md b/chrome/chrome_cleaner/README.md
index 6e16a5c..26e1bbc 100644
--- a/chrome/chrome_cleaner/README.md
+++ b/chrome/chrome_cleaner/README.md
@@ -77,8 +77,9 @@
 To check out the internal resources set both of these to True in .gclient:
 
 *  `checkout_src_internal` (standard argument defined in DEPS)
-*  `checkout_chrome_cleaner_internal` (defined in src-internal's DEPS, causes the
-   internal resources to be checked out under `chrome/chrome_cleaner/internal`)
+*  `checkout_chrome_cleaner_internal` (defined in src-internal's DEPS, causes
+   the internal resources to be checked out under
+   `chrome/chrome_cleaner/internal`)
 
 To actually build with the internal resources, also set
 `is_internal_chrome_cleaner_build` to true in args.gn.
@@ -92,6 +93,9 @@
    public resources.
 *  `is_official_chrome_cleaner_build`: If true, various development options
    will be disabled since the build is meant for release to end users.
+*  `enable_software_reporter`: This is true by default for `is_chrome_branded`
+   builds, but can be set to false if a Chrome branded build is needed but
+   Software Reporter execution is not required.
 *  `reporter_branding_path`, `cleaner_branding_path`, `version_path`: Paths to
    resource files that will be used to populate the VERSIONINFO of the
    executables.
@@ -103,4 +107,6 @@
 
 ## Contact
 
-joenotcharles@chromium.org
+Please file bugs / feature requests / inquiries using the
+[Services>SafeBrowsing>ChromeCleanup component](https://bugs.chromium.org/p/chromium/issues/entry?components=Services%3ESafebrowsing%3EChromeCleanup)
+for tracking.
\ No newline at end of file
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 14eddaff..a733353 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -212,6 +212,10 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
+// Enables Privacy Hub for ChromeOS.
+const base::Feature kCrosPrivacyHub{"CrosPrivacyHub",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables parsing and enforcing Data Leak Prevention policy rules that
 // restricts usage of some system features, e.g.clipboard, screenshot, etc.
 // for confidential content.
@@ -430,15 +434,6 @@
 const base::Feature kGeoLanguage{"GeoLanguage",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if BUILDFLAG(IS_ANDROID)
-// Gives Java tasks that are posted with the UiThreadTaskTraits.DEFAULT traits
-// user-blocking priority rather than their default user-visible priority.
-// See crbug.com/1259560.
-const base::Feature kGiveJavaUiThreadDefaultTaskTraitsUserBlockingPriority{
-    "GiveJavaUiThreadDefaultTaskTraitsUserBlockingPriority",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 #if !BUILDFLAG(IS_ANDROID)
 // Enables or disables the Happiness Tracking System demo mode for Desktop
 // Chrome.
@@ -632,9 +627,6 @@
 const base::Feature kUpdateHistoryEntryPointsInIncognito{
     "UpdateHistoryEntryPointsInIncognito", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kIncognitoParamFilterEnabled{
-    "IncognitoParamFilterEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // When enabled, shows a demo of in-product help in a WebUI context.
 const base::Feature kIPHInWebUIDemo{"IPHInWebUIDemo",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index ebfe779..ba96cfba 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -160,6 +160,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kCrosPrivacyHub;
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDataLeakPreventionPolicy;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
@@ -301,12 +303,6 @@
 
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kGeoLanguage;
 
-#if BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature
-    kGiveJavaUiThreadDefaultTaskTraitsUserBlockingPriority;
-#endif
-
 #if !BUILDFLAG(IS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kHappinessTrackingSurveysForDesktopDemo;
@@ -444,9 +440,6 @@
 extern const base::Feature kUpdateHistoryEntryPointsInIncognito;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kIncognitoParamFilterEnabled;
-
-COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kIPHInWebUIDemo;
 
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 810960db..026590f 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -13,8 +13,6 @@
   "+components/content_capture/renderer",
   "+components/content_settings/core/common",
   "+components/content_settings/renderer",
-  "+components/contextual_search/buildflags.h",
-  "+components/contextual_search/content/renderer",
   "+components/continuous_search",
   "+components/crash/core/common/crash_key.h",
   "+components/crx_file",
diff --git a/chrome/renderer/autofill/autofill_renderer_browsertest.cc b/chrome/renderer/autofill/autofill_renderer_browsertest.cc
index 8c05963..9be42b2 100644
--- a/chrome/renderer/autofill/autofill_renderer_browsertest.cc
+++ b/chrome/renderer/autofill/autofill_renderer_browsertest.cc
@@ -98,6 +98,11 @@
                               const FormFieldData& field,
                               const gfx::RectF& bounding_box) override {}
 
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override {}
+
   void AskForValuesToFill(int32_t query_id,
                           const FormData& form,
                           const FormFieldData& field,
diff --git a/chrome/renderer/autofill/form_autocomplete_browsertest.cc b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
index b345ff2..3d74d4268 100644
--- a/chrome/renderer/autofill/form_autocomplete_browsertest.cc
+++ b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
@@ -96,6 +96,11 @@
     select_control_changed_ = std::make_unique<FormFieldData>(field);
   }
 
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override {}
+
   void AskForValuesToFill(int32_t query_id,
                           const FormData& form,
                           const FormFieldData& field,
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index d6c15dc..345c2e3 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -73,8 +73,6 @@
 #include "components/content_capture/common/content_capture_features.h"
 #include "components/content_capture/renderer/content_capture_sender.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/contextual_search/buildflags.h"
-#include "components/contextual_search/content/renderer/overlay_js_render_frame_observer.h"
 #include "components/continuous_search/renderer/search_result_extractor_impl.h"
 #include "components/dom_distiller/content/renderer/distillability_agent.h"
 #include "components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h"
@@ -632,11 +630,6 @@
     new dom_distiller::DistillabilityAgent(render_frame, DCHECK_IS_ON());
   }
 
-#if BUILDFLAG(BUILD_CONTEXTUAL_SEARCH)
-  // Set up a mojo service to test if this page is a contextual search page.
-  new contextual_search::OverlayJsRenderFrameObserver(render_frame, registry);
-#endif
-
   blink::AssociatedInterfaceRegistry* associated_interfaces =
       render_frame_observer->associated_interfaces();
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1f9c6d40..d11a3e6f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -117,8 +117,6 @@
     "../browser/search_engines/template_url_service_test_util.h",
     "../browser/signin/identity_test_environment_profile_adaptor.cc",
     "../browser/signin/identity_test_environment_profile_adaptor.h",
-    "../browser/url_param_filter/url_param_filter_test_helper.cc",
-    "../browser/url_param_filter/url_param_filter_test_helper.h",
     "base/chrome_render_view_host_test_harness.cc",
     "base/chrome_render_view_host_test_harness.h",
     "base/chrome_render_view_test.cc",
@@ -1399,6 +1397,9 @@
       "//components/url_formatter/spoof_checks/top_domains:browsertest_top500_domains",
       "//components/url_formatter/spoof_checks/top_domains:browsertest_top500_domains_header",
       "//components/url_formatter/spoof_checks/top_domains:generate_top_domains_browsertest_trie",
+      "//components/url_param_filter/core",
+      "//components/url_param_filter/core:test_support",
+      "//components/url_param_filter/core:url_param_filter_classification_proto",
       "//components/user_education/common",
       "//components/user_education/test",
       "//components/user_manager",
@@ -3192,6 +3193,8 @@
         "../browser/ui/views/extensions/extension_install_friction_dialog_view_browsertest.cc",
         "../browser/ui/views/extensions/extension_installed_bubble_view_browsertest.cc",
         "../browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc",
+        "../browser/ui/views/extensions/extensions_dialogs_browsertest.cc",
+        "../browser/ui/views/extensions/extensions_dialogs_browsertest.h",
         "../browser/ui/views/extensions/extensions_request_access_button_hover_card_browsertest.cc",
         "../browser/ui/views/extensions/media_galleries_dialog_views_browsertest.cc",
         "../browser/ui/views/extensions/settings_overridden_dialog_view_browsertest.cc",
@@ -5347,10 +5350,6 @@
     "../browser/ui/webui/fileicon_source_unittest.cc",
     "../browser/ui/webui/log_web_ui_url_unittest.cc",
     "../browser/update_client/chrome_update_query_params_delegate_unittest.cc",
-    "../browser/url_param_filter/cross_otr_observer_unittest.cc",
-    "../browser/url_param_filter/url_param_classifications_loader_unittest.cc",
-    "../browser/url_param_filter/url_param_filter_throttle_unittest.cc",
-    "../browser/url_param_filter/url_param_filterer_unittest.cc",
     "../browser/visibility_timer_tab_helper_unittest.cc",
     "../browser/vr/vr_tab_helper_unittest.cc",
     "../browser/webid/federated_identity_active_session_permission_context_unittest.cc",
@@ -5505,7 +5504,6 @@
     "//chrome/browser:buildflags",
     "//chrome/browser:permissions_proto",
     "//chrome/browser:theme_properties",
-    "//chrome/browser:url_param_filter_classification_proto",
     "//chrome/browser/breadcrumbs",
     "//chrome/browser/breadcrumbs:unit_tests",
     "//chrome/browser/browsing_data:constants",
@@ -5741,6 +5739,10 @@
     "//components/unified_consent",
     "//components/url_formatter/spoof_checks/common_words:common",
     "//components/url_formatter/spoof_checks/common_words:common_words_dafsa",
+    "//components/url_param_filter/content",
+    "//components/url_param_filter/core",
+    "//components/url_param_filter/core:test_support",
+    "//components/url_param_filter/core:url_param_filter_classification_proto",
     "//components/user_education/common",
     "//components/user_education/test",
     "//components/user_manager",
@@ -6239,6 +6241,7 @@
       "//components/signin/core/browser",
       "//components/signin/public/android:signin_java_test_support",
       "//components/ukm:test_support",
+      "//components/url_param_filter/content",
       "//components/webapk:proto",
       "//components/webapps/browser",
       "//content/public/android:content_java",
@@ -7147,6 +7150,7 @@
       "//ash/shortcut_viewer/strings:strings_grit",
       "//ash/strings",
       "//ash/webui/camera_app_ui",
+      "//ash/webui/personalization_app/mojom:mojom",
       "//ash/webui/projector_app:test_support",
       "//ash/webui/projector_app/public/cpp",
       "//ash/webui/scanning",
diff --git a/chrome/test/data/extensions/api_test/messaging/connect/test.js b/chrome/test/data/extensions/api_test/messaging/connect/test.js
index d02d427..f4799ef 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect/test.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect/test.js
@@ -21,6 +21,7 @@
     chrome.test.assertFalse(!!actual.tab);
   }
 
+  chrome.test.assertEq('active', actual.documentLifecycle);
   chrome.test.assertEq(expected.frameId, actual.frameId);
   chrome.test.assertEq(expected.url, actual.url);
   chrome.test.assertEq(expected.origin, actual.origin);
diff --git a/chrome/test/data/extensions/api_test/messaging/connect_fenced_frames/test.js b/chrome/test/data/extensions/api_test/messaging/connect_fenced_frames/test.js
index 5baab86c..28acc1a 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect_fenced_frames/test.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect_fenced_frames/test.js
@@ -12,6 +12,7 @@
   // documentId is a unique ID so we can't assume anything about it, just
   // that it is provided.
   chrome.test.assertTrue(actual.documentId != undefined);
+  chrome.test.assertEq('active', actual.documentLifecycle);
   chrome.test.assertEq(expected.frameId, actual.frameId);
   chrome.test.assertEq(expected.url, actual.url);
   chrome.test.assertEq(serverOrigin, actual.origin);
diff --git a/chrome/test/data/notifications/notification_permission_checker.html b/chrome/test/data/notifications/notification_permission_checker.html
index fa4693fb..a50b153 100644
--- a/chrome/test/data/notifications/notification_permission_checker.html
+++ b/chrome/test/data/notifications/notification_permission_checker.html
@@ -3,38 +3,33 @@
 <script>
 
 function getNotificationPermission() {
-  sendResultToTest(Notification.permission);
+  return Notification.permission;
 }
 
 async function getServiceWorkerNotificationPermission() {
-  sendResultToTest(
-      await sendMessage('getServiceWorkerNotificationPermission'));
+  return sendMessage('getServiceWorkerNotificationPermission');
 }
 
 async function queryNotificationPermission() {
   let result = await navigator.permissions.query({name: 'notifications'});
-  sendResultToTest(result.state);
+  return result.state;
 }
 
 async function queryServiceWorkerNotificationPermission() {
-  sendResultToTest(
-      await sendMessage('queryServiceWorkerNotificationPermission'));
+  return sendMessage('queryServiceWorkerNotificationPermission');
 }
 
 async function getPushPermission() {
   let sw = await getServiceWorkerRegistration();
-  let result = await sw.pushManager.permissionState({userVisibleOnly:true});
-  sendResultToTest(result);
+  return sw.pushManager.permissionState({userVisibleOnly:true});
 }
 
 async function getServiceWorkerPushPermission() {
-  sendResultToTest(await sendMessage('getServiceWorkerPushPermission'));
+  return sendMessage('getServiceWorkerPushPermission');
 }
 
-function requestNotificationPermission() {
-  Notification.requestPermission()
-      .then(permission => sendResultToTest(permission))
-      .catch(() => sendResultToTest('error'));
+async function requestNotificationPermission() {
+  return Notification.requestPermission();
 }
 
 async function requestPushPermission() {
@@ -47,9 +42,8 @@
       new TextEncoder().encode(
         String.fromCharCode(...dummy_application_server_key)),
   };
-  sw.pushManager.subscribe(options)
-      .then(sub => sendResultToTest(sub.toJSON()))
-      .catch(e => sendResultToTest(e));
+
+  return sw.pushManager.subscribe(options);
 }
 
 // Sends a message to the service worker and returns a promise that resolves
@@ -72,16 +66,6 @@
       'notification_permission_checker_worker.js',
       location.pathname);
 }
-
-// Sends a result back to the main test logic.
-function sendResultToTest(result) {
-  // Convert the result to a string.
-  var stringResult = "" + result;
-  window.document.title = stringResult;
-  if (typeof stringResult != "string")
-    stringResult = JSON.stringify(result);
-  window.domAutomationController.send(stringResult);
-}
 </script>
 
 <body>
diff --git a/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.js b/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.js
index dea991e7..5206b3f 100644
--- a/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.js
@@ -46,7 +46,7 @@
 
   setup(function() {
     parentalControlsBrowserProxy = new TestParentalControlsBrowserProxy();
-    ParentalControlsBrowserProxyImpl.instance_ = parentalControlsBrowserProxy;
+    ParentalControlsBrowserProxyImpl.setInstance(parentalControlsBrowserProxy);
 
     PolymerTest.clearBody();
     parentalControlsPage =
@@ -62,8 +62,8 @@
 
   test('parental controls page enabled when online', () => {
     // Setup button is shown and enabled.
-    const setupButton =
-        assert(parentalControlsPage.$$('#parental-controls-item cr-button'));
+    const setupButton = assert(parentalControlsPage.shadowRoot.querySelector(
+        '#parental-controls-item cr-button'));
 
     setupButton.click();
 
@@ -78,8 +78,8 @@
     // Simulate going offline
     window.dispatchEvent(new CustomEvent('offline'));
     // Setup button is shown but disabled.
-    const setupButton =
-        assert(parentalControlsPage.$$('#parental-controls-item cr-button'));
+    const setupButton = assert(parentalControlsPage.shadowRoot.querySelector(
+        '#parental-controls-item cr-button'));
     assertTrue(setupButton.disabled);
 
     setupButton.click();
@@ -95,8 +95,8 @@
     // Simulate going offline
     window.dispatchEvent(new CustomEvent('offline'));
     // Setup button is shown but disabled.
-    const setupButton =
-        assert(parentalControlsPage.$$('#parental-controls-item cr-button'));
+    const setupButton = assert(parentalControlsPage.shadowRoot.querySelector(
+        '#parental-controls-item cr-button'));
     assertTrue(setupButton.disabled);
 
     // Come back online.
@@ -124,7 +124,7 @@
 
   setup(async function() {
     parentalControlsBrowserProxy = new TestParentalControlsBrowserProxy();
-    ParentalControlsBrowserProxyImpl.instance_ = parentalControlsBrowserProxy;
+    ParentalControlsBrowserProxyImpl.setInstance(parentalControlsBrowserProxy);
 
     PolymerTest.clearBody();
     parentalControlsPage =
@@ -140,8 +140,8 @@
 
   test('parental controls page child view shown to child account', () => {
     // Get the link row.
-    const linkRow =
-        assert(parentalControlsPage.$$('#parental-controls-item cr-link-row'));
+    const linkRow = assert(parentalControlsPage.shadowRoot.querySelector(
+        '#parental-controls-item cr-link-row'));
 
     linkRow.click();
     // Ensure that the request to launch FLH went through.
diff --git a/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js b/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
index 5554351..fe5f8fb3 100644
--- a/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
+++ b/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
@@ -456,7 +456,7 @@
 
   setup(function() {
     parentalControlsBrowserProxy = new TestParentalControlsBrowserProxy();
-    ParentalControlsBrowserProxyImpl.instance_ = parentalControlsBrowserProxy;
+    ParentalControlsBrowserProxyImpl.setInstance(parentalControlsBrowserProxy);
     PolymerTest.clearBody();
 
     accountManager = document.createElement('settings-account-manager');
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index eff3830a..39a797bb 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -94,6 +94,8 @@
   virtual void ExpectLastStarted() const = 0;
   virtual void UninstallApp(const std::string& app_id) const = 0;
 
+  virtual void RunOfflineInstall() = 0;
+
  protected:
   friend class base::RefCountedThreadSafe<IntegrationTestCommands>;
 
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index e9baada..865f76a 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -270,6 +270,8 @@
     RunCommand("uninstall_app", {Param("app_id", app_id)});
   }
 
+  void RunOfflineInstall() override { RunCommand("run_offline_install"); }
+
  private:
   ~IntegrationTestCommandsSystem() override = default;
 
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index be1c375..421e5f1 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -234,6 +234,10 @@
     updater::test::UninstallApp(updater_scope_, app_id);
   }
 
+  void RunOfflineInstall() override {
+    updater::test::RunOfflineInstall(updater_scope_);
+  }
+
  private:
   ~IntegrationTestCommandsUser() override = default;
 
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 3f8db104..7cca93b 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -301,6 +301,8 @@
 
   void ExpectLastStarted() { test_commands_->ExpectLastStarted(); }
 
+  void RunOfflineInstall() { test_commands_->RunOfflineInstall(); }
+
   scoped_refptr<IntegrationTestCommands> test_commands_;
 
  private:
@@ -751,6 +753,13 @@
   Uninstall();
 }
 
+TEST_F(IntegrationTest, OfflineInstall) {
+  Install();
+  ExpectInstalled();
+  RunOfflineInstall();
+  Uninstall();
+}
+
 #endif  // BUILDFLAG(IS_WIN) || !defined(COMPONENT_BUILD)
 
 }  // namespace test
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 7b318c3..956c24b 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -290,6 +290,7 @@
                                                     &RunRecoveryComponent))))},
     {"expect_last_checked", WithSystemScope(Wrap(&ExpectLastChecked))},
     {"expect_last_started", WithSystemScope(Wrap(&ExpectLastStarted))},
+    {"run_offline_install", WithSystemScope(Wrap(&RunOfflineInstall))},
   };
 
   const base::CommandLine* command_line =
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index 1f332f6..29d6a7f 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -216,6 +216,8 @@
 
 void UninstallApp(UpdaterScope scope, const std::string& app_id);
 
+void RunOfflineInstall(UpdaterScope scope);
+
 }  // namespace test
 }  // namespace updater
 
diff --git a/chrome/updater/test/integration_tests_linux.cc b/chrome/updater/test/integration_tests_linux.cc
index c1add23..8f23697 100644
--- a/chrome/updater/test/integration_tests_linux.cc
+++ b/chrome/updater/test/integration_tests_linux.cc
@@ -102,5 +102,9 @@
   NOTREACHED();
 }
 
+void RunOfflineInstall(UpdaterScope scope) {
+  NOTREACHED();
+}
+
 }  // namespace test
 }  // namespace updater
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index 5c583ffe..f8e5212 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -418,5 +418,9 @@
                           base::FilePath(FILE_PATH_LITERAL("NONE")));
 }
 
+void RunOfflineInstall(UpdaterScope scope) {
+  // TODO(crbug.com/1286574).
+}
+
 }  // namespace test
 }  // namespace updater
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index c42a017..9c33df17e 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -13,9 +13,11 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/path_service.h"
@@ -29,6 +31,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
+#include "base/test/test_timeouts.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/values.h"
@@ -51,6 +54,10 @@
 #include "chrome/updater/util.h"
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/task_scheduler.h"
+#include "chrome/updater/win/test/test_executables.h"
+#include "chrome/updater/win/test/test_strings.h"
+#include "chrome/updater/win/ui/l10n_util.h"
+#include "chrome/updater/win/ui/resources/updater_installer_strings.h"
 #include "chrome/updater/win/win_constants.h"
 #include "chrome/updater/win/win_util.h"
 #include "components/crx_file/crx_verifier.h"
@@ -333,6 +340,63 @@
   VLOG(2) << "Sleep complete.";
 }
 
+class WindowEnumerator {
+ public:
+  WindowEnumerator(HWND parent,
+                   base::RepeatingCallback<bool(HWND hwnd)> filter,
+                   base::RepeatingCallback<void(HWND hwnd)> action)
+      : parent_(parent), filter_(filter), action_(action) {}
+
+  WindowEnumerator(const WindowEnumerator&) = delete;
+  WindowEnumerator& operator=(const WindowEnumerator&) = delete;
+
+  void Run() const {
+    ::EnumChildWindows(parent_, &OnWindowProc, reinterpret_cast<LPARAM>(this));
+  }
+
+  static std::wstring GetWindowClass(HWND hwnd) {
+    constexpr int kMaxWindowClassNameLength = 256;
+    wchar_t buffer[kMaxWindowClassNameLength + 1] = {0};
+    int name_len = ::GetClassName(hwnd, buffer, std::size(buffer));
+    if (name_len <= 0 || name_len > kMaxWindowClassNameLength)
+      return std::wstring();
+
+    return std::wstring(&buffer[0], name_len);
+  }
+
+  static bool IsSystemDialog(HWND hwnd) {
+    constexpr wchar_t kSystemDialogClass[] = L"#32770";
+    return GetWindowClass(hwnd) == kSystemDialogClass;
+  }
+
+  static std::wstring GetWindowText(HWND hwnd) {
+    const int num_chars = ::GetWindowTextLength(hwnd);
+    if (!num_chars)
+      return std::wstring();
+    std::vector<wchar_t> text(num_chars + 1);
+    if (!::GetWindowText(hwnd, &text.front(), text.size()))
+      return std::wstring();
+    return std::wstring(text.begin(), text.end());
+  }
+
+ private:
+  bool OnWindow(HWND hwnd) const {
+    if (filter_.Run(hwnd))
+      action_.Run(hwnd);
+
+    // Returns true to keep enumerating.
+    return true;
+  }
+
+  static BOOL CALLBACK OnWindowProc(HWND hwnd, LPARAM lparam) {
+    return reinterpret_cast<WindowEnumerator*>(lparam)->OnWindow(hwnd);
+  }
+
+  const HWND parent_;
+  base::RepeatingCallback<bool(HWND hwnd)> filter_;
+  base::RepeatingCallback<void(HWND hwnd)> action_;
+};
+
 }  // namespace
 
 base::FilePath GetSetupExecutablePath() {
@@ -977,5 +1041,126 @@
   ASSERT_EQ(key.DeleteKey(base::SysUTF8ToWide(app_id).c_str()), ERROR_SUCCESS);
 }
 
+void RunOfflineInstall(UpdaterScope scope) {
+  constexpr wchar_t kTestRegKey[] = L"software\\updater\\test";
+  constexpr wchar_t kTestRegValue[] = L"install_result";
+  constexpr char kManifestFormat[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<response protocol=\"3.0\">"
+      "  <app appid=\"{CDABE316-39CD-43BA-8440-6D1E0547AEE6}\" status=\"ok\">"
+      "    <updatecheck status=\"ok\">"
+      "      <manifest version=\"1.2.3.4\">"
+      "        <packages>"
+      "          <package hash_sha256=\"sha256hash_foobar\""
+      "            name=\"reg.exe\" required=\"true\" size=\"%lld\"/>"
+      "        </packages>"
+      "        <actions>"
+      "          <action event=\"install\" needsadmin=\"false\""
+      "            run=\"reg.exe\""
+      "            arguments=\"ADD HKCU\\%ls /t REG_DWORD /v %ls /d 123 /f\"/>"
+      "        </actions>"
+      "      </manifest>"
+      "    </updatecheck>"
+      "    <data index=\"verboselogging\" name=\"install\" status=\"ok\">"
+      "      {\"distribution\": { \"verbose_logging\": true}}"
+      "    </data>"
+      "  </app>"
+      "</response>";
+
+  DeleteRegKey(HKEY_CURRENT_USER, kTestRegKey);
+
+  wchar_t reg_exe_path[MAX_PATH] = {0};
+  DWORD size = ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\reg.exe",
+                                        reg_exe_path, std::size(reg_exe_path));
+  ASSERT_TRUE(size > 0 && size < MAX_PATH);
+  const base::FilePath exe_path(reg_exe_path);
+  ASSERT_TRUE(base::PathExists(exe_path));
+
+  base::ScopedTempDir temp_dir;
+  EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
+  const base::FilePath& offline_dir = temp_dir.GetPath();
+
+  // Create manifest file.
+  base::FilePath manifest_path =
+      offline_dir.Append(FILE_PATH_LITERAL("OfflineManifest.gup"));
+  int64_t exe_size = 0;
+  EXPECT_TRUE(base::GetFileSize(exe_path, &exe_size));
+  const std::string manifest =
+      base::StringPrintf(kManifestFormat, exe_size, kTestRegKey, kTestRegValue);
+  EXPECT_TRUE(base::WriteFile(manifest_path, manifest));
+
+  // Copy app installer.
+  ASSERT_TRUE(base::CopyFile(exe_path,
+                             offline_dir.Append(FILE_PATH_LITERAL("reg.exe"))));
+
+  // Trigger offline install.
+  const absl::optional<base::FilePath> updater_exe =
+      GetInstalledExecutablePath(scope);
+  ASSERT_TRUE(updater_exe.has_value());
+  base::CommandLine offline_install_cmd(updater_exe.value());
+
+  offline_install_cmd.AppendSwitch(kEnableLoggingSwitch);
+  offline_install_cmd.AppendSwitchASCII(kLoggingModuleSwitch,
+                                        kLoggingModuleSwitchValue);
+  if (scope == UpdaterScope::kSystem)
+    offline_install_cmd.AppendSwitch(kSystemSwitch);
+
+  offline_install_cmd.AppendSwitchASCII(
+      updater::kHandoffSwitch,
+      "appguid={CDABE316-39CD-43BA-8440-6D1E0547AEE6}&lang=en");
+  offline_install_cmd.AppendSwitchASCII(
+      updater::kSessionIdSwitch, "{E85204C6-6F2F-40BF-9E6C-4952208BB977}");
+  offline_install_cmd.AppendSwitchNative(updater::kOfflineDirSwitch,
+                                         offline_dir.value());
+
+  base::Process process = base::LaunchProcess(offline_install_cmd, {});
+  EXPECT_TRUE(process.IsValid());
+
+  // Dismiss the installation completion dialog, then wait for the process exit.
+  WaitFor(base::BindRepeating(
+      [](const wchar_t* test_key_name, const wchar_t* test_value_name) {
+        // Enumerate the top-level dialogs to find the setup dialog.
+        WindowEnumerator(
+            ::GetDesktopWindow(), base::BindRepeating([](HWND hwnd) {
+              return WindowEnumerator::IsSystemDialog(hwnd) &&
+                     base::Contains(WindowEnumerator::GetWindowText(hwnd),
+                                    GetLocalizedStringF(
+                                        IDS_INSTALLER_DISPLAY_NAME_BASE,
+                                        GetLocalizedString(
+                                            IDS_FRIENDLY_COMPANY_NAME_BASE)));
+            }),
+            base::BindRepeating([](HWND hwnd) {
+              // Enumerates the dialog items to search for installation complete
+              // message. Once found, close the dialog.
+              WindowEnumerator(
+                  hwnd, base::BindRepeating([](HWND hwnd) {
+                    return base::Contains(
+                        WindowEnumerator::GetWindowText(hwnd),
+                        GetLocalizedString(
+                            IDS_BUNDLE_INSTALLED_SUCCESSFULLY_BASE));
+                  }),
+                  base::BindRepeating([](HWND hwnd) {
+                    ::PostMessage(::GetParent(hwnd), WM_CLOSE, 0, 0);
+                  }))
+                  .Run();
+            }))
+            .Run();
+
+        if (IsUpdaterRunning())
+          return false;
+
+        // Wait for the app installer writes the expected reg value.
+        base::win::RegKey key;
+        DWORD value = 0;
+        return (ERROR_SUCCESS == key.Open(HKEY_CURRENT_USER, test_key_name,
+                                          Wow6432(KEY_QUERY_VALUE)) &&
+                ERROR_SUCCESS == key.ReadValueDW(test_value_name, &value) &&
+                value == 123);
+      },
+      kTestRegKey, kTestRegValue));
+
+  DeleteRegKey(HKEY_CURRENT_USER, kTestRegKey);
+}
+
 }  // namespace test
 }  // namespace updater
diff --git a/chromeos/ash/components/dbus/DEPS b/chromeos/ash/components/dbus/DEPS
index 6e1f8ee2..ca7d39f 100644
--- a/chromeos/ash/components/dbus/DEPS
+++ b/chromeos/ash/components/dbus/DEPS
@@ -7,6 +7,7 @@
   "+components/policy/proto",
   "+dbus",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
 
   # TODO(crbug.com/1164001): Remove after chromeos/dbus migration is done.
diff --git a/chromeos/ash/components/network/DEPS b/chromeos/ash/components/network/DEPS
index 478fcd0..f417a6b 100644
--- a/chromeos/ash/components/network/DEPS
+++ b/chromeos/ash/components/network/DEPS
@@ -30,6 +30,7 @@
   "+net",
   "+services/preferences/public/cpp",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
   "+url",
 ]
diff --git a/chromeos/components/onc/DEPS b/chromeos/components/onc/DEPS
index 8567161..d858bd02 100644
--- a/chromeos/components/onc/DEPS
+++ b/chromeos/components/onc/DEPS
@@ -8,4 +8,5 @@
   "+crypto",
   "+net",
   "+testing",
+  "+third_party/abseil-cpp/absl",
 ]
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 68cc94c8..1752df2 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -1069,7 +1069,7 @@
 // has accidentally become a kitchen sink for different features. This was not
 // intentional.
 //
-// Next MinVersion: 68.
+// Next MinVersion: 69.
 // Next ID: 19
 //
 [Stable, Uuid="4e04dc16-b34c-40fd-9e3f-3c55c2c6cf91",
@@ -1114,14 +1114,17 @@
   NewGuestWindow@14() => ();
 
   // Opens a new tab in the browser with, currently, the last used profile.
-  // This may open a new window, if there's no window. Please see also
-  // the Chrome's NewTab command for details.
+  // This may open a new window, if there's no window. If a new browser is
+  // opened and `should_trigger_session_restore` is set, the new browser will
+  // open with the profile's session startup pref. If this is not set the
+  // browser will open without considering the user's session startup pref.
+  // Please see also the Chrome's NewTab command for details.
   // The callback is called on the command execution.
   // This is designed to be equivalent of CTRL+T behavior. By default, this
   // opens a new-tab-page, but extensions may override the behavior.
   // Added in M91.
   [MinVersion=10]
-  NewTab@7() => ();
+  NewTab@7([MinVersion=68] bool should_trigger_session_restore) => ();
 
   // Opens the specified URL in the browser. This opens a new tab and loads
   // the page at specified URL.
diff --git a/chromeos/crosapi/mojom/file_system_provider.mojom b/chromeos/crosapi/mojom/file_system_provider.mojom
index 080bf44..221cefa 100644
--- a/chromeos/crosapi/mojom/file_system_provider.mojom
+++ b/chromeos/crosapi/mojom/file_system_provider.mojom
@@ -4,7 +4,9 @@
 
 module crosapi.mojom;
 
+import "mojo/public/mojom/base/big_buffer.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
+import "mojo/public/mojom/base/values.mojom";
 
 // A file system is uniquely identified by an opaque string (consisting of at
 // least the extension id + profile unique identifier) and a file_system_id.
@@ -98,6 +100,18 @@
   mojo_base.mojom.FilePath path@1;
 };
 
+[Stable, Extensible]
+enum FSPOperationResponse {
+  [Default] kUnknown,
+  kUnmountSuccess,
+  kGetEntryMetadataSuccess,
+  kGetActionsSuccess,
+  kReadDirectorySuccess,
+  kReadFileSuccess,
+  kGenericSuccess,
+  kGenericFailure,
+};
+
 // Implemented by Lacros.
 [Stable, Uuid="8cbcf151-c189-4fe9-90ff-892c7f5637a9"]
 interface FileSystemProvider {
@@ -144,4 +158,18 @@
   // exclusive.
   Notify@5(FileSystemId file_system_id, FSPWatcher watcher,
       FSPChangeType type, array<FSPChange> changes) => (string error);
+
+  // The following function represents responses from the Extension after
+  // handling an operation. We use mojo_base.mojom.Value as the type because:
+  //   * The extension API is stable.
+  //   * Both the supplier and consumer of this information uses a type
+  //   interchangeable with mojo_base.mojom.Value. While we could add a
+  //   translation layer to a strongly typed mojom struct, this adds a
+  //   overhead and the potential for errors with no benefit.
+  // |request_id| corresponds to ids passed in via methods on interface
+  // FileSystemProvider.
+  [MinVersion=1]
+  OperationFinished@6(FSPOperationResponse response,
+      FileSystemId file_system_id, int64 request_id,
+      array<mojo_base.mojom.Value> args) => (string error);
 };
diff --git a/chromeos/dbus/DEPS b/chromeos/dbus/DEPS
index 5a5179f..1712b665 100644
--- a/chromeos/dbus/DEPS
+++ b/chromeos/dbus/DEPS
@@ -9,6 +9,7 @@
   "+dbus",
   "+net",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
   "+url",
 ]
diff --git a/chromeos/network/DEPS b/chromeos/network/DEPS
index ed3628d..36339956 100644
--- a/chromeos/network/DEPS
+++ b/chromeos/network/DEPS
@@ -30,6 +30,7 @@
   "+net",
   "+services/preferences/public/cpp",
   "+testing",
+  "+third_party/abseil-cpp/absl",
   "+third_party/cros_system_api",
   "+url",
 ]
diff --git a/chromeos/ui/base/OWNERS b/chromeos/ui/base/OWNERS
index 42ead959..eadc496 100644
--- a/chromeos/ui/base/OWNERS
+++ b/chromeos/ui/base/OWNERS
@@ -1 +1 @@
-per-file file_icon_util.* = file://ui/file_manager/OWNERS
+per-file file_icon_util* = file://ui/file_manager/OWNERS
diff --git a/components/BUILD.gn b/components/BUILD.gn
index ad7c2897..771453f 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -233,6 +233,7 @@
     "//components/url_formatter/spoof_checks/common_words:unit_tests",
     "//components/url_formatter/spoof_checks/top_domains:unit_tests",
     "//components/url_matcher:unit_tests",
+    "//components/url_param_filter/core:unit_tests",
     "//components/url_pattern_index:unit_tests",
     "//components/variations:unit_tests",
     "//components/variations/field_trial_config:unit_tests",
@@ -397,6 +398,7 @@
       "//components/translate/content/browser:unit_tests",
       "//components/translate/content/renderer:unit_tests",
       "//components/ukm/content:unit_tests",
+      "//components/url_param_filter/content:unit_tests",
       "//components/url_rewrite:unit_tests",
       "//components/user_education/common:unit_tests",
       "//components/value_store:unit_tests",
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc
index 945ddfc..4368386 100644
--- a/components/android_autofill/browser/android_autofill_manager.cc
+++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -148,6 +148,11 @@
 void AndroidAutofillManager::SelectFieldOptionsDidChange(const FormData& form) {
 }
 
+void AndroidAutofillManager::JavaScriptChangedAutofilledValue(
+    const FormData& form,
+    const FormFieldData& field,
+    const std::u16string& old_value) {}
+
 void AndroidAutofillManager::PropagateAutofillPredictions(
     const std::vector<FormStructure*>& forms) {
   has_server_prediction_ = true;
diff --git a/components/android_autofill/browser/android_autofill_manager.h b/components/android_autofill/browser/android_autofill_manager.h
index a19fa3f..fd6358b 100644
--- a/components/android_autofill/browser/android_autofill_manager.h
+++ b/components/android_autofill/browser/android_autofill_manager.h
@@ -115,6 +115,11 @@
                                     const FormFieldData& field,
                                     const gfx::RectF& bounding_box) override;
 
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override;
+
   bool ShouldParseForms(const std::vector<FormData>& forms) override;
 
   void OnBeforeProcessParsedForms() override {}
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 2d864b4..3e47876 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -377,6 +377,13 @@
   autofill_manager_->SelectFieldOptionsDidChange(form);
 }
 
+void ContentAutofillDriver::JavaScriptChangedAutofilledValueImpl(
+    const FormData& form,
+    const FormFieldData& field,
+    const std::u16string& old_value) {
+  autofill_manager_->JavaScriptChangedAutofilledValue(form, field, old_value);
+}
+
 void ContentAutofillDriver::FillFormForAssistantImpl(
     const AutofillableData& fill_data,
     const FormData& form,
@@ -550,6 +557,19 @@
       this, GetFormWithFrameAndFormMetaData(raw_form));
 }
 
+void ContentAutofillDriver::JavaScriptChangedAutofilledValue(
+    const FormData& raw_form,
+    const FormFieldData& raw_field,
+    const std::u16string& old_value) {
+  if (!bad_message::CheckFrameNotPrerendering(render_frame_host_))
+    return;
+  FormData form = raw_form;
+  FormFieldData field = raw_field;
+  SetFrameAndFormMetaData(form, &field);
+  GetAutofillRouter().JavaScriptChangedAutofilledValue(this, form, field,
+                                                       old_value);
+}
+
 void ContentAutofillDriver::FillFormForAssistant(
     const AutofillableData& fill_data,
     const FormData& raw_form,
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 28c1099..ab8995b 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -237,6 +237,10 @@
   void DidPreviewAutofillFormData() override;
   void DidEndTextFieldEditing() override;
   void SelectFieldOptionsDidChange(const FormData& form) override;
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override;
 
   // Implementations of the mojom::AutofillDriver functions called by the
   // renderer. These functions are called by ContentAutofillRouter.
@@ -272,6 +276,9 @@
   void DidPreviewAutofillFormDataImpl();
   void DidEndTextFieldEditingImpl();
   void SelectFieldOptionsDidChangeImpl(const FormData& form);
+  void JavaScriptChangedAutofilledValueImpl(const FormData& form,
+                                            const FormFieldData& field,
+                                            const std::u16string& old_value);
 
   // Triggers filling of |fill_data| into |raw_form| and |raw_field|. This event
   // is called only by Autofill Assistant on the browser side and provides the
diff --git a/components/autofill/content/browser/content_autofill_router.cc b/components/autofill/content/browser/content_autofill_router.cc
index 4f36650..3b8038f0 100644
--- a/components/autofill/content/browser/content_autofill_router.cc
+++ b/components/autofill/content/browser/content_autofill_router.cc
@@ -532,6 +532,29 @@
   target->SelectFieldOptionsDidChangeImpl(browser_form);
 }
 
+void ContentAutofillRouter::JavaScriptChangedAutofilledValue(
+    ContentAutofillDriver* source,
+    const FormData& form,
+    const FormFieldData& field,
+    const std::u16string& old_value) {
+  if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) {
+    source->JavaScriptChangedAutofilledValueImpl(form, field, old_value);
+    return;
+  }
+
+  some_rfh_for_debugging_ = source->render_frame_host()->GetGlobalId();
+
+  form_forest_.UpdateTreeOfRendererForm(form, source);
+
+  TriggerReparseExcept(source);
+
+  const FormData& browser_form =
+      form_forest_.GetBrowserFormOfRendererForm(form);
+  auto* target = DriverOfFrame(browser_form.host_frame);
+  AFCHECK(target, return);
+  target->JavaScriptChangedAutofilledValueImpl(browser_form, field, old_value);
+}
+
 void ContentAutofillRouter::FillFormForAssistant(
     ContentAutofillDriver* source,
     const AutofillableData& fill_data,
diff --git a/components/autofill/content/browser/content_autofill_router.h b/components/autofill/content/browser/content_autofill_router.h
index b4ce114..61d97cd 100644
--- a/components/autofill/content/browser/content_autofill_router.h
+++ b/components/autofill/content/browser/content_autofill_router.h
@@ -209,6 +209,10 @@
   void DidEndTextFieldEditing(ContentAutofillDriver* source_driver);
   void SelectFieldOptionsDidChange(ContentAutofillDriver* source_driver,
                                    const FormData& form);
+  void JavaScriptChangedAutofilledValue(ContentAutofillDriver* source,
+                                        const FormData& form,
+                                        const FormFieldData& field,
+                                        const std::u16string& old_value);
 
   // Event called by Autofill Assistant as if it was called by the renderer.
   void FillFormForAssistant(ContentAutofillDriver* source_driver,
diff --git a/components/autofill/content/common/mojom/autofill_driver.mojom b/components/autofill/content/common/mojom/autofill_driver.mojom
index 5fa1c41..5db3e7b 100644
--- a/components/autofill/content/common/mojom/autofill_driver.mojom
+++ b/components/autofill/content/common/mojom/autofill_driver.mojom
@@ -86,6 +86,15 @@
 
   // Sent when a text field is done editing.
   DidEndTextFieldEditing();
+
+  // Sent when |field| was in autofilled state but JavaScript modified the
+  // value. Note that from a renderer's perspective, modifying the value with
+  // JavaScript leads to a state where the field is not considered autofilled
+  // anymore. So this notification won't be sent again until the field gets
+  // autofilled again.
+  JavaScriptChangedAutofilledValue(FormData form,
+                     FormFieldData field,
+                     mojo_base.mojom.String16 old_value);
 };
 
 // There is one instance of this interface per web contents in the browser
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index f8448c6..6b73adc1 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -205,6 +205,13 @@
   void DidEndTextFieldEditing() override {
     DeferMsg(&mojom::AutofillDriver::DidEndTextFieldEditing);
   }
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override {
+    DeferMsg(&mojom::AutofillDriver::JavaScriptChangedAutofilledValue, form,
+             field, old_value);
+  }
 
   AutofillAgent* agent_ = nullptr;
   base::WeakPtrFactory<DeferringAutofillDriver> weak_ptr_factory_{this};
@@ -1170,7 +1177,15 @@
 void AutofillAgent::JavaScriptChangedAutofilledValue(
     const blink::WebFormControlElement& element,
     const blink::WebString& old_value) {
-  // TODO(crbug.com/1314360): Send event to browser.
+  if (old_value == element.Value())
+    return;
+  FormData form;
+  FormFieldData field;
+  if (FindFormAndFieldForFormControlElement(element, field_data_manager_.get(),
+                                            &form, &field)) {
+    GetAutofillDriver().JavaScriptChangedAutofilledValue(form, field,
+                                                         old_value.Utf16());
+  }
 }
 
 void AutofillAgent::OnProvisionallySaveForm(
diff --git a/components/autofill/content/renderer/autofill_agent_browsertest.cc b/components/autofill/content/renderer/autofill_agent_browsertest.cc
index 0ce0d30..ecd2e89 100644
--- a/components/autofill/content/renderer/autofill_agent_browsertest.cc
+++ b/components/autofill/content/renderer/autofill_agent_browsertest.cc
@@ -85,6 +85,12 @@
               (const FormData& form),
               (override));
   MOCK_METHOD(void,
+              JavaScriptChangedAutofilledValue,
+              (const FormData& form,
+               const FormFieldData& field,
+               const std::u16string& old_value),
+              (override));
+  MOCK_METHOD(void,
               AskForValuesToFill,
               (int32_t query_id,
                const FormData& form,
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
index 4ae3017..f3e66bf7 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
@@ -157,6 +157,10 @@
   background-color: #F8BBD0;
 }
 
+.log-entry[scope='WebsiteModifiedFieldValue'] {
+  background-color: #FF7C5D;
+}
+
 /*
  * Checkboxes add/remove hide-<Scope> classes to the #log-entries. Hiding of the
  * relevant <div>'s and adjacent <hr>'s is implemented by these classes.
@@ -212,6 +216,11 @@
   display: none;
 }
 
+.hide-WebsiteModifiedFieldValue .log-entry[scope=WebsiteModifiedFieldValue],
+.hide-WebsiteModifiedFieldValue .log-entry[scope=WebsiteModifiedFieldValue] + hr {
+  display: none;
+}
+
 .form {
   border: 1px black solid;
   margin: 3px;
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
index 8dea262..b21c1894 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
@@ -255,6 +255,7 @@
     'AutofillServer',
     'Metrics',
     'AddressProfileFormImport',
+    'WebsiteModifiedFieldValue',
   ];
   const logDiv = document.getElementById('log-entries');
   const autoScrollInput = document.getElementById('enable-autoscroll');
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 64b4d0c..9997b340 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -195,6 +195,16 @@
   // Invoked when the options of a select element in the |form| changed.
   virtual void SelectFieldOptionsDidChange(const FormData& form) = 0;
 
+  // Invoked after JavaScript set the value of |field| in |form|. Only called
+  // if |field| was in autofilled state. Note that from a renderer's
+  // perspective, modifying the value with JavaScript leads to a state where
+  // the field is not considered autofilled anymore. So this notification won't
+  // be sent again until the field gets autofilled again.
+  virtual void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) = 0;
+
   // Invoked when the field type predictions are downloaded from the autofill
   // server.
   virtual void PropagateAutofillPredictions(
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 32c4f3988..dd63441f5 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -91,6 +91,12 @@
               (const FormData& form),
               (override));
   MOCK_METHOD(void,
+              JavaScriptChangedAutofilledValue,
+              (const FormData& form,
+               const FormFieldData& field,
+               const std::u16string& old_value),
+              (override));
+  MOCK_METHOD(void,
               PropagateAutofillPredictions,
               (const std::vector<FormStructure*>& forms),
               (override));
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 066225ea..98285ae 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1485,6 +1485,47 @@
     TriggerRefill(form);
 }
 
+void BrowserAutofillManager::JavaScriptChangedAutofilledValue(
+    const FormData& form,
+    const FormFieldData& field,
+    const std::u16string& old_value) {
+  // Log to chrome://autofill-internals that a field's value was set by
+  // JavaScript.
+  if (log_manager()) {
+    auto StructureOfString = [](std::u16string str) {
+      for (auto& c : str) {
+        if (base::IsAsciiAlpha(c)) {
+          c = 'a';
+        } else if (base::IsAsciiDigit(c)) {
+          c = '0';
+        } else if (base::IsAsciiWhitespace(c)) {
+          c = ' ';
+        } else {
+          c = '$';
+        }
+      }
+      return str;
+    };
+    std::string field_number = "unknown";
+    for (size_t i = 0; i < form.fields.size(); ++i) {
+      if (form.fields[i].global_id() == field.global_id()) {
+        field_number = base::StringPrintf("Field %zu", i);
+      }
+    }
+    LogBuffer change;
+    change << Tag{"div"} << Attrib{"class", "form"};
+    change << field << Br{};
+    change << "Old value structure: '"
+           << StructureOfString(old_value.substr(0, 80)) << "'" << Br{};
+    change << "New value structure: '"
+           << StructureOfString(field.value.substr(0, 80)) << "'";
+    log_manager()->Log() << LoggingScope::kWebsiteModifiedFieldValue
+                         << LogMessage::kJavaScriptChangedAutofilledValue
+                         << Br{} << Tag{"table"} << Tr{} << field_number
+                         << std::move(change);
+  }
+}
+
 void BrowserAutofillManager::PropagateAutofillPredictions(
     const std::vector<FormStructure*>& forms) {
   client()->PropagateAutofillPredictions(driver(), forms);
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 5c84077..4be86f9 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -251,6 +251,10 @@
   void OnDidEndTextFieldEditing() override;
   void OnHidePopup() override;
   void SelectFieldOptionsDidChange(const FormData& form) override;
+  void JavaScriptChangedAutofilledValue(
+      const FormData& form,
+      const FormFieldData& field,
+      const std::u16string& old_value) override;
   void PropagateAutofillPredictions(
       const std::vector<FormStructure*>& forms) override;
   void Reset() override;
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index 9b02abc..87a2197 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -379,7 +379,8 @@
       return country == "US" && !IsValidZip(data);
 
     case PHONE_HOME_WHOLE_NUMBER:
-      return !i18n::PhoneObject(data, country).IsValidNumber();
+      return !i18n::PhoneObject(data, country, /*infer_country_code=*/false)
+                  .IsValidNumber();
 
     case EMAIL_ADDRESS:
       return !IsValidEmailAddress(data);
diff --git a/components/autofill/core/browser/data_model/phone_number.cc b/components/autofill/core/browser/data_model/phone_number.cc
index 7a9cefb..e3fa5b1 100644
--- a/components/autofill/core/browser/data_model/phone_number.cc
+++ b/components/autofill/core/browser/data_model/phone_number.cc
@@ -290,8 +290,16 @@
 
 void PhoneNumber::UpdateCacheIfNeeded(const std::string& app_locale) const {
   std::string region = GetRegion(*profile_, app_locale);
-  if (!number_.empty() && cached_parsed_phone_.region() != region)
-    cached_parsed_phone_ = i18n::PhoneObject(number_, region);
+  if (!number_.empty() && cached_parsed_phone_.region() != region) {
+    // To enable filling of country calling codes for nationally formatted
+    // numbers, infer it from the `profile_`'s country information while parsing
+    // the number.
+    cached_parsed_phone_ = i18n::PhoneObject(
+        number_, region,
+        /*infer_country_code=*/profile_->HasInfo(ADDRESS_HOME_COUNTRY) &&
+            base::FeatureList::IsEnabled(
+                features::kAutofillInferCountryCallingCode));
+  }
 }
 
 PhoneNumber::PhoneCombineHelper::PhoneCombineHelper() {}
diff --git a/components/autofill/core/browser/data_model/phone_number_unittest.cc b/components/autofill/core/browser/data_model/phone_number_unittest.cc
index 1a43db6..4bee80c7 100644
--- a/components/autofill/core/browser/data_model/phone_number_unittest.cc
+++ b/components/autofill/core/browser/data_model/phone_number_unittest.cc
@@ -165,6 +165,51 @@
   EXPECT_EQ(u"514", phone.GetInfo(PHONE_HOME_CITY_CODE, kLocale));
 }
 
+TEST(PhoneNumberTest, InferCountryCallingCode) {
+  base::test::ScopedFeatureList complement_calling_code_enabled;
+  complement_calling_code_enabled.InitAndEnableFeature(
+      features::kAutofillInferCountryCallingCode);
+
+  AutofillProfile profile;
+  PhoneNumber phone(&profile);
+  const char kLocale[] = "US";
+
+  // No country information available and thus no calling code inferred.
+  EXPECT_TRUE(
+      phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-5678", kLocale));
+  EXPECT_TRUE(phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale).empty());
+  EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+  EXPECT_EQ(u"(650) 234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+  // With country information available, the calling code is inferred.
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
+  EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"6502345678", kLocale));
+  EXPECT_EQ(u"1", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+  EXPECT_EQ(u"16502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+  EXPECT_EQ(u"1 650-234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+  // Pre-formatted number.
+  // In this case the raw info is kept as-is, while the calling code is inferred
+  // for the filling information.
+  EXPECT_TRUE(
+      phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-5678", kLocale));
+  EXPECT_EQ(u"1", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+  EXPECT_EQ(u"16502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+  EXPECT_EQ(u"(650) 234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+  // Different country.
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
+  EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"015787912345", kLocale));
+  EXPECT_EQ(u"49", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+  EXPECT_EQ(u"+4915787912345", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+  EXPECT_EQ(u"+49 1578 7912345", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_EQ(u"015787912345",
+            phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+}
+
 // Test that cached phone numbers are correctly invalidated and updated.
 TEST(PhoneNumberTest, UpdateCachedPhoneNumber) {
   AutofillProfile profile;
diff --git a/components/autofill/core/browser/geo/phone_number_i18n.cc b/components/autofill/core/browser/geo/phone_number_i18n.cc
index a93f2bfb..8368f0d6 100644
--- a/components/autofill/core/browser/geo/phone_number_i18n.cc
+++ b/components/autofill/core/browser/geo/phone_number_i18n.cc
@@ -367,13 +367,9 @@
 }
 
 PhoneObject::PhoneObject(const std::u16string& number,
-                         const std::string& region) {
+                         const std::string& region,
+                         bool infer_country_code) {
   DCHECK_EQ(2u, region.size());
-  // TODO(isherman): Autofill profiles should always have a |region| set, but in
-  // some cases it should be marked as implicit.  Otherwise, phone numbers
-  // might behave differently when they are synced across computers:
-  // [ http://crbug.com/100845 ].  Once the bug is fixed, add a DCHECK here to
-  // verify.
 
   std::unique_ptr<::i18n::phonenumbers::PhoneNumber> i18n_number(
       new ::i18n::phonenumbers::PhoneNumber);
@@ -383,6 +379,13 @@
     // The formatted and normalized versions will be set on the first call to
     // the coresponding methods.
     i18n_number_ = std::move(i18n_number);
+    // `ParsePhoneNumber()` only sets `country_code_` for internationally
+    // formatted numbers. `i18n_number_`'s country_code defaults to `region` in,
+    // this case. If `infer_country_code` is true, fall back to that.
+    if (infer_country_code && country_code_.empty() &&
+        i18n_number_->has_country_code()) {
+      country_code_ = base::NumberToString16(i18n_number_->country_code());
+    }
   } else {
     // Parsing failed. Store passed phone "as is" into |whole_number_|.
     whole_number_ = number;
diff --git a/components/autofill/core/browser/geo/phone_number_i18n.h b/components/autofill/core/browser/geo/phone_number_i18n.h
index 678c615..bb9b996 100644
--- a/components/autofill/core/browser/geo/phone_number_i18n.h
+++ b/components/autofill/core/browser/geo/phone_number_i18n.h
@@ -117,7 +117,9 @@
 // The cached phone number, does parsing only once, improves performance.
 class PhoneObject {
  public:
-  PhoneObject(const std::u16string& number, const std::string& default_region);
+  PhoneObject(const std::u16string& number,
+              const std::string& default_region,
+              bool infer_country_code);
   PhoneObject(const PhoneObject&);
   PhoneObject();
   ~PhoneObject();
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 61739ba..70c8411 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -84,6 +84,12 @@
 const base::Feature kAutofillAllowNonHttpActivation{
     "AutofillAllowNonHttpActivation", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// If enabled, the country calling code for nationally formatted phone numbers
+// is inferred from the profile's country, if available.
+// TODO(crbug.com/1311937): Cleanup when launched.
+const base::Feature kAutofillInferCountryCallingCode{
+    "AutofillInferCountryCallingCode", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, whenever a form without a country field is parsed, the profile's
 // country code is complemented with the predicted country code, used to
 // determine the address requirements.
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index e479d9a..3376929d 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -45,6 +45,8 @@
 COMPONENT_EXPORT(AUTOFILL)
 extern const base::Feature kAutofillAllowNonHttpActivation;
 COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillInferCountryCallingCode;
+COMPONENT_EXPORT(AUTOFILL)
 extern const base::Feature kAutofillComplementCountryCodeOnImport;
 COMPONENT_EXPORT(AUTOFILL)
 extern const base::Feature kAutofillConsiderPlaceholderForParsing;
diff --git a/components/autofill/core/common/autofill_internals/log_message.h b/components/autofill/core/common/autofill_internals/log_message.h
index 9430cb5..8842eef6 100644
--- a/components/autofill/core/common/autofill_internals/log_message.h
+++ b/components/autofill/core/common/autofill_internals/log_message.h
@@ -53,7 +53,10 @@
   T(CardUploadDecisionUploadNotOffered, "Credit card upload was not offered.") \
   T(SuggestionSuppressed, "Autofill suggestion(s) suppressed.")                \
   T(Rationalization, "Rationalization: ")                                      \
-  T(ProcessingServerData, "Processing server data.")
+  T(ProcessingServerData, "Processing server data.")                           \
+  T(JavaScriptChangedAutofilledValue,                                          \
+    "JavaScript set value of autofilled "                                      \
+    "field: ")
 
 // Log messages for chrome://autofill-internals.
 
diff --git a/components/autofill/core/common/autofill_internals/logging_scope.h b/components/autofill/core/common/autofill_internals/logging_scope.h
index fad91e60..2abc842b 100644
--- a/components/autofill/core/common/autofill_internals/logging_scope.h
+++ b/components/autofill/core/common/autofill_internals/logging_scope.h
@@ -36,7 +36,9 @@
   /* If credit card upload is either enabled or disabled. */           \
   T(CreditCardUploadStatus)                                            \
   /* Whether or not card upload was offered to the user. */            \
-  T(CardUploadDecision)
+  T(CardUploadDecision)                                                \
+  /* The website modified a field */                                   \
+  T(WebsiteModifiedFieldValue)
 
 // Define a bunch of logging scopes: kContext, kParsing, ...
 #define AUTOFILL_TEMPLATE(NAME) k##NAME,
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index fc14474c..51dd50a 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -27,7 +27,11 @@
 }
 
 proto_library("test_proto") {
-  sources = [ "parse_jspb_test.proto" ]
+  sources = [
+    "external_action_extension_test.proto",
+    "parse_jspb_test.proto",
+  ]
+  link_deps = [ "//components/autofill_assistant/browser/public:proto" ]
 }
 
 static_library("browser") {
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 9324126..fe0162b 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -477,7 +477,7 @@
   virtual void RequestExternalAction(
       const ExternalActionProto& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) = 0;
 
   // Returns whether or not this instance of Autofill Assistant must use a
diff --git a/components/autofill_assistant/browser/actions/external_action.cc b/components/autofill_assistant/browser/actions/external_action.cc
index bb733ab9..ee6d6fa 100644
--- a/components/autofill_assistant/browser/actions/external_action.cc
+++ b/components/autofill_assistant/browser/actions/external_action.cc
@@ -52,14 +52,16 @@
   }
 }
 
-void ExternalAction::OnExternalActionFinished(
-    ExternalActionDelegate::ActionResult result) {
+void ExternalAction::OnExternalActionFinished(const external::Result& result) {
   if (!callback_) {
     return;
   }
 
-  EndAction(result.success ? ClientStatus(ACTION_APPLIED)
-                           : ClientStatus(UNKNOWN_ACTION_STATUS));
+  *processed_action_proto_->mutable_external_action_result()
+       ->mutable_result_info() = result.result_info();
+
+  EndAction(result.success() ? ClientStatus(ACTION_APPLIED)
+                             : ClientStatus(UNKNOWN_ACTION_STATUS));
 }
 
 void ExternalAction::EndAction(const ClientStatus& status) {
diff --git a/components/autofill_assistant/browser/actions/external_action.h b/components/autofill_assistant/browser/actions/external_action.h
index e4db17b..193e059c1 100644
--- a/components/autofill_assistant/browser/actions/external_action.h
+++ b/components/autofill_assistant/browser/actions/external_action.h
@@ -26,7 +26,7 @@
   void InternalProcessAction(ProcessActionCallback callback) override;
 
   void StartDomChecks();
-  void OnExternalActionFinished(ExternalActionDelegate::ActionResult success);
+  void OnExternalActionFinished(const external::Result& success);
   void EndAction(const ClientStatus& status);
 
   ProcessActionCallback callback_;
diff --git a/components/autofill_assistant/browser/actions/external_action_unittest.cc b/components/autofill_assistant/browser/actions/external_action_unittest.cc
index 0815397..41fbccc 100644
--- a/components/autofill_assistant/browser/actions/external_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/external_action_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/external_action_extension_test.pb.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -15,10 +16,11 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::Eq;
 using ::testing::Property;
 using ::testing::Return;
 
-class ExternalActionTest : public testing::Test {
+class ExternalActionTest : public ::testing::Test {
  protected:
   void Run() {
     ON_CALL(mock_action_delegate_, SupportsExternalActions)
@@ -35,26 +37,61 @@
   ExternalActionProto proto_;
 };
 
+external::Result MakeResult(bool success) {
+  external::Result result;
+  result.set_success(success);
+  testing::TestResultExtension test_extension_proto;
+  test_extension_proto.set_text("test text");
+
+  *result.mutable_result_info()->MutableExtension(
+      testing::test_result_extension) = std::move(test_extension_proto);
+  return result;
+}
+
 TEST_F(ExternalActionTest, Success) {
   proto_.mutable_info();
-  EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
-      .WillOnce(RunOnceCallback<2>(
-          ExternalActionDelegate::ActionResult{.success = true}));
 
-  EXPECT_CALL(
-      callback_,
-      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+      .WillOnce(RunOnceCallback<2>(MakeResult(/* success= */ true)));
+
+  std::unique_ptr<ProcessedActionProto> returned_processed_action_proto;
+  EXPECT_CALL(callback_, Run)
+      .WillOnce(
+          [&returned_processed_action_proto](
+              std::unique_ptr<ProcessedActionProto> processed_action_proto) {
+            returned_processed_action_proto = std::move(processed_action_proto);
+          });
   Run();
+  EXPECT_THAT(returned_processed_action_proto->status(), Eq(ACTION_APPLIED));
+  EXPECT_TRUE(returned_processed_action_proto->has_external_action_result());
+  EXPECT_THAT(returned_processed_action_proto->external_action_result()
+                  .result_info()
+                  .GetExtension(testing::test_result_extension)
+                  .text(),
+              Eq("test text"));
 }
 
 TEST_F(ExternalActionTest, ExternalFailure) {
   proto_.mutable_info();
+
+  std::unique_ptr<ProcessedActionProto> returned_processed_action_proto;
+  EXPECT_CALL(callback_, Run)
+      .WillOnce(
+          [&returned_processed_action_proto](
+              std::unique_ptr<ProcessedActionProto> processed_action_proto) {
+            returned_processed_action_proto = std::move(processed_action_proto);
+          });
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
-      .WillOnce(RunOnceCallback<2>(
-          ExternalActionDelegate::ActionResult{.success = false}));
-  EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
-                                              UNKNOWN_ACTION_STATUS))));
+      .WillOnce(RunOnceCallback<2>(MakeResult(/* success= */ false)));
   Run();
+  EXPECT_THAT(returned_processed_action_proto->status(),
+              Eq(UNKNOWN_ACTION_STATUS));
+  EXPECT_TRUE(returned_processed_action_proto->has_external_action_result());
+  EXPECT_THAT(returned_processed_action_proto->external_action_result()
+                  .result_info()
+                  .GetExtension(testing::test_result_extension)
+                  .text(),
+              Eq("test text"));
 }
 
 TEST_F(ExternalActionTest, FailsIfProtoExtensionInfoNotSet) {
@@ -83,10 +120,10 @@
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
       .WillOnce([](const ExternalActionProto& external_action,
                    base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(ExternalActionDelegate::ActionResult
-                                               result)> end_action_callback) {
+                   base::OnceCallback<void(const external::Result& result)>
+                       end_action_callback) {
         std::move(start_dom_checks_callback).Run();
-        std::move(end_action_callback).Run({.success = true});
+        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
       });
   EXPECT_CALL(mock_action_delegate_, WaitForDom);
   EXPECT_CALL(
@@ -102,10 +139,10 @@
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
       .WillOnce([](const ExternalActionProto& external_action,
                    base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(ExternalActionDelegate::ActionResult
-                                               result)> end_action_callback) {
+                   base::OnceCallback<void(const external::Result& result)>
+                       end_action_callback) {
         std::move(start_dom_checks_callback).Run();
-        std::move(end_action_callback).Run({.success = true});
+        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
       });
   EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
   EXPECT_CALL(
@@ -121,11 +158,11 @@
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
       .WillOnce([](const ExternalActionProto& external_action,
                    base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(ExternalActionDelegate::ActionResult
-                                               result)> end_action_callback) {
+                   base::OnceCallback<void(const external::Result& result)>
+                       end_action_callback) {
         // We call the |end_action_callback| without calling
         // |start_dom_checks_callback|.
-        std::move(end_action_callback).Run({.success = true});
+        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
       });
   EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
   EXPECT_CALL(
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 308221fe..16c3d5c 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -215,12 +215,11 @@
            base::OnceCallback<void(bool, const GetUserDataResponseProto&)>
                callback));
   MOCK_METHOD0(SupportsExternalActions, bool());
-  MOCK_METHOD3(
-      RequestExternalAction,
-      void(const ExternalActionProto& external_action,
-           base::OnceCallback<void()> start_dom_checks_callback,
-           base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
-               end_action_callback));
+  MOCK_METHOD3(RequestExternalAction,
+               void(const ExternalActionProto& external_action,
+                    base::OnceCallback<void()> start_dom_checks_callback,
+                    base::OnceCallback<void(const external::Result& result)>
+                        end_action_callback));
   MOCK_CONST_METHOD0(MustUseBackendData, bool());
 
   base::WeakPtr<ActionDelegate> GetWeakPtr() const override {
diff --git a/components/autofill_assistant/browser/external_action_extension_test.proto b/components/autofill_assistant/browser/external_action_extension_test.proto
new file mode 100644
index 0000000..18efa16
--- /dev/null
+++ b/components/autofill_assistant/browser/external_action_extension_test.proto
@@ -0,0 +1,19 @@
+// 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.
+
+syntax = "proto2";
+
+package autofill_assistant.testing;
+
+option optimize_for = LITE_RUNTIME;
+
+import "public/external_action.proto";
+
+extend external.ResultInfo {
+  optional TestResultExtension test_result_extension = 100;
+}
+
+message TestResultExtension {
+  optional string text = 1;
+}
diff --git a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
index 61f539b..fb78baa 100644
--- a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
@@ -171,9 +171,11 @@
 void FakeScriptExecutorUiDelegate::ExecuteExternalAction(
     const external::Action& external_action,
     base::OnceCallback<void()> start_dom_checks_callback,
-    base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+    base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
-  std::move(end_action_callback).Run({true});
+  external::Result result;
+  result.set_success(true);
+  std::move(end_action_callback).Run(result);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
index 479951cf..6b72f0b 100644
--- a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
@@ -83,7 +83,7 @@
   void ExecuteExternalAction(
       const external::Action& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
 
   const std::vector<Details>& GetDetails() { return details_; }
diff --git a/components/autofill_assistant/browser/headless/headless_ui_controller.cc b/components/autofill_assistant/browser/headless/headless_ui_controller.cc
index e005c79..233f788 100644
--- a/components/autofill_assistant/browser/headless/headless_ui_controller.cc
+++ b/components/autofill_assistant/browser/headless/headless_ui_controller.cc
@@ -16,7 +16,7 @@
 void HeadlessUiController::ExecuteExternalAction(
     const external::Action& external_action,
     base::OnceCallback<void()> start_dom_checks_callback,
-    base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+    base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   DCHECK(action_extension_delegate_);
 
diff --git a/components/autofill_assistant/browser/headless/headless_ui_controller.h b/components/autofill_assistant/browser/headless/headless_ui_controller.h
index 769548e..deab955 100644
--- a/components/autofill_assistant/browser/headless/headless_ui_controller.h
+++ b/components/autofill_assistant/browser/headless/headless_ui_controller.h
@@ -81,7 +81,7 @@
   void ExecuteExternalAction(
       const external::Action& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
 
  private:
diff --git a/components/autofill_assistant/browser/js_flow_util.cc b/components/autofill_assistant/browser/js_flow_util.cc
index e99d986..4db0305 100644
--- a/components/autofill_assistant/browser/js_flow_util.cc
+++ b/components/autofill_assistant/browser/js_flow_util.cc
@@ -215,6 +215,9 @@
     case ProcessedActionProto::kSaveSubmittedPasswordResult:
       proto = &processed_action.save_submitted_password_result();
       break;
+    case ProcessedActionProto::kExternalActionResult:
+      proto = &processed_action.external_action_result();
+      break;
     case ProcessedActionProto::RESULT_DATA_NOT_SET:
       return absl::nullopt;
   }
diff --git a/components/autofill_assistant/browser/public/external_action.proto b/components/autofill_assistant/browser/public/external_action.proto
index 89dbf82..e75afaf 100644
--- a/components/autofill_assistant/browser/public/external_action.proto
+++ b/components/autofill_assistant/browser/public/external_action.proto
@@ -10,10 +10,28 @@
 
 package autofill_assistant.external;
 
+// Defines an action to be executed by the ExternalActionDelegate.
 message Action {
+  // Integrator-specific information. This was forwarded without modifications.
   optional ActionInfo info = 1;
 }
 
+// Extended by the integrator.
 message ActionInfo {
   extensions 100 to max;
 }
+
+// The result of the execution of |Action|
+message Result {
+  // Integrator-specific information. This will be forwarded to the backend
+  // without modification.
+  optional ResultInfo result_info = 1;
+
+  // Whether the action was successful.
+  optional bool success = 2;
+}
+
+// Extended by the integrator.
+message ResultInfo {
+  extensions 100 to max;
+}
diff --git a/components/autofill_assistant/browser/public/external_action_delegate.h b/components/autofill_assistant/browser/public/external_action_delegate.h
index c9c4d67..9a1af5e 100644
--- a/components/autofill_assistant/browser/public/external_action_delegate.h
+++ b/components/autofill_assistant/browser/public/external_action_delegate.h
@@ -14,10 +14,6 @@
 // script.
 class ExternalActionDelegate {
  public:
-  struct ActionResult {
-    bool success = false;
-  };
-
   // Called when the script reaches an external action.
   // The |start_dom_checks_callback| can optionally be called to start the DOM
   // checks. This will allow interrupts to trigger (if the action itself allows
@@ -26,7 +22,8 @@
   virtual void OnActionRequested(
       const external::Action& action_info,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ActionResult result)> end_action_callback) = 0;
+      base::OnceCallback<void(const external::Result& result)>
+          end_action_callback) = 0;
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index d5a6854..683e369 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -1116,7 +1116,7 @@
 void ScriptExecutor::RequestExternalAction(
     const ExternalActionProto& external_action,
     base::OnceCallback<void()> start_dom_checks_callback,
-    base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+    base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   bool prompt = external_action.allow_interrupt() ||
                 external_action.show_touchable_area();
@@ -1140,9 +1140,9 @@
 void ScriptExecutor::OnExternalActionFinished(
     const ExternalActionProto& external_action,
     const bool prompt,
-    base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+    base::OnceCallback<void(const external::Result& result)>
         end_action_callback,
-    ExternalActionDelegate::ActionResult result) {
+    const external::Result& result) {
   if (prompt) {
     CleanUpAfterPrompt(external_action.show_touchable_area());
   }
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index f9c32d4d..191455c 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -270,7 +270,7 @@
   void RequestExternalAction(
       const ExternalActionProto& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
   bool MustUseBackendData() const override;
 
@@ -347,9 +347,8 @@
   void OnExternalActionFinished(
       const ExternalActionProto& external_action,
       const bool prompt,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
-          callback,
-      ExternalActionDelegate::ActionResult result);
+      base::OnceCallback<void(const external::Result& result)> callback,
+      const external::Result& result);
 
   // Maybe shows the message specified in a callout, depending on the current
   // state and client settings.
diff --git a/components/autofill_assistant/browser/script_executor_ui_delegate.h b/components/autofill_assistant/browser/script_executor_ui_delegate.h
index cf9764e8..cf52d1e 100644
--- a/components/autofill_assistant/browser/script_executor_ui_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_ui_delegate.h
@@ -98,7 +98,7 @@
   virtual void ExecuteExternalAction(
       const external::Action& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) = 0;
 
  protected:
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 2f01478..683223f6 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1154,6 +1154,8 @@
     JsFlowProto.Result js_flow_result = 37;
     // Should be set as a result of SaveSubmittedPassword.
     SaveSubmittedPasswordProto.Result save_submitted_password_result = 38;
+    // Should be set as a result of an ExternalAction.
+    ExternalActionProto.Result external_action_result = 39;
   }
 
   // Reports information about navigation that happened while
@@ -3436,4 +3438,8 @@
   // Whether interrupts should be executed while Autofill Assistant is in prompt
   // state.
   optional bool allow_interrupt = 3;
+
+  message Result {
+    optional external.ResultInfo result_info = 1;
+  }
 }
diff --git a/components/autofill_assistant/browser/ui_controller.cc b/components/autofill_assistant/browser/ui_controller.cc
index e283afb..1f829d0 100644
--- a/components/autofill_assistant/browser/ui_controller.cc
+++ b/components/autofill_assistant/browser/ui_controller.cc
@@ -1181,7 +1181,7 @@
 void UiController::ExecuteExternalAction(
     const external::Action& external_action,
     base::OnceCallback<void()> start_dom_checks_callback,
-    base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+    base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   NOTREACHED() << "Flows using default UI don't support external actions.";
 }
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index 9aadb62..c1adeb8 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -196,7 +196,7 @@
   void ExecuteExternalAction(
       const external::Action& external_action,
       base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(ExternalActionDelegate::ActionResult result)>
+      base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
 
   // Overrides AutofillAssistantTtsController::TtsEventDelegate
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 1b3bd92..47aedbad4 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "9.21",
-  "log_list_timestamp": "2022-05-23T12:55:37Z",
+  "version": "9.22",
+  "log_list_timestamp": "2022-05-24T12:55:38Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/chrome_cleaner/public/constants/BUILD.gn b/components/chrome_cleaner/public/constants/BUILD.gn
index 38c17138..7e16990 100644
--- a/components/chrome_cleaner/public/constants/BUILD.gn
+++ b/components/chrome_cleaner/public/constants/BUILD.gn
@@ -2,10 +2,26 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//build/config/chrome_build.gni")
+
+declare_args() {
+  # Whether software reporter is enabled for this build. This defaults to true
+  # for Chrome branded builds, but can be disabled manually if a Chrome branded
+  # build is required without necessarily needing the software reporter.
+  enable_software_reporter = is_chrome_branded
+}
+
 source_set("constants") {
   sources = [
     "constants.cc",
     "constants.h",
     "result_codes.h",
   ]
+  public_deps = [ ":buildflags" ]
+}
+
+buildflag_header("buildflags") {
+  header = "buildflags.h"
+  flags = [ "ENABLE_SOFTWARE_REPORTER=$enable_software_reporter" ]
 }
diff --git a/components/commerce/ios/browser/web_state_wrapper.mm b/components/commerce/ios/browser/web_state_wrapper.mm
index febfd52..408ec73 100644
--- a/components/commerce/ios/browser/web_state_wrapper.mm
+++ b/components/commerce/ios/browser/web_state_wrapper.mm
@@ -5,7 +5,6 @@
 #include "components/commerce/ios/browser/web_state_wrapper.h"
 
 #include "base/bind.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "ios/web/public/browser_state.h"
 #include "ios/web/public/js_messaging/web_frame.h"
@@ -45,7 +44,7 @@
   }
 
   web_state_->GetWebFramesManager()->GetMainWebFrame()->ExecuteJavaScript(
-      base::UTF16ToUTF8(script),
+      script,
       base::BindOnce(
           [](base::OnceCallback<void(const base::Value)> callback,
              const base::Value* response) {
diff --git a/components/page_load_metrics/renderer/BUILD.gn b/components/page_load_metrics/renderer/BUILD.gn
index d53e654..c80874c3 100644
--- a/components/page_load_metrics/renderer/BUILD.gn
+++ b/components/page_load_metrics/renderer/BUILD.gn
@@ -34,6 +34,7 @@
     "fake_page_timing_sender.cc",
     "fake_page_timing_sender.h",
     "metrics_render_frame_observer_unittest.cc",
+    "page_timing_metadata_recorder_unittest.cc",
     "page_timing_metrics_sender_unittest.cc",
   ]
 
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index 8492f22d..5de4643 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -510,11 +510,16 @@
   }
   if (perf.FirstInputDelay().has_value()) {
     timing->interactive_timing->first_input_delay = *perf.FirstInputDelay();
+    monotonic_timing.first_input_delay = perf.FirstInputDelay();
   }
   if (perf.FirstInputTimestamp().has_value()) {
     timing->interactive_timing->first_input_timestamp =
         ClampDelta((*perf.FirstInputTimestamp()).InSecondsF(), start);
   }
+  if (perf.FirstInputTimestampAsMonotonicTime()) {
+    monotonic_timing.first_input_timestamp =
+        perf.FirstInputTimestampAsMonotonicTime();
+  }
   if (perf.LongestInputDelay().has_value()) {
     timing->interactive_timing->longest_input_delay = *perf.LongestInputDelay();
   }
diff --git a/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc b/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
index fc74022..2cf6e58 100644
--- a/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
+++ b/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
@@ -4,10 +4,7 @@
 
 #include "components/page_load_metrics/renderer/page_timing_metadata_recorder.h"
 
-#include "base/profiler/sample_metadata.h"
-
 namespace page_load_metrics {
-
 namespace {
 bool IsTimeTicksRangeSensible(base::TimeTicks start, base::TimeTicks end) {
   return start <= end && end <= base::TimeTicks::Now();
@@ -38,23 +35,59 @@
 PageTimingMetadataRecorder::~PageTimingMetadataRecorder() = default;
 
 void PageTimingMetadataRecorder::UpdateMetadata(const MonotonicTiming& timing) {
+  UpdateFirstContentfulPaintMetadata(timing.navigation_start,
+                                     timing.first_contentful_paint);
+  UpdateFirstInputDelayMetadata(timing.first_input_timestamp,
+                                timing.first_input_delay);
+  timing_ = timing;
+}
+
+void PageTimingMetadataRecorder::ApplyMetadataToPastSamples(
+    base::TimeTicks period_start,
+    base::TimeTicks period_end,
+    base::StringPiece name,
+    int64_t key,
+    int64_t value,
+    base::SampleMetadataScope scope) {
+  base::ApplyMetadataToPastSamples(period_start, period_end, name, key, value,
+                                   scope);
+}
+
+void PageTimingMetadataRecorder::UpdateFirstInputDelayMetadata(
+    const absl::optional<base::TimeTicks>& first_input_timestamp,
+    const absl::optional<base::TimeDelta>& first_input_delay) {
   // Applying metadata to past samples has non-trivial cost so only do so if
   // the relevant values changed.
   const bool should_apply_metadata =
-      timing.navigation_start.has_value() &&
-      timing.first_contentful_paint.has_value() &&
-      (timing_.navigation_start != timing.navigation_start ||
-       timing_.first_contentful_paint != timing.first_contentful_paint);
-  if (should_apply_metadata &&
-      IsTimeTicksRangeSensible(*timing.navigation_start,
-                               *timing.first_contentful_paint)) {
-    base::ApplyMetadataToPastSamples(
-        *timing.navigation_start, *timing.first_contentful_paint,
-        "PageLoad.PaintTiming.NavigationToFirstContentfulPaint", instance_id_,
-        1, base::SampleMetadataScope::kProcess);
-  }
+      first_input_timestamp.has_value() && first_input_delay.has_value() &&
+      (timing_.first_input_timestamp != first_input_timestamp ||
+       timing_.first_input_delay != first_input_delay);
 
-  timing_ = timing;
+  if (should_apply_metadata) {
+    ApplyMetadataToPastSamples(
+        *first_input_timestamp, *first_input_timestamp + *first_input_delay,
+        "PageLoad.InteractiveTiming.FirstInputDelay4", /* key=*/instance_id_,
+        /* value=*/1, base::SampleMetadataScope::kProcess);
+  }
+}
+
+void PageTimingMetadataRecorder::UpdateFirstContentfulPaintMetadata(
+    const absl::optional<base::TimeTicks>& navigation_start,
+    const absl::optional<base::TimeTicks>& first_contentful_paint) {
+  // Applying metadata to past samples has non-trivial cost so only do so if
+  // the relevant values changed.
+  const bool should_apply_metadata =
+      navigation_start.has_value() && first_contentful_paint.has_value() &&
+      (timing_.navigation_start != navigation_start ||
+       timing_.first_contentful_paint != first_contentful_paint);
+  if (should_apply_metadata &&
+      IsTimeTicksRangeSensible(*navigation_start, *first_contentful_paint)) {
+    ApplyMetadataToPastSamples(
+        *navigation_start, *first_contentful_paint,
+        "PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+        /* key=*/instance_id_,
+        /* value=*/1, base::SampleMetadataScope::kProcess);
+  }
 }
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/renderer/page_timing_metadata_recorder.h b/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
index 6bf38a9..285f39f 100644
--- a/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
+++ b/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_PAGE_LOAD_METRICS_RENDERER_PAGE_TIMING_METADATA_RECORDER_H_
 #define COMPONENTS_PAGE_LOAD_METRICS_RENDERER_PAGE_TIMING_METADATA_RECORDER_H_
 
+#include "base/profiler/sample_metadata.h"
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -16,7 +17,9 @@
 // reach out to page_load_metrics owners to discuss it.
 class PageTimingMetadataRecorder {
  public:
-  // Records the monotonic times that define first contentful paint.
+  // Records the monotonic times that define
+  // - First contentful paint
+  // - First input delay
   struct MonotonicTiming {
     MonotonicTiming();
 
@@ -27,6 +30,9 @@
 
     absl::optional<base::TimeTicks> navigation_start;
     absl::optional<base::TimeTicks> first_contentful_paint;
+
+    absl::optional<base::TimeTicks> first_input_timestamp;
+    absl::optional<base::TimeDelta> first_input_delay;
   };
 
   PageTimingMetadataRecorder(const MonotonicTiming& initial_timing);
@@ -36,15 +42,32 @@
   PageTimingMetadataRecorder& operator=(const PageTimingMetadataRecorder&) =
       delete;
 
+  // Updates the metadata on past samples based on given timing. Called whenever
+  // `PageTimingMetricsSender::Update` is called.
   void UpdateMetadata(const MonotonicTiming& timing);
 
+ protected:
+  // To be overridden by test class.
+  virtual void ApplyMetadataToPastSamples(base::TimeTicks period_start,
+                                          base::TimeTicks period_end,
+                                          base::StringPiece name,
+                                          int64_t key,
+                                          int64_t value,
+                                          base::SampleMetadataScope scope);
+
  private:
+  void UpdateFirstInputDelayMetadata(
+      const absl::optional<base::TimeTicks>& first_input_timestamp,
+      const absl::optional<base::TimeDelta>& first_input_delay);
+  void UpdateFirstContentfulPaintMetadata(
+      const absl::optional<base::TimeTicks>& navigation_start,
+      const absl::optional<base::TimeTicks>& first_contentful_paint);
+
   // Uniquely identifies an instance of the PageTimingMetadataRecorder. Used to
   // distinguish page loads for different documents when applying sample
   // metadata.
   const int instance_id_;
 
-  // Records the monotonic times that define first contentful paint.
   MonotonicTiming timing_;
 };
 
diff --git a/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc b/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc
new file mode 100644
index 0000000..aac32f7
--- /dev/null
+++ b/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/renderer/page_timing_metadata_recorder.h"
+
+#include <vector>
+
+#include "base/profiler/sample_metadata.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace page_load_metrics {
+
+struct MetadataTaggingRequest {
+  base::TimeTicks period_start;
+  base::TimeTicks period_end;
+  base::StringPiece name;
+  int64_t key;
+  int64_t value;
+};
+
+class TestPageTimingMetadataRecorder : public PageTimingMetadataRecorder {
+ public:
+  explicit TestPageTimingMetadataRecorder(const MonotonicTiming& initial_timing)
+      : PageTimingMetadataRecorder(initial_timing) {}
+
+  void ApplyMetadataToPastSamples(base::TimeTicks period_start,
+                                  base::TimeTicks period_end,
+                                  base::StringPiece name,
+                                  int64_t key,
+                                  int64_t value,
+                                  base::SampleMetadataScope scope) override {
+    requests_.push_back({
+        period_start,
+        period_end,
+        name,
+        key,
+        value,
+    });
+  }
+
+  const std::vector<MetadataTaggingRequest>& GetMetadataTaggingRequests()
+      const {
+    return requests_;
+  }
+
+ private:
+  std::vector<MetadataTaggingRequest> requests_ = {};
+};
+
+using PageTimingMetadataRecorderTest = testing::Test;
+
+TEST_F(PageTimingMetadataRecorderTest, FirstContentfulPaintUpdate) {
+  PageTimingMetadataRecorder::MonotonicTiming timing = {};
+  // The PageTimingMetadataRecorder constructor is supposed to call
+  // UpdateMetadata once, but due to class construction limitation, the
+  // call to ApplyMetadataToPastSample will not be captured by test class,
+  // as the test class is not ready yet.
+  TestPageTimingMetadataRecorder recorder(timing);
+  const std::vector<MetadataTaggingRequest>& requests =
+      recorder.GetMetadataTaggingRequests();
+
+  timing.navigation_start = base::TimeTicks::Now() - base::Milliseconds(500);
+  timing.first_contentful_paint =
+      *timing.navigation_start + base::Milliseconds(10);
+  recorder.UpdateMetadata(timing);
+  ASSERT_EQ(1u, requests.size());
+  EXPECT_EQ(*timing.navigation_start, requests.at(0).period_start);
+  EXPECT_EQ(*timing.first_contentful_paint, requests.at(0).period_end);
+  EXPECT_EQ("PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+            requests.at(0).name);
+
+  // Update first contentful paint timetick should sends another request.
+  timing.first_contentful_paint =
+      *timing.navigation_start + base::Milliseconds(20);
+  recorder.UpdateMetadata(timing);
+  ASSERT_EQ(2u, requests.size());
+  EXPECT_EQ(*timing.navigation_start, requests.at(1).period_start);
+  EXPECT_EQ(*timing.first_contentful_paint, requests.at(1).period_end);
+  EXPECT_EQ("PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+            requests.at(1).name);
+
+  // If nothing modified, should not send any requests.
+  recorder.UpdateMetadata(timing);
+  EXPECT_EQ(2u, requests.size());
+}
+
+TEST_F(PageTimingMetadataRecorderTest, FirstInputDelayUpdate) {
+  PageTimingMetadataRecorder::MonotonicTiming timing = {};
+  // The PageTimingMetadataRecorder constructor is supposed to call
+  // UpdateMetadata once, but due to class construction limitation, the
+  // call to ApplyMetadataToPastSample will not be captured by test class,
+  // as the test class is not ready yet.
+  TestPageTimingMetadataRecorder recorder(timing);
+  const std::vector<MetadataTaggingRequest>& requests =
+      recorder.GetMetadataTaggingRequests();
+
+  timing.first_input_delay = base::Milliseconds(10);
+  timing.first_input_timestamp = base::TimeTicks::Now();
+  recorder.UpdateMetadata(timing);
+  ASSERT_EQ(1u, requests.size());
+  EXPECT_EQ(*timing.first_input_timestamp, requests.at(0).period_start);
+  EXPECT_EQ(*timing.first_input_timestamp + *timing.first_input_delay,
+            requests.at(0).period_end);
+  EXPECT_EQ("PageLoad.InteractiveTiming.FirstInputDelay4", requests.at(0).name);
+
+  // Update first input delay should sends another request.
+  timing.first_input_delay = base::Milliseconds(11);
+  recorder.UpdateMetadata(timing);
+  ASSERT_EQ(2u, requests.size());
+  EXPECT_EQ(*timing.first_input_timestamp, requests.at(1).period_start);
+  EXPECT_EQ(*timing.first_input_timestamp + *timing.first_input_delay,
+            requests.at(1).period_end);
+  EXPECT_EQ("PageLoad.InteractiveTiming.FirstInputDelay4", requests.at(1).name);
+
+  // If nothing modified, should not send any requests.
+  recorder.UpdateMetadata(timing);
+  EXPECT_EQ(2u, requests.size());
+}
+
+}  // namespace page_load_metrics
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index ab522d1..b9e0e99 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -743,18 +743,20 @@
   if (is_android) {
     sources += [
       "built_in_backend_to_android_backend_migrator_unittest.cc",
-      "capabilities_service_impl_unittest.cc",
-      "password_scripts_fetcher_impl_unittests.cc",
       "password_store_backend_metrics_recorder_unittest.cc",
       "password_store_backend_migration_decorator_unittest.cc",
       "password_store_proxy_backend_unittest.cc",
-      "saved_passwords_capabilities_fetcher_unittest.cc",
     ]
   }
   if (is_ios) {
     sources += [ "login_database_ios_unittest.cc" ]
   } else {
-    sources += [ "http_credentials_cleaner_unittest.cc" ]
+    sources += [
+      "capabilities_service_impl_unittest.cc",
+      "http_credentials_cleaner_unittest.cc",
+      "password_scripts_fetcher_impl_unittests.cc",
+      "saved_passwords_capabilities_fetcher_unittest.cc",
+    ]
   }
 
   if (is_win || is_mac || is_linux || is_chromeos) {
@@ -826,7 +828,7 @@
     "//url",
   ]
 
-  if (is_android) {
+  if (!is_ios) {
     deps += [
       "//components/autofill_assistant/browser/public:public",
       "//components/autofill_assistant/browser/public:unit_test_support",
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 3e7a75ae..08dca5a5 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -1045,7 +1045,7 @@
       UpdateInsecureCredentials(primary_key,
                                 form_with_encrypted_password.password_issues);
     }
-    UpdatePasswordNote(primary_key, form.note);
+    UpdatePasswordNotes(primary_key, form.notes);
     list.emplace_back(PasswordStoreChange::ADD,
                       std::move(form_with_encrypted_password), primary_key,
                       /*password_changed=*/false);
@@ -1074,7 +1074,7 @@
       insecure_changed = UpdateInsecureCredentials(
           primary_key, form_with_encrypted_password.password_issues);
     }
-    UpdatePasswordNote(primary_key, form_with_encrypted_password.note);
+    UpdatePasswordNotes(primary_key, form_with_encrypted_password.notes);
     list.emplace_back(PasswordStoreChange::ADD,
                       std::move(form_with_encrypted_password),
                       FormPrimaryKey(db_.GetLastInsertRowId()),
@@ -1191,8 +1191,8 @@
   InsecureCredentialsChanged insecure_changed = UpdateInsecureCredentials(
       FormPrimaryKey(old_primary_key_password.primary_key),
       form_with_encrypted_password.password_issues);
-  UpdatePasswordNote(FormPrimaryKey(old_primary_key_password.primary_key),
-                     form.note);
+  UpdatePasswordNotes(FormPrimaryKey(old_primary_key_password.primary_key),
+                      form.notes);
 
   PasswordStoreChangeList list;
   FillFormInStore(&form_with_encrypted_password);
@@ -1418,7 +1418,7 @@
   form->date_password_modified = base::Time::FromDeltaSinceWindowsEpoch(
       base::Microseconds(s.ColumnInt64(COLUMN_DATE_PASSWORD_MODIFIED)));
   PopulateFormWithPasswordIssues(FormPrimaryKey(*primary_key), form);
-  PopulateFormWithNote(FormPrimaryKey(*primary_key), form);
+  PopulateFormWithNotes(FormPrimaryKey(*primary_key), form);
 
   return ENCRYPTION_RESULT_SUCCESS;
 }
@@ -2026,24 +2026,24 @@
   return InsecureCredentialsChanged(changed);
 }
 
-void LoginDatabase::PopulateFormWithNote(FormPrimaryKey primary_key,
-                                         PasswordForm* form) const {
+void LoginDatabase::PopulateFormWithNotes(FormPrimaryKey primary_key,
+                                          PasswordForm* form) const {
   if (!base::FeatureList::IsEnabled(features::kPasswordNotes))
     return;
-  absl::optional<PasswordNote> note =
-      password_notes_table_.GetPasswordNote(primary_key);
-  form->note = note ? note.value() : PasswordNote();
+  form->notes = password_notes_table_.GetPasswordNotes(primary_key);
 }
 
-void LoginDatabase::UpdatePasswordNote(FormPrimaryKey primary_key,
-                                       PasswordNote note) {
+void LoginDatabase::UpdatePasswordNotes(
+    FormPrimaryKey primary_key,
+    const std::vector<PasswordNote>& notes) {
   if (!base::FeatureList::IsEnabled(features::kPasswordNotes))
     return;
-  if (note.value.empty()) {
-    password_notes_table_.RemovePasswordNote(primary_key);
+  if (notes.empty() || notes[0].value.empty()) {
+    password_notes_table_.RemovePasswordNotes(primary_key);
     return;
   }
-  password_notes_table_.InsertOrReplace(primary_key, note);
+  for (const PasswordNote& note : notes)
+    password_notes_table_.InsertOrReplace(primary_key, note);
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index 0e7b38b..3ea08ca 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -329,15 +329,15 @@
       FormPrimaryKey primary_key,
       const base::flat_map<InsecureType, InsecurityMetadata>& password_issues);
 
-  // Reads the `password_notes` table for the note with `primary_key` and fills
-  // the `form->note` field. If there are no notes for `primary_key`, the form
-  // is set to empty note.
-  void PopulateFormWithNote(FormPrimaryKey primary_key,
-                            PasswordForm* form) const;
+  // Reads the `password_notes` table for the notes with `primary_key` and fills
+  // the `form->notes` field. If there are no notes for `primary_key`, the form
+  // is set to empty notes.
+  void PopulateFormWithNotes(FormPrimaryKey primary_key,
+                             PasswordForm* form) const;
 
-  // Updates the `password_notes` table if `note.value` changed for
-  // `primary_key`.
-  void UpdatePasswordNote(FormPrimaryKey primary_key, PasswordNote note);
+  // Updates the `password_notes` table if `notes` changed for `primary_key`.
+  void UpdatePasswordNotes(FormPrimaryKey primary_key,
+                           const std::vector<PasswordNote>& notes);
 
   const base::FilePath db_path_;
   const IsAccountStore is_account_store_;
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index d91d069c..8513367 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -2360,7 +2360,7 @@
                              /* should_PSL_matching_apply */ true, &results));
 
   PasswordForm expected_form = form;
-  expected_form.note = note;
+  expected_form.notes = {note};
   EXPECT_THAT(results, UnorderedElementsAre(Pointee(expected_form)));
 }
 
@@ -2370,11 +2370,11 @@
 
   PasswordForm form = GenerateExamplePasswordForm();
   PasswordNote note(u"example note", base::Time::Now());
-  form.note = note;
+  form.notes = {note};
 
   std::ignore = db().AddLogin(form);
 
-  EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
+  EXPECT_EQ(db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1))[0],
             note);
 }
 
@@ -2383,12 +2383,12 @@
   feature_list.InitAndEnableFeature(features::kPasswordNotes);
 
   PasswordForm form = GenerateExamplePasswordForm();
-  form.note = PasswordNote(std::u16string(), base::Time::Now());
+  form.notes = {PasswordNote(std::u16string(), base::Time::Now())};
 
   std::ignore = db().AddLogin(form);
 
-  EXPECT_THAT(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
-              absl::nullopt);
+  EXPECT_TRUE(
+      db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1)).empty());
 }
 
 TEST_F(LoginDatabaseTest, UpdateLoginWithEmptyNoteDeletesExistingNote) {
@@ -2397,17 +2397,17 @@
 
   PasswordForm form = GenerateExamplePasswordForm();
   PasswordNote note = PasswordNote(u"example note", base::Time::Now());
-  form.note = note;
+  form.notes = {note};
 
   std::ignore = db().AddLogin(form);
-  EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
+  EXPECT_EQ(db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1))[0],
             note);
 
-  form.note = PasswordNote(u"", base::Time::Now());
+  form.notes = {PasswordNote(u"", base::Time::Now())};
   std::ignore = db().UpdateLogin(form);
 
-  EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
-            absl::nullopt);
+  EXPECT_TRUE(
+      db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1)).empty());
 }
 
 TEST_F(LoginDatabaseTest, RemoveLoginRemovesNoteAttachedToTheLogin) {
@@ -2416,16 +2416,16 @@
 
   PasswordForm form = GenerateExamplePasswordForm();
   PasswordNote note = PasswordNote(u"example note", base::Time::Now());
-  form.note = note;
+  form.notes = {note};
   std::ignore = db().AddLogin(form);
 
-  EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
+  EXPECT_EQ(db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1))[0],
             note);
 
   PasswordStoreChangeList list;
   EXPECT_TRUE(db().RemoveLogin(form, &list));
-  EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
-            absl::nullopt);
+  EXPECT_TRUE(
+      db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1)).empty());
 }
 
 TEST_F(LoginDatabaseTest, RemovingLoginRemovesInsecureCredentials) {
diff --git a/components/password_manager/core/browser/password_form.cc b/components/password_manager/core/browser/password_form.cc
index cbf5715..9ec2389 100644
--- a/components/password_manager/core/browser/password_form.cc
+++ b/components/password_manager/core/browser/password_form.cc
@@ -189,10 +189,17 @@
 
   target->SetKey("password_issues ", base::Value(password_issues));
 
-  base::Value note_value(base::Value::Type::DICTIONARY);
-  note_value.SetStringKey("note_value", form.note.value);
-  note_value.SetKey("date_created", base::TimeToValue(form.note.date_created));
-  target->SetKey("note", std::move(note_value));
+  std::vector<base::Value> password_notes;
+  password_notes.reserve(form.notes.size());
+  for (const auto& note : form.notes) {
+    base::Value note_value(base::Value::Type::DICTIONARY);
+    note_value.SetStringKey("unique_display_name", note.unique_display_name);
+    note_value.SetStringKey("value", note.value);
+    note_value.SetKey("date_created", base::TimeToValue(note.date_created));
+    note_value.SetBoolKey("hide_by_default", note.hide_by_default);
+    password_notes.push_back(std::move(note_value));
+  }
+  target->SetKey("notes", base::Value(password_notes));
 
   target->SetStringKey("previously_associated_sync_account_email",
                        form.previously_associated_sync_account_email);
@@ -215,6 +222,15 @@
 PasswordNote::PasswordNote(std::u16string value, base::Time date_created)
     : value(std::move(value)), date_created(std::move(date_created)) {}
 
+PasswordNote::PasswordNote(std::u16string unique_display_name,
+                           std::u16string value,
+                           base::Time date_created,
+                           bool hide_by_default)
+    : unique_display_name(std::move(unique_display_name)),
+      value(std::move(value)),
+      date_created(date_created),
+      hide_by_default(hide_by_default) {}
+
 PasswordNote::PasswordNote(const PasswordNote& rhs) = default;
 
 PasswordNote::PasswordNote(PasswordNote&& rhs) = default;
@@ -226,7 +242,9 @@
 PasswordNote::~PasswordNote() = default;
 
 bool operator==(const PasswordNote& lhs, const PasswordNote& rhs) {
-  return lhs.value == rhs.value && lhs.date_created == rhs.date_created;
+  return lhs.unique_display_name == rhs.unique_display_name &&
+         lhs.value == rhs.value && lhs.date_created == rhs.date_created &&
+         lhs.hide_by_default == rhs.hide_by_default;
 }
 
 PasswordForm::PasswordForm() = default;
@@ -337,7 +355,7 @@
          lhs.is_new_password_reliable == rhs.is_new_password_reliable &&
          lhs.in_store == rhs.in_store &&
          lhs.moving_blocked_for_list == rhs.moving_blocked_for_list &&
-         lhs.password_issues == rhs.password_issues && lhs.note == rhs.note &&
+         lhs.password_issues == rhs.password_issues && lhs.notes == rhs.notes &&
          lhs.previously_associated_sync_account_email ==
              rhs.previously_associated_sync_account_email;
 }
diff --git a/components/password_manager/core/browser/password_form.h b/components/password_manager/core/browser/password_form.h
index 160f298..f158edc 100644
--- a/components/password_manager/core/browser/password_form.h
+++ b/components/password_manager/core/browser/password_form.h
@@ -66,16 +66,26 @@
 struct PasswordNote {
   PasswordNote();
   PasswordNote(std::u16string value, base::Time date_created);
+  PasswordNote(std::u16string unique_display_name,
+               std::u16string value,
+               base::Time date_created,
+               bool hide_by_default);
   PasswordNote(const PasswordNote& rhs);
   PasswordNote(PasswordNote&& rhs);
   PasswordNote& operator=(const PasswordNote& rhs);
   PasswordNote& operator=(PasswordNote&& rhs);
   ~PasswordNote();
 
+  // The name displayed in the UI labeling this note. Currently unused and added
+  // for future compatibility.
+  std::u16string unique_display_name;
   // The value of the note.
   std::u16string value;
   // The date when the note was created.
   base::Time date_created;
+  // Whether the value of the note will be hidden by default in the UI similar
+  // to password values. Currently unused and added for future compatibility.
+  bool hide_by_default = false;
 };
 
 bool operator==(const PasswordNote& lhs, const PasswordNote& rhs);
@@ -395,8 +405,8 @@
   // to its metadata (e.g. time it was discovered, whether alerts are muted).
   base::flat_map<InsecureType, InsecurityMetadata> password_issues;
 
-  // Attached note to the credential.
-  PasswordNote note;
+  // Attached notes to the credential.
+  std::vector<PasswordNote> notes;
 
   // Email address of the last sync account this password was associated with.
   // This field is non empty only if the password is NOT currently associated
diff --git a/components/password_manager/core/browser/password_notes_table.cc b/components/password_manager/core/browser/password_notes_table.cc
index c607284..e7b3615f 100644
--- a/components/password_manager/core/browser/password_notes_table.cc
+++ b/components/password_manager/core/browser/password_notes_table.cc
@@ -22,24 +22,27 @@
 namespace password_manager {
 namespace {
 
-// Helper function to return a password note map from the SQL statement.
-std::map<FormPrimaryKey, PasswordNote> StatementToPasswordNotes(
+// Helper function to return a password notes map from the SQL statement.
+std::map<FormPrimaryKey, std::vector<PasswordNote>> StatementToPasswordNotes(
     sql::Statement* s) {
-  std::map<FormPrimaryKey, PasswordNote> results;
+  std::map<FormPrimaryKey, std::vector<PasswordNote>> results;
   while (s->Step()) {
+    std::u16string unique_display_name = s->ColumnString16(1);
     std::string encrypted_value;
-    s->ColumnBlobAsString(1, &encrypted_value);
+    s->ColumnBlobAsString(2, &encrypted_value);
     std::u16string decrypted_value;
     if (LoginDatabase::DecryptedString(encrypted_value, &decrypted_value) !=
         LoginDatabase::ENCRYPTION_RESULT_SUCCESS) {
       continue;
     }
     base::Time date_created = base::Time::FromDeltaSinceWindowsEpoch(
-        base::Microseconds(s->ColumnInt64(2)));
+        base::Microseconds(s->ColumnInt64(3)));
+    bool hide_by_default = s->ColumnBool(4);
 
-    results.emplace(
-        FormPrimaryKey(s->ColumnInt(0)),
-        PasswordNote(std::move(decrypted_value), std::move(date_created)));
+    std::vector<PasswordNote>& notes = results[FormPrimaryKey(s->ColumnInt(0))];
+    notes.emplace_back(std::move(unique_display_name),
+                       std::move(decrypted_value), date_created,
+                       hide_by_default);
   }
   return results;
 }
@@ -64,20 +67,20 @@
   sql::Statement s(db_->GetCachedStatement(
       SQL_FROM_HERE,
       base::StringPrintf("INSERT OR REPLACE INTO %s (parent_id, key, value, "
-                         "date_created) VALUES (?, ?, ?, ?)",
+                         "date_created, confidential) VALUES (?, ?, ?, ?, ?)",
                          kTableName)
           .c_str()));
 
   s.BindInt(0, parent_id.value());
-  // Key column is not used but added for future compatibility.
-  s.BindString(1, "");
+  s.BindString16(1, note.unique_display_name);
   s.BindString(2, encrypted_value);
   s.BindInt64(3, note.date_created.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  s.BindBool(4, note.hide_by_default);
 
   return s.Run() && db_->GetLastChangeCount();
 }
 
-bool PasswordNotesTable::RemovePasswordNote(FormPrimaryKey parent_id) {
+bool PasswordNotesTable::RemovePasswordNotes(FormPrimaryKey parent_id) {
   DCHECK(db_);
   sql::Statement s(db_->GetCachedStatement(
       SQL_FROM_HERE,
@@ -88,27 +91,30 @@
   return s.Run() && db_->GetLastChangeCount();
 }
 
-absl::optional<PasswordNote> PasswordNotesTable::GetPasswordNote(
+std::vector<PasswordNote> PasswordNotesTable::GetPasswordNotes(
     FormPrimaryKey parent_id) const {
   DCHECK(db_);
   sql::Statement s(db_->GetCachedStatement(
-      SQL_FROM_HERE, base::StringPrintf("SELECT parent_id, value, date_created "
-                                        "FROM %s WHERE parent_id = ? ",
-                                        kTableName)
-                         .c_str()));
+      SQL_FROM_HERE,
+      base::StringPrintf(
+          "SELECT parent_id, key, value, date_created, confidential "
+          "FROM %s WHERE parent_id = ? ",
+          kTableName)
+          .c_str()));
   s.BindInt(0, parent_id.value());
-  auto notes = StatementToPasswordNotes(&s);
-  return notes.empty() ? absl::optional<PasswordNote>() : notes[parent_id];
+  return StatementToPasswordNotes(&s)[parent_id];
 }
 
-std::map<FormPrimaryKey, PasswordNote>
+std::map<FormPrimaryKey, std::vector<PasswordNote>>
 PasswordNotesTable::GetAllPasswordNotesForTest() const {
   DCHECK(db_);
   sql::Statement s(db_->GetCachedStatement(
-      SQL_FROM_HERE, base::StringPrintf("SELECT parent_id, value, date_created "
-                                        "FROM %s",
-                                        kTableName)
-                         .c_str()));
+      SQL_FROM_HERE,
+      base::StringPrintf(
+          "SELECT parent_id, key, value, date_created, confidential "
+          "FROM %s",
+          kTableName)
+          .c_str()));
   return StatementToPasswordNotes(&s);
 }
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_notes_table.h b/components/password_manager/core/browser/password_notes_table.h
index ce7e9427..e02a0ea2 100644
--- a/components/password_manager/core/browser/password_notes_table.h
+++ b/components/password_manager/core/browser/password_notes_table.h
@@ -34,16 +34,16 @@
 
   // Adds the note if it doesn't exist.
   // If it does, it removes the previous entry and adds the new one.
-  // Note that it sets the key column as empty string.
   bool InsertOrReplace(FormPrimaryKey parent_id, const PasswordNote& note);
 
-  // Removes the note corresponding to `parent_id`.
-  bool RemovePasswordNote(FormPrimaryKey parent_id);
+  // Removes the notes corresponding to `parent_id`.
+  bool RemovePasswordNotes(FormPrimaryKey parent_id);
 
-  // Gets the note in the database for `parent_id`.
-  absl::optional<PasswordNote> GetPasswordNote(FormPrimaryKey parent_id) const;
+  // Gets the notes in the database for `parent_id`.
+  std::vector<PasswordNote> GetPasswordNotes(FormPrimaryKey parent_id) const;
 
-  std::map<FormPrimaryKey, PasswordNote> GetAllPasswordNotesForTest() const;
+  std::map<FormPrimaryKey, std::vector<PasswordNote>>
+  GetAllPasswordNotesForTest() const;
 
  private:
   sql::Database* db_ = nullptr;
diff --git a/components/password_manager/core/browser/password_notes_table_unittest.cc b/components/password_manager/core/browser/password_notes_table_unittest.cc
index 7d1474d..8fd3cea 100644
--- a/components/password_manager/core/browser/password_notes_table_unittest.cc
+++ b/components/password_manager/core/browser/password_notes_table_unittest.cc
@@ -38,6 +38,8 @@
 using testing::SizeIs;
 using testing::UnorderedElementsAre;
 
+// TODO(crbug.com/1326554): Update the tests in this file to cover
+// reading/writing of fields other than the note value.
 class PasswordNotesTableTest : public testing::Test {
  protected:
   void SetUp() override {
@@ -75,12 +77,14 @@
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kSecondNote));
 
-  EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kSecondNote);
+  EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+              ElementsAre(kSecondNote));
   EXPECT_THAT(table()->GetAllPasswordNotesForTest(), SizeIs(1));
 
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kThirdNote));
 
-  EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kThirdNote);
+  EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+              ElementsAre(kThirdNote));
   EXPECT_THAT(table()->GetAllPasswordNotesForTest(), SizeIs(1));
 }
 
@@ -93,26 +97,31 @@
   ReloadDatabase();
 
   EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
-              ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote),
-                          std::make_pair(FormPrimaryKey(2), kSecondNote)));
+              UnorderedElementsAre(
+                  std::make_pair(FormPrimaryKey(1),
+                                 std::vector<PasswordNote>({kFirstNote})),
+                  std::make_pair(FormPrimaryKey(2),
+                                 std::vector<PasswordNote>({kSecondNote}))));
 }
 
-TEST_F(PasswordNotesTableTest, GetPasswordNote) {
+TEST_F(PasswordNotesTableTest, GetPasswordNotes) {
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user2")), SizeIs(1));
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(2), kSecondNote));
 
-  EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kFirstNote);
+  EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+              ElementsAre(kFirstNote));
 
-  EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(2)), kSecondNote);
+  EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(2)),
+              ElementsAre(kSecondNote));
 }
 
-TEST_F(PasswordNotesTableTest, GetPasswordNoteWhenParentIdDoesntExist) {
-  EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(2)), absl::nullopt);
+TEST_F(PasswordNotesTableTest, GetPasswordNotesWhenParentIdDoesntExist) {
+  EXPECT_TRUE(table()->GetPasswordNotes(FormPrimaryKey(2)).empty());
 }
 
-TEST_F(PasswordNotesTableTest, RemovePasswordNote) {
+TEST_F(PasswordNotesTableTest, RemovePasswordNotes) {
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user2")), SizeIs(1));
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user3")), SizeIs(1));
@@ -120,20 +129,24 @@
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(2), kSecondNote));
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(3), kThirdNote));
 
-  EXPECT_TRUE(table()->RemovePasswordNote(FormPrimaryKey(2)));
+  EXPECT_TRUE(table()->RemovePasswordNotes(FormPrimaryKey(2)));
 
-  EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
-              ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote),
-                          std::make_pair(FormPrimaryKey(3), kThirdNote)));
+  EXPECT_THAT(
+      table()->GetAllPasswordNotesForTest(),
+      ElementsAre(std::make_pair(FormPrimaryKey(1),
+                                 std::vector<PasswordNote>({kFirstNote})),
+                  std::make_pair(FormPrimaryKey(3),
+                                 std::vector<PasswordNote>({kThirdNote}))));
 }
 
-TEST_F(PasswordNotesTableTest, RemovePasswordNoteWithNonExistingKey) {
+TEST_F(PasswordNotesTableTest, RemovePasswordNotesWithNonExistingKey) {
   EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
   EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
 
-  EXPECT_FALSE(table()->RemovePasswordNote(FormPrimaryKey(1000)));
+  EXPECT_FALSE(table()->RemovePasswordNotes(FormPrimaryKey(1000)));
   EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
-              ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote)));
+              ElementsAre(std::make_pair(
+                  FormPrimaryKey(1), std::vector<PasswordNote>({kFirstNote}))));
 }
 
 }  // namespace
diff --git a/components/password_manager/core/browser/ui/saved_passwords_presenter.cc b/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
index 1622882..66c33f5 100644
--- a/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
+++ b/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
@@ -170,14 +170,16 @@
     return false;
   IsUsernameChanged username_changed(new_username != forms[0].username_value);
   IsPasswordChanged password_changed(new_password != forms[0].password_value);
-  IsPasswordNoteChanged note_changed =
-      IsPasswordNoteChanged(forms[0].note.value != new_note);
+  IsPasswordNoteChanged note_changed = IsPasswordNoteChanged(
+      (forms[0].notes.empty() && !new_note.empty()) ||
+      (!forms[0].notes.empty() && forms[0].notes[0].value != new_note));
 
   if (new_password.empty())
     return false;
   if (username_changed &&
-      IsUsernameAlreadyUsed(passwords_, forms, new_username))
+      IsUsernameAlreadyUsed(passwords_, forms, new_username)) {
     return false;
+  }
 
   // An updated username implies a change in the primary key, thus we need to
   // make sure to call the right API. Update every entry in the equivalence
@@ -195,10 +197,15 @@
 
       if (note_changed) {
         // if the old note is empty, the note is just created.
-        if (old_form.note.value.empty()) {
-          new_form.note.date_created = base::Time::Now();
+        if (old_form.notes.empty()) {
+          new_form.notes.emplace_back(new_note,
+                                      /*date_created=*/base::Time::Now());
+        } else {
+          if (old_form.notes[0].value.empty()) {
+            new_form.notes[0].date_created = base::Time::Now();
+          }
+          new_form.notes[0].value = new_note;
         }
-        new_form.note.value = new_note;
       }
 
       if (username_changed) {
diff --git a/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc b/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
index ab09bfe..2933a52 100644
--- a/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
+++ b/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
@@ -393,7 +393,8 @@
   RunUntilIdle();
 
   PasswordForm expected_updated_form = form;
-  expected_updated_form.note = PasswordNote(kNewNoteValue, base::Time::Now());
+  expected_updated_form.notes = {
+      PasswordNote(kNewNoteValue, base::Time::Now())};
   EXPECT_THAT(
       store().stored_passwords(),
       ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
@@ -418,7 +419,8 @@
   RunUntilIdle();
 
   PasswordForm expected_updated_form = form;
-  expected_updated_form.note = PasswordNote(kNewNoteValue, base::Time::Now());
+  expected_updated_form.notes = {
+      PasswordNote(kNewNoteValue, base::Time::Now())};
   EXPECT_THAT(
       store().stored_passwords(),
       ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
@@ -429,7 +431,7 @@
       PasswordNote(u"existing note", base::Time::Now());
   PasswordForm form =
       CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
-  form.note = kExistingNote;
+  form.notes = {kExistingNote};
 
   store().AddLogin(form);
   RunUntilIdle();
@@ -442,7 +444,7 @@
   RunUntilIdle();
 
   PasswordForm expected_updated_form = form;
-  expected_updated_form.note.value = kNewNoteValue;
+  expected_updated_form.notes[0].value = kNewNoteValue;
   EXPECT_THAT(
       store().stored_passwords(),
       ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
@@ -451,7 +453,7 @@
 TEST_F(SavedPasswordsPresenterTest, EditNoteAsEmpty) {
   PasswordForm form =
       CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
-  form.note = PasswordNote(u"existing note", base::Time::Now());
+  form.notes = {PasswordNote(u"existing note", base::Time::Now())};
   std::vector<PasswordForm> forms = {form};
 
   store().AddLogin(form);
@@ -462,7 +464,7 @@
   RunUntilIdle();
 
   PasswordForm expected_updated_form = form;
-  expected_updated_form.note.value = u"";
+  expected_updated_form.notes[0].value = u"";
   EXPECT_THAT(
       store().stored_passwords(),
       ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index 93ee6d42..ec0a664 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -126,8 +126,6 @@
     "features.h",
     "json_schema_constants.cc",
     "json_schema_constants.h",
-    "legacy_chrome_policy_migrator.cc",
-    "legacy_chrome_policy_migrator.h",
     "management/management_service.cc",
     "management/management_service.h",
     "management/platform_management_service.cc",
@@ -468,7 +466,6 @@
     "cloud/user_info_fetcher_unittest.cc",
     "command_line_policy_provider_unittest.cc",
     "generate_policy_source_unittest.cc",
-    "legacy_chrome_policy_migrator_unittest.cc",
     "management/management_service_unittest.cc",
     "policy_bundle_unittest.cc",
     "policy_loader_command_line_unittest.cc",
diff --git a/components/policy/core/common/legacy_chrome_policy_migrator.cc b/components/policy/core/common/legacy_chrome_policy_migrator.cc
deleted file mode 100644
index bf3a1f0..0000000
--- a/components/policy/core/common/legacy_chrome_policy_migrator.cc
+++ /dev/null
@@ -1,34 +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 "components/policy/core/common/legacy_chrome_policy_migrator.h"
-
-#include <string>
-
-#include "components/policy/core/common/policy_bundle.h"
-#include "components/policy/core/common/policy_map.h"
-#include "components/policy/core/common/policy_namespace.h"
-
-namespace policy {
-
-LegacyChromePolicyMigrator::LegacyChromePolicyMigrator(const char* old_name,
-                                                       const char* new_name)
-    : migration_(old_name, new_name) {}
-
-LegacyChromePolicyMigrator::LegacyChromePolicyMigrator(
-    const char* old_name,
-    const char* new_name,
-    Migration::ValueTransform transform)
-    : migration_(old_name, new_name, transform) {}
-
-LegacyChromePolicyMigrator::~LegacyChromePolicyMigrator() = default;
-
-void LegacyChromePolicyMigrator::Migrate(policy::PolicyBundle* bundle) {
-  policy::PolicyMap& chrome_map =
-      bundle->Get(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, ""));
-
-  CopyPolicyIfUnset(chrome_map, &chrome_map, migration_);
-}
-
-}  // namespace policy
diff --git a/components/policy/core/common/legacy_chrome_policy_migrator.h b/components/policy/core/common/legacy_chrome_policy_migrator.h
deleted file mode 100644
index 0012b64..0000000
--- a/components/policy/core/common/legacy_chrome_policy_migrator.h
+++ /dev/null
@@ -1,40 +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 COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
-#define COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
-
-#include "components/policy/core/common/policy_migrator.h"
-
-namespace policy {
-
-// LegacyChromePolicyMigrator migrates a deprecated Chrome domain policy to a
-// new name, setting up the new policy based on the old one.
-//
-// This is intended to be used for policies that do not have a corresponding
-// pref. If the policy has a pref, please use
-// |LegacyPoliciesDeprecatingPolicyHandler| instead.
-class POLICY_EXPORT LegacyChromePolicyMigrator : public PolicyMigrator {
- public:
-  using Migration = PolicyMigrator::Migration;
-
-  LegacyChromePolicyMigrator(const char* old_name, const char* new_name);
-  LegacyChromePolicyMigrator(const char* old_name,
-                             const char* new_name,
-                             Migration::ValueTransform transform);
-  ~LegacyChromePolicyMigrator() override;
-
-  LegacyChromePolicyMigrator(const LegacyChromePolicyMigrator&) = delete;
-  LegacyChromePolicyMigrator& operator=(const LegacyChromePolicyMigrator&) =
-      delete;
-
-  void Migrate(policy::PolicyBundle* bundle) override;
-
- private:
-  Migration migration_;
-};
-
-}  // namespace policy
-
-#endif  // COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
diff --git a/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc b/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc
deleted file mode 100644
index 3259f501..0000000
--- a/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc
+++ /dev/null
@@ -1,124 +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 "components/policy/core/common/legacy_chrome_policy_migrator.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace policy {
-
-namespace {
-const char kOldPolicy[] = "OldPolicy";
-const char kNewPolicy[] = "NewPolicy";
-const char kOtherPolicy[] = "OtherPolicy";
-
-const int kOldValue = 111;
-const int kNewValue = 222;
-const int kTransformedValue = 333;
-const int kOtherValue = 999;
-
-void MultiplyByThree(base::Value* val) {
-  *val = base::Value(val->GetInt() * 3);
-}
-
-void SetPolicy(PolicyMap* policy, const char* policy_name, base::Value value) {
-  policy->Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-              POLICY_SOURCE_CLOUD, std::move(value), nullptr);
-}
-
-}  // namespace
-
-TEST(LegacyChromePolicyMigratorTest, CopyPolicyIfUnset) {
-  PolicyBundle bundle;
-
-  PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
-  SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
-  SetPolicy(&chrome_map, kOtherPolicy, base::Value(kOtherValue));
-
-  LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy);
-
-  migrator.Migrate(&bundle);
-
-  // kOldPolicy should have been copied to kNewPolicy, kOtherPolicy remains
-  EXPECT_EQ(3u, chrome_map.size());
-  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-  // Old Value should be copied over.
-  EXPECT_EQ(base::Value(kOldValue),
-            *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-  // Other Value should be unchanged.
-  EXPECT_EQ(base::Value(kOtherValue),
-            *chrome_map.GetValue(kOtherPolicy, base::Value::Type::INTEGER));
-  base::RepeatingCallback<std::u16string(int)> l10nlookup =
-      base::BindRepeating(&l10n_util::GetStringUTF16);
-  // Old policy should always be marked deprecated
-  EXPECT_FALSE(
-      chrome_map.Get(kOldPolicy)
-          ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
-          .empty());
-  EXPECT_FALSE(
-      chrome_map.Get(kNewPolicy)
-          ->GetLocalizedMessages(PolicyMap::MessageType::kWarning, l10nlookup)
-          .empty());
-}
-
-TEST(LegacyChromePolicyMigratorTest, TransformPolicy) {
-  PolicyBundle bundle;
-
-  PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
-  SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
-
-  LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy,
-                                      base::BindRepeating(&MultiplyByThree));
-
-  migrator.Migrate(&bundle);
-
-  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-  // Old Value should be transformed
-  EXPECT_EQ(base::Value(kTransformedValue),
-            *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-}
-
-TEST(LegacyChromePolicyMigratorTest, IgnoreOldIfNewIsSet) {
-  PolicyBundle bundle;
-
-  PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
-  SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
-  SetPolicy(&chrome_map, kNewPolicy, base::Value(kNewValue));
-
-  LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy);
-
-  migrator.Migrate(&bundle);
-  // New Value is unchanged
-  EXPECT_EQ(base::Value(kNewValue),
-            *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-  // Should be no warning on new policy
-  base::RepeatingCallback<std::u16string(int)> l10nlookup =
-      base::BindRepeating(&l10n_util::GetStringUTF16);
-  // Old policy should always be marked deprecated
-  EXPECT_FALSE(
-      chrome_map.Get(kOldPolicy)
-          ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
-          .empty());
-  // No warnings on new policy because it was unchanged.
-  EXPECT_TRUE(
-      chrome_map.Get(kNewPolicy)
-          ->GetLocalizedMessages(PolicyMap::MessageType::kWarning, l10nlookup)
-          .empty());
-  EXPECT_TRUE(
-      chrome_map.Get(kNewPolicy)
-          ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
-          .empty());
-}
-
-}  // namespace policy
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index 5523d57..8ccddc8 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -74,6 +74,10 @@
 #endif
 };
 
+const base::Feature kEnhancedProtectionPhase2IOS{
+    "SafeBrowsingEnhancedProtectionPhase2IOS",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kExtensionTelemetry{"SafeBrowsingExtensionTelemetry",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -164,6 +168,7 @@
     {&kDelayedWarnings, true},
     {&kDownloadBubble, true},
     {&kEnhancedProtection, true},
+    {&kEnhancedProtectionPhase2IOS, true},
     {&kExtensionTelemetry, true},
     {&kExtensionTelemetryReportContactedHosts, true},
     {&kExtensionTelemetryPersistence, true},
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h
index d5cd51f..1e703e92 100644
--- a/components/safe_browsing/core/common/features.h
+++ b/components/safe_browsing/core/common/features.h
@@ -65,6 +65,9 @@
 // Enables Enhanced Safe Browsing.
 extern const base::Feature kEnhancedProtection;
 
+// Phase 2 of Enhanced Safe Browsing changes.
+extern const base::Feature kEnhancedProtectionPhase2IOS;
+
 // Enables collection of signals related to extension activity and uploads
 // of telemetry reports to SB servers.
 extern const base::Feature kExtensionTelemetry;
diff --git a/components/sync/protocol/password_specifics.proto b/components/sync/protocol/password_specifics.proto
index 76fc901..dafc0d2 100644
--- a/components/sync/protocol/password_specifics.proto
+++ b/components/sync/protocol/password_specifics.proto
@@ -157,17 +157,15 @@
 
   message Notes {
     message Note {
-      // The id of the note. The id must be unique per password.
-      optional int64 id = 1;
       // The display name must be unique within the scope of a password.
-      optional string unique_display_name = 2;
+      optional string unique_display_name = 1;
       // The user-defined value of the note.
-      optional string value = 3;
+      optional string value = 2;
       // The creation time of the note. Number of microseconds since 1601.
-      optional int64 date_created_windows_epoch_micros = 4;
+      optional int64 date_created_windows_epoch_micros = 3;
       // Whether the value of the note is not displayed in plain text by
       // default.
-      optional bool hide_by_default = 5;
+      optional bool hide_by_default = 4;
     }
     repeated Note note = 1;
   }
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index ce9ee62..07dc35d 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -781,7 +781,6 @@
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::PasswordSpecificsData_Notes_Note& proto) {
-  VISIT(id);
   VISIT(unique_display_name);
   VISIT(value);
   VISIT(date_created_windows_epoch_micros);
diff --git a/components/url_param_filter/DEPS b/components/url_param_filter/DEPS
new file mode 100644
index 0000000..69341f5
--- /dev/null
+++ b/components/url_param_filter/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+  "+base",
+  "+net",
+  "+ui",
+  "+testing",
+  # UrlParamFilter is a layered component; subdirectories must explicitly
+  # introduce the ability to use the content layer as appropriate.
+  "-components/url_param_filter/content",
+]
diff --git a/components/url_param_filter/OWNERS b/components/url_param_filter/OWNERS
new file mode 100644
index 0000000..17e7e3b
--- /dev/null
+++ b/components/url_param_filter/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/url_param_filter/OWNERS
diff --git a/components/url_param_filter/README.md b/components/url_param_filter/README.md
new file mode 100644
index 0000000..8e681b5d
--- /dev/null
+++ b/components/url_param_filter/README.md
@@ -0,0 +1,11 @@
+This is a layered component that allows embedders to filter urls.
+
+Directory structure:
+
+**content/** -
+contains logic that relies on //content for
+use on platforms that embed //content (e.g. Desktop, Android).
+
+
+**core/** - contains logic that can be shared across
+content-embedding platforms and others (e.g. Desktop, Android, iOS).
diff --git a/components/url_param_filter/content/BUILD.gn b/components/url_param_filter/content/BUILD.gn
new file mode 100644
index 0000000..d6edaab1
--- /dev/null
+++ b/components/url_param_filter/content/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("content") {
+  if (is_component_build) {
+    check_includes = false
+  }
+  sources = [
+    "cross_otr_observer.cc",
+    "cross_otr_observer.h",
+    "url_param_filter_throttle.cc",
+    "url_param_filter_throttle.h",
+  ]
+  deps = [ "//components/url_param_filter/core" ]
+  public_deps = [
+    "//base",
+    "//content/public/browser",
+    "//content/public/common",
+    "//net",
+    "//services/network/public/cpp",
+    "//third_party/abseil-cpp:absl",
+    "//third_party/blink/public/common",
+    "//ui/base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "cross_otr_observer_unittest.cc",
+    "url_param_filter_throttle_unittest.cc",
+  ]
+  deps = [
+    ":content",
+    "//base/test:test_support",
+    "//components/url_param_filter/core",
+    "//components/url_param_filter/core:test_support",
+    "//components/url_param_filter/core:url_param_filter_classification_proto",
+  ]
+  public_deps = [
+    "//content/test:test_support",
+    "//services/network/public/mojom",
+  ]
+}
diff --git a/components/url_param_filter/content/DEPS b/components/url_param_filter/content/DEPS
new file mode 100644
index 0000000..6068e46
--- /dev/null
+++ b/components/url_param_filter/content/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+  "+components/url_param_filter/core",
+  "+content/public/browser",
+  "+content/public/common",
+  "+content/public/test",
+  "+services/network/public/cpp",
+  "+services/network/public/mojom",
+  "+third_party/blink/public/common/loader",
+]
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.cc b/components/url_param_filter/content/cross_otr_observer.cc
similarity index 95%
rename from chrome/browser/url_param_filter/cross_otr_observer.cc
rename to components/url_param_filter/content/cross_otr_observer.cc
index 402c8bbe..d825693 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer.cc
+++ b/components/url_param_filter/content/cross_otr_observer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
 
 #include <memory>
 
@@ -35,11 +35,11 @@
 
 void CrossOtrObserver::MaybeCreateForWebContents(
     content::WebContents* web_contents,
-    const NavigateParams& params) {
-  if (params.privacy_sensitivity ==
-          NavigateParams::PrivacySensitivity::CROSS_OTR &&
-      params.started_from_context_menu &&
-      !ui::PageTransitionCoreTypeIs(params.transition,
+    bool privacy_sensitivity_is_cross_otr,
+    bool started_from_context_menu,
+    ui::PageTransition transition) {
+  if (privacy_sensitivity_is_cross_otr && started_from_context_menu &&
+      !ui::PageTransitionCoreTypeIs(transition,
                                     ui::PAGE_TRANSITION_AUTO_BOOKMARK)) {
     // Inherited from WebContentsUserData and checks for an already-attached
     // instance internally.
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.h b/components/url_param_filter/content/cross_otr_observer.h
similarity index 87%
rename from chrome/browser/url_param_filter/cross_otr_observer.h
rename to components/url_param_filter/content/cross_otr_observer.h
index 7a3480e..39227c8 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer.h
+++ b/components/url_param_filter/content/cross_otr_observer.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_
-#define CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_
+#ifndef COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "ui/base/page_transition_types.h"
 
 namespace url_param_filter {
 
@@ -29,7 +29,9 @@
   // Attaches the observer in cases where it should do so; leaves `web_contents`
   // unchanged otherwise.
   static void MaybeCreateForWebContents(content::WebContents* web_contents,
-                                        const NavigateParams& params);
+                                        bool is_cross_otr,
+                                        bool started_from_context_menu,
+                                        ui::PageTransition transition);
   bool IsCrossOtrState();
   // content::WebContentsObserver:
   void DidStartNavigation(
@@ -78,4 +80,4 @@
 };
 
 }  // namespace url_param_filter
-#endif  // CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_
+#endif  // COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
diff --git a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc b/components/url_param_filter/content/cross_otr_observer_unittest.cc
similarity index 75%
rename from chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
rename to components/url_param_filter/content/cross_otr_observer_unittest.cc
index 98de0c4..aca1e08 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
+++ b/components/url_param_filter/content/cross_otr_observer_unittest.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
 
-#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "net/http/http_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -25,7 +24,7 @@
 constexpr char kCrossOtrRefreshCountMetricName[] =
     "Navigation.CrossOtr.ContextMenu.RefreshCountExperimental";
 
-class CrossOtrObserverTest : public ChromeRenderViewHostTestHarness {
+class CrossOtrObserverTest : public content::RenderViewHostTestHarness {
  public:
   CrossOtrObserverTest() = default;
 
@@ -36,76 +35,58 @@
 };
 
 TEST_F(CrossOtrObserverTest, NotContextMenuInitiated) {
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/web_contents(), /*is_cross_otr=*/false,
+      /*started_from_context_menu=*/false,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
 
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::DEFAULT;
-  params.started_from_context_menu = false;
-
-  std::unique_ptr<content::WebContents> web_contents =
-      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
-  CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
-
-  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, DefaultSensitivity) {
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/web_contents(), /*is_cross_otr=*/false,
+      /*started_from_context_menu=*/false,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
 
-  params.started_from_context_menu = false;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::DEFAULT;
-
-  std::unique_ptr<content::WebContents> web_contents =
-      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
-  CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
-
-  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, BookmarkLink) {
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/web_contents(), /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_AUTO_BOOKMARK);
 
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
-
-  std::unique_ptr<content::WebContents> web_contents =
-      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
-  CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
-
-  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, CreateKey) {
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
 
   ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
 }
 TEST_F(CrossOtrObserverTest, DuplicateCreateKey) {
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
 
   ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
 }
 TEST_F(CrossOtrObserverTest, HandleRedirects) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -138,13 +119,11 @@
 }
 TEST_F(CrossOtrObserverTest, HandleRedirectsNoParamsFiltering) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
@@ -162,13 +141,11 @@
 }
 TEST_F(CrossOtrObserverTest, FinishedNavigation) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -192,13 +169,11 @@
 }
 TEST_F(CrossOtrObserverTest, FinishedNavigationNoParamsFiltering) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
@@ -218,13 +193,11 @@
 }
 TEST_F(CrossOtrObserverTest, BadRedirectResponse) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -240,13 +213,11 @@
 }
 TEST_F(CrossOtrObserverTest, BadNavigationResponse) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -268,13 +239,11 @@
 }
 TEST_F(CrossOtrObserverTest, RefreshedAfterNavigation) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -304,13 +273,11 @@
 }
 TEST_F(CrossOtrObserverTest, RefreshedAfterNavigationNoParamsFiltering) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   ASSERT_NE(observer, nullptr);
@@ -337,13 +304,11 @@
 }
 TEST_F(CrossOtrObserverTest, UncommittedNavigationWithRefresh) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -390,13 +355,11 @@
 TEST_F(CrossOtrObserverTest,
        UncommittedNavigationWithRefreshNoParamsFiltering) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
@@ -438,13 +401,11 @@
 }
 TEST_F(CrossOtrObserverTest, MultipleRefreshesAfterNavigation) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -488,13 +449,11 @@
 }
 TEST_F(CrossOtrObserverTest, RedirectsAfterNavigation) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
 
   // Simulate params filtering, making it okay to collect metrics.
@@ -533,13 +492,11 @@
 }
 TEST_F(CrossOtrObserverTest, RedirectsAfterNavigationNoParamsFiltering) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
@@ -574,13 +531,11 @@
 }
 TEST_F(CrossOtrObserverTest, ClientRedirectCrossOtr) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
@@ -609,13 +564,11 @@
 }
 TEST_F(CrossOtrObserverTest, ClientRedirectAfterActivationNotCrossOtr) {
   base::HistogramTester histogram_tester;
-  NavigateParams params(profile(), GURL("https://www.foo.com"),
-                        ui::PAGE_TRANSITION_LINK);
-
-  params.started_from_context_menu = true;
-  params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
-  CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+  CrossOtrObserver::MaybeCreateForWebContents(
+      /*web_contents=*/contents, /*is_cross_otr=*/true,
+      /*started_from_context_menu=*/true,
+      /*transition=*/ui::PAGE_TRANSITION_LINK);
   CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
diff --git a/chrome/browser/url_param_filter/url_param_filter_throttle.cc b/components/url_param_filter/content/url_param_filter_throttle.cc
similarity index 93%
rename from chrome/browser/url_param_filter/url_param_filter_throttle.cc
rename to components/url_param_filter/content/url_param_filter_throttle.cc
index d8e3654..e96dadea 100644
--- a/chrome/browser/url_param_filter/url_param_filter_throttle.cc
+++ b/components/url_param_filter/content/url_param_filter_throttle.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_filter_throttle.h"
+#include "components/url_param_filter/content/url_param_filter_throttle.h"
 
 #include <memory>
 
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
-#include "chrome/browser/url_param_filter/url_param_filterer.h"
-#include "chrome/common/chrome_features.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_request.h"
diff --git a/chrome/browser/url_param_filter/url_param_filter_throttle.h b/components/url_param_filter/content/url_param_filter_throttle.h
similarity index 86%
rename from chrome/browser/url_param_filter/url_param_filter_throttle.h
rename to components/url_param_filter/content/url_param_filter_throttle.h
index a83e80c..d8bf3bd 100644
--- a/chrome/browser/url_param_filter/url_param_filter_throttle.h
+++ b/components/url_param_filter/content/url_param_filter_throttle.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_THROTTLE_H_
-#define CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_THROTTLE_H_
+#ifndef COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
+#define COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
 
 #include <memory>
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
@@ -52,4 +52,4 @@
   base::WeakPtr<CrossOtrObserver> observer_;
 };
 }  // namespace url_param_filter
-#endif  // CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_THROTTLE_H_
+#endif  // COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
diff --git a/chrome/browser/url_param_filter/url_param_filter_throttle_unittest.cc b/components/url_param_filter/content/url_param_filter_throttle_unittest.cc
similarity index 93%
rename from chrome/browser/url_param_filter/url_param_filter_throttle_unittest.cc
rename to components/url_param_filter/content/url_param_filter_throttle_unittest.cc
index 90abf88..e71ab8b 100644
--- a/chrome/browser/url_param_filter/url_param_filter_throttle_unittest.cc
+++ b/components/url_param_filter/content/url_param_filter_throttle_unittest.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_filter_throttle.h"
+#include "components/url_param_filter/content/url_param_filter_throttle.h"
 
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/url_param_filter/cross_otr_observer.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
+#include "content/public/test/test_renderer_host.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,17 +19,18 @@
 namespace url_param_filter {
 
 namespace {
+constexpr static const char kHistogramName[] =
+    "Navigation.UrlParamFilter.FilteredParamCountExperimental";
+}  // namespace
 
 // Tests the UrlParamFilterThrottle, which is currently a very thin wrapper
 // around the url_param_filter::FilterUrl() function. Coverage is accordingly
 // somewhat less thorough than that seen in url_param_filterer_unittest.
-class UrlParamFilterThrottleTest : public ChromeRenderViewHostTestHarness {
+class UrlParamFilterThrottleTest : public content::RenderViewHostTestHarness {
  public:
   UrlParamFilterThrottleTest() = default;
 
  protected:
-  constexpr static const char kHistogramName[] =
-      "Navigation.UrlParamFilter.FilteredParamCountExperimental";
   std::string encoded_classification =
       CreateBase64EncodedFilterParamClassificationForTesting(
           {{"source.xyz", {"plzblock"}},
@@ -39,13 +39,10 @@
           {{"destination.xyz", {"plzblock1"}}});
 
   void CreateCrossOtrState() {
-    NavigateParams params(profile(), GURL("https://www.foo.com"),
-                          ui::PAGE_TRANSITION_LINK);
-
-    params.started_from_context_menu = true;
-    params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
     content::WebContents* contents = web_contents();
-    CrossOtrObserver::MaybeCreateForWebContents(contents, params);
+    CrossOtrObserver::MaybeCreateForWebContents(
+        contents, /*is_cross_otr=*/true, /*started_from_context_menu=*/true,
+        ui::PAGE_TRANSITION_LINK);
   }
 };
 
@@ -369,6 +366,4 @@
   EXPECT_FALSE(defer);
 }
 
-}  // namespace
-
 }  // namespace url_param_filter
diff --git a/components/url_param_filter/core/BUILD.gn b/components/url_param_filter/core/BUILD.gn
new file mode 100644
index 0000000..6e7d2d0
--- /dev/null
+++ b/components/url_param_filter/core/BUILD.gn
@@ -0,0 +1,62 @@
+# 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("//third_party/protobuf/proto_library.gni")
+
+source_set("core") {
+  sources = [
+    "features.cc",
+    "features.h",
+    "url_param_classifications_loader.cc",
+    "url_param_classifications_loader.h",
+    "url_param_filterer.cc",
+    "url_param_filterer.h",
+  ]
+  deps = [ ":url_param_filter_classification_proto" ]
+  public_deps = [
+    "//base",
+    "//net",
+    "//third_party/abseil-cpp:absl",
+    "//third_party/zlib/google:compression_utils",
+    "//url",
+  ]
+}
+
+proto_library("url_param_filter_classification_proto") {
+  sources = [ "url_param_filter_classification.proto" ]
+  proto_out_dir = "components/url_param_filter/core"
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "url_param_filter_test_helper.cc",
+    "url_param_filter_test_helper.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//skia",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/zlib/google:compression_utils",
+  ]
+
+  deps = [ ":core" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "url_param_classifications_loader_unittest.cc",
+    "url_param_filterer_unittest.cc",
+  ]
+
+  deps = [
+    ":core",
+    ":test_support",
+    ":url_param_filter_classification_proto",
+    "//base/test:test_support",
+  ]
+}
diff --git a/components/url_param_filter/core/DEPS b/components/url_param_filter/core/DEPS
new file mode 100644
index 0000000..5d33c93
--- /dev/null
+++ b/components/url_param_filter/core/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/zlib/google",
+  "+url",
+]
diff --git a/components/url_param_filter/core/features.cc b/components/url_param_filter/core/features.cc
new file mode 100644
index 0000000..af197d3
--- /dev/null
+++ b/components/url_param_filter/core/features.cc
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/url_param_filter/core/features.h"
+
+#include "base/feature_list.h"
+
+namespace url_param_filter::features {
+
+const base::Feature kIncognitoParamFilterEnabled{
+    "IncognitoParamFilterEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace url_param_filter::features
diff --git a/components/url_param_filter/core/features.h b/components/url_param_filter/core/features.h
new file mode 100644
index 0000000..860f0fd
--- /dev/null
+++ b/components/url_param_filter/core/features.h
@@ -0,0 +1,16 @@
+// 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_URL_PARAM_FILTER_CORE_FEATURES_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace url_param_filter::features {
+
+extern const base::Feature kIncognitoParamFilterEnabled;
+
+}  // namespace url_param_filter::features
+
+#endif  // COMPONENTS_URL_PARAM_FILTER_CORE_FEATURES_H_
diff --git a/chrome/browser/url_param_filter/url_param_classifications_loader.cc b/components/url_param_filter/core/url_param_classifications_loader.cc
similarity index 95%
rename from chrome/browser/url_param_filter/url_param_classifications_loader.cc
rename to components/url_param_filter/core/url_param_classifications_loader.cc
index f6de6cd..3f107f4 100644
--- a/chrome/browser/url_param_filter/url_param_classifications_loader.cc
+++ b/components/url_param_filter/core/url_param_classifications_loader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
 
 #include <string>
 #include <utility>
@@ -11,8 +11,8 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/sequence_checker.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
-#include "chrome/common/chrome_features.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/zlib/google/compression_utils.h"
 
diff --git a/chrome/browser/url_param_filter/url_param_classifications_loader.h b/components/url_param_filter/core/url_param_classifications_loader.h
similarity index 89%
rename from chrome/browser/url_param_filter/url_param_classifications_loader.h
rename to components/url_param_filter/core/url_param_classifications_loader.h
index 5a95d25..2886d9b 100644
--- a/chrome/browser/url_param_filter/url_param_classifications_loader.h
+++ b/components/url_param_filter/core/url_param_classifications_loader.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_CLASSIFICATIONS_LOADER_H_
-#define CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_CLASSIFICATIONS_LOADER_H_
+#ifndef COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
 
 #include <unordered_map>
 
 #include "base/no_destructor.h"
 #include "base/sequence_checker.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace url_param_filter {
@@ -71,4 +71,4 @@
 };
 
 }  // namespace url_param_filter
-#endif  // CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_CLASSIFICATIONS_LOADER_H_
+#endif  // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
diff --git a/chrome/browser/url_param_filter/url_param_classifications_loader_unittest.cc b/components/url_param_filter/core/url_param_classifications_loader_unittest.cc
similarity index 98%
rename from chrome/browser/url_param_filter/url_param_classifications_loader_unittest.cc
rename to components/url_param_filter/core/url_param_classifications_loader_unittest.cc
index 4d59b69..4f3526b 100644
--- a/chrome/browser/url_param_filter/url_param_classifications_loader_unittest.cc
+++ b/components/url_param_filter/core/url_param_classifications_loader_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
 
 #include <map>
 #include <string>
@@ -12,9 +12,9 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chrome/browser/url_param_filter/url_param_filter_classification.proto b/components/url_param_filter/core/url_param_filter_classification.proto
similarity index 100%
rename from chrome/browser/url_param_filter/url_param_filter_classification.proto
rename to components/url_param_filter/core/url_param_filter_classification.proto
diff --git a/chrome/browser/url_param_filter/url_param_filter_test_helper.cc b/components/url_param_filter/core/url_param_filter_test_helper.cc
similarity index 95%
rename from chrome/browser/url_param_filter/url_param_filter_test_helper.cc
rename to components/url_param_filter/core/url_param_filter_test_helper.cc
index 80e0131..7148a4db 100644
--- a/chrome/browser/url_param_filter/url_param_filter_test_helper.cc
+++ b/components/url_param_filter/core/url_param_filter_test_helper.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/browser/url_param_filter/url_param_filterer.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 
 #include "base/base64.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
 #include "third_party/zlib/google/compression_utils.h"
 
 namespace url_param_filter {
@@ -129,15 +129,15 @@
                    std::map<FilterClassification::UseCase,
                             std::vector<std::string>>>& dest_map) {
   url_param_filter::FilterClassifications classifications;
-  for (const auto& [site, params] : source_map) {
-    for (const auto& [use_case, params] : params) {
+  for (const auto& [site, param_map] : source_map) {
+    for (const auto& [use_case, params] : param_map) {
       AddClassification(classifications.add_classifications(), site,
                         FilterClassification_SiteRole_SOURCE, params,
                         {use_case});
     }
   }
-  for (const auto& [site, params] : dest_map) {
-    for (const auto& [use_case, params] : params) {
+  for (const auto& [site, param_map] : dest_map) {
+    for (const auto& [use_case, params] : param_map) {
       AddClassification(classifications.add_classifications(), site,
                         FilterClassification_SiteRole_DESTINATION, params,
                         {use_case});
diff --git a/chrome/browser/url_param_filter/url_param_filter_test_helper.h b/components/url_param_filter/core/url_param_filter_test_helper.h
similarity index 93%
rename from chrome/browser/url_param_filter/url_param_filter_test_helper.h
rename to components/url_param_filter/core/url_param_filter_test_helper.h
index 3347af8..226e1c65 100644
--- a/chrome/browser/url_param_filter/url_param_filter_test_helper.h
+++ b/components/url_param_filter/core/url_param_filter_test_helper.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_TEST_HELPER_H_
-#define CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_TEST_HELPER_H_
+#ifndef COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
 
-#include "chrome/browser/url_param_filter/url_param_filterer.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
 
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -104,4 +104,4 @@
     const std::vector<std::string>& params,
     const std::vector<FilterClassification::UseCase>& use_cases);
 }  // namespace url_param_filter
-#endif  // CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTER_TEST_HELPER_H_
+#endif  // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
diff --git a/chrome/browser/url_param_filter/url_param_filterer.cc b/components/url_param_filter/core/url_param_filterer.cc
similarity index 95%
rename from chrome/browser/url_param_filter/url_param_filterer.cc
rename to components/url_param_filter/core/url_param_filterer.cc
index 9c4e096..a27f61a 100644
--- a/chrome/browser/url_param_filter/url_param_filterer.cc
+++ b/components/url_param_filter/core/url_param_filterer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_filterer.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
 
 #include <vector>
 
@@ -13,9 +13,9 @@
 #include "base/strings/escape.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
-#include "chrome/common/chrome_features.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
 #include "third_party/zlib/google/compression_utils.h"
diff --git a/chrome/browser/url_param_filter/url_param_filterer.h b/components/url_param_filter/core/url_param_filterer.h
similarity index 84%
rename from chrome/browser/url_param_filter/url_param_filterer.h
rename to components/url_param_filter/core/url_param_filterer.h
index 186cd02..1d34326 100644
--- a/chrome/browser/url_param_filter/url_param_filterer.h
+++ b/components/url_param_filter/core/url_param_filterer.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTERER_H_
-#define CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTERER_H_
+#ifndef COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
 
-#include "chrome/browser/url_param_filter/url_param_classifications_loader.h"
-#include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
 #include "url/gurl.h"
 
 // Used to filter URL parameters based on backend classification rules. Note
@@ -47,4 +47,4 @@
                        const FilterClassification::UseCase use_case);
 
 }  // namespace url_param_filter
-#endif  // CHROME_BROWSER_URL_PARAM_FILTER_URL_PARAM_FILTERER_H_
+#endif  // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
diff --git a/chrome/browser/url_param_filter/url_param_filterer_unittest.cc b/components/url_param_filter/core/url_param_filterer_unittest.cc
similarity index 98%
rename from chrome/browser/url_param_filter/url_param_filterer_unittest.cc
rename to components/url_param_filter/core/url_param_filterer_unittest.cc
index b8687d4..b166f3a 100644
--- a/chrome/browser/url_param_filter/url_param_filterer_unittest.cc
+++ b/components/url_param_filter/core/url_param_filterer_unittest.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/url_param_filter/url_param_filterer.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
 
 #include <string>
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
-#include "chrome/common/chrome_features.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace url_param_filter {
-namespace {
 
 class UrlParamFiltererTest : public ::testing::Test {};
 
@@ -609,5 +609,4 @@
   ASSERT_EQ(result.filtered_param_count, 2);
 }
 
-}  // namespace
 }  // namespace url_param_filter
diff --git a/components/user_notes/browser/user_note_service.cc b/components/user_notes/browser/user_note_service.cc
index bba603e7..54eeeff 100644
--- a/components/user_notes/browser/user_note_service.cc
+++ b/components/user_notes/browser/user_note_service.cc
@@ -194,6 +194,12 @@
   (*entry_it->second.managers.begin())->RemoveNote(id);
 }
 
+void UserNoteService::OnNoteUpdated(const base::UnguessableToken& id,
+                                    const std::string& note_content) {
+  DCHECK(IsUserNotesEnabled());
+  NOTIMPLEMENTED();
+}
+
 UserNoteService::ModelMapEntry::ModelMapEntry(std::unique_ptr<UserNote> model)
     : model(std::move(model)) {}
 
diff --git a/components/user_notes/browser/user_note_service.h b/components/user_notes/browser/user_note_service.h
index 42d04f0..3fcd1c0 100644
--- a/components/user_notes/browser/user_note_service.h
+++ b/components/user_notes/browser/user_note_service.h
@@ -20,6 +20,8 @@
 #include "components/user_notes/interfaces/user_notes_ui_delegate.h"
 #include "components/user_notes/model/user_note.h"
 
+class UserNoteUICoordinatorTest;
+
 namespace content {
 class RenderFrameHost;
 }  // namespace content
@@ -85,6 +87,8 @@
   void OnNoteCreationDone(const base::UnguessableToken& id,
                           const std::string& note_content) override;
   void OnNoteCreationCancelled(const base::UnguessableToken& id) override;
+  void OnNoteUpdated(const base::UnguessableToken& id,
+                     const std::string& note_content) override;
 
  private:
   struct ModelMapEntry {
@@ -101,7 +105,7 @@
   friend class UserNoteBaseTest;
   friend class UserNoteInstanceTest;
   friend class UserNoteUtilsTest;
-  friend class UserNoteUICoordinatorTest;
+  friend class ::UserNoteUICoordinatorTest;
 
   // Source of truth for the in-memory note models. Any note currently being
   // displayed in a tab is stored in this data structure. Each entry also
diff --git a/components/user_notes/interfaces/user_notes_ui_delegate.h b/components/user_notes/interfaces/user_notes_ui_delegate.h
index 4bfed148..16aa2bf 100644
--- a/components/user_notes/interfaces/user_notes_ui_delegate.h
+++ b/components/user_notes/interfaces/user_notes_ui_delegate.h
@@ -29,6 +29,10 @@
 
   // Called when the user aborts the note creation process in the UI.
   virtual void OnNoteCreationCancelled(const base::UnguessableToken& id) = 0;
+
+  // Called when the user updates an existing note in the UI.
+  virtual void OnNoteUpdated(const base::UnguessableToken& id,
+                             const std::string& note_content) = 0;
 };
 
 }  // namespace user_notes
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index e1f2eedc..f018b200 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -367,21 +367,21 @@
 }
 
 void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
-  // We need to apply the matrix manually to have pixel-sized stroke width.
-  SkPoint vertices[4];
-  gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
-  SkPoint transformed_vertices[4];
-  current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
-                                              4);
+  SkMatrix m = current_canvas_->getTotalMatrix();
   current_canvas_->resetMatrix();
 
+  SkPath path;
+  path.addRect(gfx::RectFToSkRect(QuadVertexRect()));
+  path.transform(m);
+
   current_paint_.setColor(quad->color);
   current_paint_.setAlpha(quad->shared_quad_state->opacity *
                           SkColorGetA(quad->color));
   current_paint_.setStyle(SkPaint::kStroke_Style);
+  current_paint_.setStrokeJoin(SkPaint::kMiter_Join);
   current_paint_.setStrokeWidth(quad->width);
-  current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4,
-                              transformed_vertices, current_paint_);
+
+  current_canvas_->drawPath(path, current_paint_);
 }
 
 void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index 2590dbc..5cd1301 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -129,9 +129,10 @@
     static constexpr char kObserveEmptyReportsTableScript[] = R"(
     let table = document.querySelector('#reportTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1 &&
           table.children[0].children[0].innerText === "No sent or pending reports.") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -170,8 +171,9 @@
   // results are returned in promises.
   static constexpr char wait_script[] = R"(
     let status = document.getElementById("feature-status-content");
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (status.innerText.trim() === "enabled") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -202,8 +204,9 @@
   // results are returned in promises.
   static constexpr char wait_script[] = R"(
     let status = document.getElementById("feature-status-content");
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (status.innerText.trim() === "disabled") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -225,10 +228,11 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#sourceTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1 &&
           table.children[0].children[0].innerText ===
           "No sources.") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -298,7 +302,7 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#sourceTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 8 &&
           table.children[0].children[0].innerText === $1 &&
           table.children[0].children[7].innerText === "Navigation" &&
@@ -321,6 +325,7 @@
           table.children[5].children[1].innerText === "Rejected: insufficient source capacity" &&
           table.children[6].children[1].innerText === "Rejected: insufficient unique destination capacity" &&
           table.children[7].children[1].innerText === "Rejected: excessive reporting origins") {
+        obs.disconnect();
         document.title = $3;
       }
     });
@@ -356,8 +361,9 @@
   // results are returned in promises.
   static constexpr char wait_script[] = R"(
     let status = document.getElementById("debug-mode-content");
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (status.innerText.trim() === "") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -383,8 +389,9 @@
   // results are returned in promises.
   static constexpr char wait_script[] = R"(
     let status = document.getElementById("debug-mode-content");
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (status.innerText.trim() !== "") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -466,7 +473,7 @@
     static constexpr char wait_script[] = R"(
       let table = document.querySelector('#reportTable')
           .shadowRoot.querySelector('tbody');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 6 &&
             table.children[0].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/report-event-attribution" &&
@@ -484,6 +491,7 @@
             table.children[5].children[2].innerText === "Network error: ERR_TIMED_OUT" &&
             table.children[5].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/debug/report-event-attribution") {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -499,7 +507,7 @@
     static constexpr char wait_script[] = R"(
       let table = document.querySelector('#reportTable')
           .shadowRoot.querySelector('tbody');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 6 &&
             table.children[5].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/report-event-attribution" &&
@@ -517,6 +525,7 @@
             table.children[0].children[2].innerText === "Network error: ERR_TIMED_OUT" &&
             table.children[0].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/debug/report-event-attribution") {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -535,7 +544,7 @@
     static constexpr char wait_script[] = R"(
       let table = document.querySelector('#reportTable')
           .shadowRoot.querySelector('tbody');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 6 &&
             table.children[0].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/report-event-attribution" &&
@@ -553,6 +562,7 @@
             table.children[5].children[2].innerText === "Network error: ERR_TIMED_OUT" &&
             table.children[5].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/debug/report-event-attribution") {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -601,10 +611,11 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#reportTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 2 &&
           table.children[0].children[6].innerText === "7" &&
           table.children[1].children[2].innerText === "Sent: HTTP 200") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -650,10 +661,11 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#sourceTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 2 &&
           table.children[0].children[0].innerText === "5" &&
           table.children[1].children[0].innerText === "6") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -671,9 +683,10 @@
   static constexpr char kObserveEmptySourcesTableScript[] = R"(
     let table = document.querySelector('#sourceTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1 &&
           table.children[0].children[0].innerText === "No sources.") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -714,9 +727,10 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#reportTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1 &&
           table.children[0].children[6].innerText === "7") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -836,7 +850,7 @@
     static constexpr char wait_script[] = R"(
       let table = document.querySelector('#aggregatableReportTable')
           .shadowRoot.querySelector('tbody');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 6 &&
             table.children[0].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/report-aggregate-attribution" &&
@@ -849,6 +863,7 @@
             table.children[5].children[2].innerText === "Network error: ERR_INTERNET_DISCONNECTED" &&
             table.children[5].children[3].innerText ===
               "https://report.test/.well-known/attribution-reporting/debug/report-aggregate-attribution") {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -898,7 +913,7 @@
   static constexpr char wait_script[] = R"(
       let table = document.querySelector('#triggerTable')
           .shadowRoot.querySelector('tbody');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 1 &&
             table.children[0].children[1].innerText === "Success: Report stored" &&
             table.children[0].children[2].innerText === "Success: Report stored" &&
@@ -907,6 +922,7 @@
             table.children[0].children[5].innerText === "1" &&
             table.children[0].children[6].innerText === '{ "a": [  "b" ]}' &&
             table.children[0].children[7].innerText === $2) {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -969,8 +985,9 @@
   static constexpr char wait_script[] = R"(
     let table = document.querySelector('#aggregatableReportTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1) {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -989,9 +1006,10 @@
   static constexpr char kObserveEmptyReportsTableScript[] = R"(
     let table = document.querySelector('#aggregatableReportTable')
         .shadowRoot.querySelector('tbody');
-    let obs = new MutationObserver(() => {
+    let obs = new MutationObserver((_, obs) => {
       if (table.children.length === 1 &&
           table.children[0].children[0].innerText === "No sent or pending reports.") {
+        obs.disconnect();
         document.title = $1;
       }
     });
@@ -1045,11 +1063,12 @@
       let table = document.querySelector('#reportTable')
           .shadowRoot.querySelector('tbody');
       let label = document.querySelector('#show-debug-event-reports span');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 2 &&
             table.children[0].children[6].innerText === "1" &&
             table.children[1].children[6].innerText === "2" &&
             label.innerText === '') {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -1083,10 +1102,11 @@
       let table = document.querySelector('#reportTable')
           .shadowRoot.querySelector('tbody');
       let label = document.querySelector('#show-debug-event-reports span');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 1 &&
             table.children[0].children[6].innerText === "2" &&
             label.innerText === ' (2 hidden)') {
+          obs.disconnect();
           document.title = $1;
         }
       });
@@ -1110,12 +1130,13 @@
       let table = document.querySelector('#reportTable').shadowRoot
           .querySelector('tbody');
       let label = document.querySelector('#show-debug-event-reports span');
-      let obs = new MutationObserver(() => {
+      let obs = new MutationObserver((_, obs) => {
         if (table.children.length === 3 &&
             table.children[0].children[6].innerText === "1" &&
             table.children[1].children[6].innerText === "2" &&
             table.children[2].children[6].innerText === "3" &&
             label.innerText === '') {
+          obs.disconnect();
           document.title = $1;
         }
       });
diff --git a/content/browser/compute_pressure/procfs_stat_cpu_parser_unittest.cc b/content/browser/compute_pressure/procfs_stat_cpu_parser_unittest.cc
index 0a0095b..6b1f821 100644
--- a/content/browser/compute_pressure/procfs_stat_cpu_parser_unittest.cc
+++ b/content/browser/compute_pressure/procfs_stat_cpu_parser_unittest.cc
@@ -192,9 +192,9 @@
       "cpu-100 1 2 3 4 5 6 7 8 9 0",
       "cpu1a 1 2 3 4 5 6 7 8 9 0",
       "cpu1- 1 2 3 4 5 6 7 8 9 0",
-      u8"cpu\U0001f41b 1 2 3 4 5 6 7 8 9 0",
-      u8"cpu1\U0001f41b 1 2 3 4 5 6 7 8 9 0",
-      u8"cpu\U0001f41b1 1 2 3 4 5 6 7 8 9 0",
+      "cpu\U0001f41b 1 2 3 4 5 6 7 8 9 0",
+      "cpu1\U0001f41b 1 2 3 4 5 6 7 8 9 0",
+      "cpu\U0001f41b1 1 2 3 4 5 6 7 8 9 0",
   };
 
   for (const char* test_case : test_cases) {
@@ -219,9 +219,9 @@
       "cpu0 123456789012345678901 2 3 4 5 6 7 8 9 10",
       "cpu0 -123456789012345678901 2 3 4 5 6 7 8 9 10",
       "cpu0 18446744073709551616 2 3 4 5 6 7 8 9 10",
-      u8"cpu0 \U0001f41b 2 3 4 5 6 7 8 9 10",
-      u8"cpu0 1\U0001f41b 2 3 4 5 6 7 8 9 10",
-      u8"cpu0 \U0001f41b1 2 3 4 5 6 7 8 9 10",
+      "cpu0 \U0001f41b 2 3 4 5 6 7 8 9 10",
+      "cpu0 1\U0001f41b 2 3 4 5 6 7 8 9 10",
+      "cpu0 \U0001f41b1 2 3 4 5 6 7 8 9 10",
   };
 
   for (const char* test_case : test_cases) {
@@ -262,9 +262,9 @@
       {"cpu0 1 2 3 4 5 6 7 a 9 10", 7},
       {"cpu0 1 2 3 4 5 6 7 8 a 10", 8},
       {"cpu0 1 2 3 4 5 6 7 8 9 a", 9},
-      {u8"cpu0 1 \U0001f41b 3 4 5 6 7 8 9 10", 1},
-      {u8"cpu0 1 2 3\U0001f41b 4 5 6 7 8 9 10", 2},
-      {u8"cpu0 1 2 3 \U0001f41b4 5 6 7 8 9 10", 3},
+      {"cpu0 1 \U0001f41b 3 4 5 6 7 8 9 10", 1},
+      {"cpu0 1 2 3\U0001f41b 4 5 6 7 8 9 10", 2},
+      {"cpu0 1 2 3 \U0001f41b4 5 6 7 8 9 10", 3},
   };
 
   for (const TestCase& test_case : test_cases) {
diff --git a/content/browser/compute_pressure/sysfs_cpufreq_core_parser_unittest.cc b/content/browser/compute_pressure/sysfs_cpufreq_core_parser_unittest.cc
index ddd4a320..3612f3e9 100644
--- a/content/browser/compute_pressure/sysfs_cpufreq_core_parser_unittest.cc
+++ b/content/browser/compute_pressure/sysfs_cpufreq_core_parser_unittest.cc
@@ -199,15 +199,8 @@
 
 TEST_P(SysfsCpufreqCoreParserRoutingTest, Read_InvalidFirmwareFile) {
   std::vector<const char*> invalid_numbers = {
-      "-1",
-      "-100",
-      "-",
-      "a",
-      "1a",
-      "1234a",
-      u8"\U0001f41b",
-      u8"1\U0001f41b",
-      u8"\U0001f41b1",
+      "-1",    "-100",       "-",           "a",           "1a",
+      "1234a", "\U0001f41b", "1\U0001f41b", "\U0001f41b1",
   };
 
   for (const char* invalid_number : invalid_numbers) {
@@ -230,15 +223,8 @@
 
 TEST_P(SysfsCpufreqCoreParserRoutingTest, Read_InvalidGovernorFile) {
   std::vector<const char*> invalid_numbers = {
-      "-1",
-      "-100",
-      "-",
-      "a",
-      "1a",
-      "1234a",
-      u8"\U0001f41b",
-      u8"1\U0001f41b",
-      u8"\U0001f41b1",
+      "-1",    "-100",       "-",           "a",           "1a",
+      "1234a", "\U0001f41b", "1\U0001f41b", "\U0001f41b1",
   };
 
   for (const char* invalid_number : invalid_numbers) {
diff --git a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc b/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc
index 4ac52a2..040ae3c0 100644
--- a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc
+++ b/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc
@@ -109,7 +109,7 @@
   )")
                   .ExtractBool());
   int64_t usage_after_operation = GetUsageSync(quota_manager, storage_key);
-  EXPECT_EQ(usage_before_operation + 10, usage_after_operation);
+  EXPECT_EQ(usage_after_operation, usage_before_operation + 10);
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest,
@@ -171,13 +171,6 @@
   const GURL& test_url =
       embedded_test_server()->GetURL("/run_async_code_on_worker.html");
   Shell* browser = CreateBrowser();
-  scoped_refptr<storage::QuotaManager> quota_manager =
-      browser->web_contents()
-          ->GetBrowserContext()
-          ->GetDefaultStoragePartition()
-          ->GetQuotaManager();
-  blink::StorageKey storage_key =
-      blink::StorageKey::CreateFromStringForTesting(test_url.spec());
 
   NavigateToURLBlockUntilNavigationsComplete(browser, test_url,
                                              /*number_of_navigations=*/1);
@@ -216,4 +209,127 @@
             4 * 1024 * 1024);
 }
 
+// TODO(crbug.com/1304977): Failing on Mac builders.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_QuotaUsageShrinks DISABLED_QuotaUsageShrinks
+#else
+#define MAYBE_QuotaUsageShrinks QuotaUsageShrinks
+#endif
+IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest,
+                       MAYBE_QuotaUsageShrinks) {
+  const GURL& test_url =
+      embedded_test_server()->GetURL("/run_async_code_on_worker.html");
+  Shell* browser = CreateBrowser();
+
+  NavigateToURLBlockUntilNavigationsComplete(browser, test_url,
+                                             /*number_of_navigations=*/1);
+  EXPECT_EQ(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_file_medium', {create: true});
+      let ah =  await fh.createSyncAccessHandle();
+      let storage_manager = await navigator.storage.estimate();
+      let usage_before_operation = storage_manager.usageDetails.fileSystem;
+      let new_file_size = 3*1024*1024;
+      await ah.truncate(new_file_size);
+      storage_manager = await navigator.storage.estimate();
+      let usage_after_operation = storage_manager.usageDetails.fileSystem;
+      await ah.close();
+      return usage_after_operation-usage_before_operation;
+    `);
+  )")
+                .ExtractInt(),
+            4 * 1024 * 1024);
+  EXPECT_EQ(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_file_small', {create: true});
+      let ah =  await fh.createSyncAccessHandle();
+      let storage_manager = await navigator.storage.estimate();
+      let usage_before_operation = storage_manager.usageDetails.fileSystem;
+      await ah.truncate(100);
+      storage_manager = await navigator.storage.estimate();
+      let usage_after_operation = storage_manager.usageDetails.fileSystem;
+      await ah.close();
+      return usage_after_operation-usage_before_operation;
+    `);
+  )")
+                .ExtractInt(),
+            1024 * 1024);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest,
+                       QuotaUsageWrite) {
+  const GURL& test_url =
+      embedded_test_server()->GetURL("/run_async_code_on_worker.html");
+  Shell* browser = CreateBrowser();
+  scoped_refptr<storage::QuotaManager> quota_manager =
+      browser->web_contents()
+          ->GetBrowserContext()
+          ->GetDefaultStoragePartition()
+          ->GetQuotaManager();
+  blink::StorageKey storage_key =
+      blink::StorageKey::CreateFromStringForTesting(test_url.spec());
+
+  NavigateToURLBlockUntilNavigationsComplete(browser, test_url,
+                                             /*number_of_navigations=*/1);
+  EXPECT_TRUE(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_closing', {create: true});
+      return true;
+    `);
+  )")
+                  .ExtractBool());
+  int64_t usage_before_operation = GetUsageSync(quota_manager, storage_key);
+
+  // write() should update quota correctly.
+  EXPECT_TRUE(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_closing', {create: false});
+      let ah = await fh.createSyncAccessHandle();
+      const buffer = new DataView(new ArrayBuffer(10));
+      ah.write(buffer, { at: 0 });
+      await ah.close();
+      return true;
+    `);
+  )")
+                  .ExtractBool());
+  int64_t usage_after_write1 = GetUsageSync(quota_manager, storage_key);
+  EXPECT_EQ(usage_after_write1, usage_before_operation + 10);
+
+  // Write at an offset past the end of the file.
+  EXPECT_TRUE(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_closing', {create: false});
+      let ah = await fh.createSyncAccessHandle();
+      const buffer = new DataView(new ArrayBuffer(80));
+      ah.write(buffer, { at: 20 });
+      await ah.close();
+      return true;
+    `);
+  )")
+                  .ExtractBool());
+  int64_t usage_after_write2 = GetUsageSync(quota_manager, storage_key);
+  EXPECT_EQ(usage_after_write2, usage_before_operation + 100);
+
+  // Writing in bytes the middle of the file so the file size does not change.
+  EXPECT_TRUE(EvalJs(browser, R"(
+    runOnWorkerAndWaitForResult(`
+      let root = await navigator.storage.getDirectory();
+      let fh = await root.getFileHandle('test_closing', {create: false});
+      let ah = await fh.createSyncAccessHandle();
+      const buffer = new DataView(new ArrayBuffer(20));
+      ah.write(buffer, { at: 5 });
+      await ah.close();
+      return true;
+    `);
+  )")
+                  .ExtractBool());
+  int64_t usage_after_write3 = GetUsageSync(quota_manager, storage_key);
+  EXPECT_EQ(usage_after_write3, usage_after_write2);
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index 7549060..6a0c8686 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -715,14 +715,19 @@
   // fenced frame's FrameTree. Note that this could be a field in FrameTree for
   // the MPArch version but for the shadow DOM version we need to keep it here
   // since the fenced frame root is not a main frame for the latter. The value
-  // of the nonce will be the same for all of the the frames inside a fenced
+  // of the nonce will be the same for all of the the iframes inside a fenced
   // frame tree. If there is a nested fenced frame it will have a different
   // nonce than its parent fenced frame. The nonce will stay the same across
-  // navigations because it is always used in conjunction with other fields of
-  // the keys. If the navigation is same-origin/site then the same network stack
-  // partition/storage will be reused and if it's cross-origin/site then other
-  // parts of the key will change and so, even with the same nonce, another
-  // partition will be used.
+  // navigations initiated from the fenced frame tree because it is always used
+  // in conjunction with other fields of the keys and would be good to access
+  // the same storage across same-origin navigations. If the navigation is
+  // same-origin/site then the same network stack partition/storage will be
+  // reused and if it's cross-origin/site then other parts of the key will
+  // change and so, even with the same nonce, another partition will be used.
+  // But if the navigation is initiated from the embedder, the nonce will be
+  // reinitialized irrespective of same or cross origin such that there is no
+  // privacy leak via storage shared between two embedder initiated navigations.
+  // Note that this reinitialization is only implemented for MPArch.
   absl::optional<base::UnguessableToken> fenced_frame_nonce_;
 
   const RenderFrameHostImpl::FencedFrameStatus fenced_frame_status_ =
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 0af88f1e..b41b617 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -231,8 +231,6 @@
 #include "third_party/blink/public/common/permissions/permission_utils.h"
 #include "third_party/blink/public/common/permissions_policy/document_policy.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
-#include "third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h"
-#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
@@ -1121,25 +1119,6 @@
       std::move(callback));
 }
 
-// Records the identifiable surface metric associated with a document created
-// event when the identifiability study is active.
-void RecordIdentifiabilityDocumentCreatedMetrics(
-    const ukm::SourceId document_ukm_source_id,
-    ukm::UkmRecorder* ukm_recorder,
-    ukm::SourceId navigation_source_id,
-    bool is_cross_origin_frame,
-    bool is_cross_site_frame,
-    bool is_main_frame) {
-  if (blink::IdentifiabilityStudySettings::Get()->IsActive()) {
-    blink::IdentifiabilityStudyDocumentCreated(document_ukm_source_id)
-        .SetNavigationSourceId(navigation_source_id)
-        .SetIsMainFrame(is_main_frame)
-        .SetIsCrossOriginFrame(is_cross_origin_frame)
-        .SetIsCrossSiteFrame(is_cross_site_frame)
-        .Record(ukm_recorder);
-  }
-}
-
 }  // namespace
 
 class RenderFrameHostImpl::SubresourceLoaderFactoriesConfig {
@@ -13333,10 +13312,6 @@
       .SetIsCrossOriginFrame(is_cross_origin_frame)
       .SetIsCrossSiteFrame(is_cross_site_frame)
       .Record(ukm_recorder);
-
-  RecordIdentifiabilityDocumentCreatedMetrics(
-      document_ukm_source_id, ukm_recorder, GetPageUkmSourceId(),
-      is_cross_origin_frame, is_cross_site_frame, IsOutermostMainFrame());
 }
 
 void RenderFrameHostImpl::BindReportingObserver(
diff --git a/extensions/browser/api/messaging/extension_message_port.cc b/extensions/browser/api/messaging/extension_message_port.cc
index 5dc34c9..246f4f5 100644
--- a/extensions/browser/api/messaging/extension_message_port.cc
+++ b/extensions/browser/api/messaging/extension_message_port.cc
@@ -289,8 +289,7 @@
 void ExtensionMessagePort::DispatchOnConnect(
     const std::string& channel_name,
     std::unique_ptr<base::DictionaryValue> source_tab,
-    int source_frame_id,
-    const ExtensionApiFrameIdMap::DocumentId& source_document_id,
+    const ExtensionApiFrameIdMap::FrameData& source_frame,
     int guest_process_id,
     int guest_render_frame_routing_id,
     const MessagingEndpoint& source_endpoint,
@@ -300,9 +299,9 @@
   SendToPort(base::BindRepeating(
       &ExtensionMessagePort::BuildDispatchOnConnectIPC,
       // Called synchronously.
-      base::Unretained(this), channel_name, source_tab.get(), source_frame_id,
-      source_document_id, guest_process_id, guest_render_frame_routing_id,
-      source_endpoint, target_extension_id, source_url, source_origin));
+      base::Unretained(this), channel_name, source_tab.get(), source_frame,
+      guest_process_id, guest_render_frame_routing_id, source_endpoint,
+      target_extension_id, source_url, source_origin));
 }
 
 void ExtensionMessagePort::DispatchOnDisconnect(
@@ -524,8 +523,7 @@
 std::unique_ptr<IPC::Message> ExtensionMessagePort::BuildDispatchOnConnectIPC(
     const std::string& channel_name,
     const base::DictionaryValue* source_tab,
-    int source_frame_id,
-    const ExtensionApiFrameIdMap::DocumentId& source_document_id,
+    const ExtensionApiFrameIdMap::FrameData& source_frame,
     int guest_process_id,
     int guest_render_frame_routing_id,
     const MessagingEndpoint& source_endpoint,
@@ -536,7 +534,7 @@
   ExtensionMsg_TabConnectionInfo source;
 
   // Source document ID should exist if and only if there is a source tab.
-  DCHECK_EQ(!!source_tab, !!source_document_id);
+  DCHECK_EQ(!!source_tab, !!source_frame.document_id);
   if (source_tab) {
     std::unique_ptr<base::Value> source_tab_value =
         base::Value::ToUniquePtrValue(source_tab->Clone());
@@ -544,9 +542,10 @@
     // remove this cast.
     source.tab.Swap(
         static_cast<base::DictionaryValue*>(source_tab_value.get()));
-    source.document_id = source_document_id.ToString();
+    source.document_id = source_frame.document_id.ToString();
+    source.document_lifecycle = ToString(source_frame.document_lifecycle);
   }
-  source.frame_id = source_frame_id;
+  source.frame_id = source_frame.frame_id;
 
   ExtensionMsg_ExternalConnectionInfo info;
   info.target_id = target_extension_id;
diff --git a/extensions/browser/api/messaging/extension_message_port.h b/extensions/browser/api/messaging/extension_message_port.h
index f3c37ebe..11b6a942 100644
--- a/extensions/browser/api/messaging/extension_message_port.h
+++ b/extensions/browser/api/messaging/extension_message_port.h
@@ -81,17 +81,15 @@
   bool HasFrame(content::RenderFrameHost* rfh) const override;
   bool IsValidPort() override;
   void RevalidatePort() override;
-  void DispatchOnConnect(
-      const std::string& channel_name,
-      std::unique_ptr<base::DictionaryValue> source_tab,
-      int source_frame_id,
-      const ExtensionApiFrameIdMap::DocumentId& source_document_id,
-      int guest_process_id,
-      int guest_render_frame_routing_id,
-      const MessagingEndpoint& source_endpoint,
-      const std::string& target_extension_id,
-      const GURL& source_url,
-      absl::optional<url::Origin> source_origin) override;
+  void DispatchOnConnect(const std::string& channel_name,
+                         std::unique_ptr<base::DictionaryValue> source_tab,
+                         const ExtensionApiFrameIdMap::FrameData& source_frame,
+                         int guest_process_id,
+                         int guest_render_frame_routing_id,
+                         const MessagingEndpoint& source_endpoint,
+                         const std::string& target_extension_id,
+                         const GURL& source_url,
+                         absl::optional<url::Origin> source_origin) override;
   void DispatchOnDisconnect(const std::string& error_message) override;
   void DispatchOnMessage(const Message& message) override;
   void IncrementLazyKeepaliveCount(bool is_for_native_message_connect) override;
@@ -136,8 +134,7 @@
   std::unique_ptr<IPC::Message> BuildDispatchOnConnectIPC(
       const std::string& channel_name,
       const base::DictionaryValue* source_tab,
-      int source_frame_id,
-      const ExtensionApiFrameIdMap::DocumentId& source_document_id,
+      const ExtensionApiFrameIdMap::FrameData& source_frame,
       int guest_process_id,
       int guest_render_frame_routing_id,
       const MessagingEndpoint& source_endpoint,
diff --git a/extensions/browser/api/messaging/message_port.cc b/extensions/browser/api/messaging/message_port.cc
index f39c701..77af2eb 100644
--- a/extensions/browser/api/messaging/message_port.cc
+++ b/extensions/browser/api/messaging/message_port.cc
@@ -20,8 +20,7 @@
 void MessagePort::DispatchOnConnect(
     const std::string& channel_name,
     std::unique_ptr<base::DictionaryValue> source_tab,
-    int source_frame_id,
-    const ExtensionApiFrameIdMap::DocumentId& source_document_id,
+    const ExtensionApiFrameIdMap::FrameData& source_frame,
     int guest_process_id,
     int guest_render_frame_routing_id,
     const MessagingEndpoint& source_endpoint,
diff --git a/extensions/browser/api/messaging/message_port.h b/extensions/browser/api/messaging/message_port.h
index 25dba800..f8d577b 100644
--- a/extensions/browser/api/messaging/message_port.h
+++ b/extensions/browser/api/messaging/message_port.h
@@ -67,8 +67,7 @@
   virtual void DispatchOnConnect(
       const std::string& channel_name,
       std::unique_ptr<base::DictionaryValue> source_tab,
-      int source_frame_id,
-      const ExtensionApiFrameIdMap::DocumentId& source_document_id,
+      const ExtensionApiFrameIdMap::FrameData& source_frame,
       int guest_process_id,
       int guest_render_frame_routing_id,
       const MessagingEndpoint& source_endpoint,
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc
index 0e7956d..b2c006d 100644
--- a/extensions/browser/api/messaging/message_service.cc
+++ b/extensions/browser/api/messaging/message_service.cc
@@ -149,8 +149,7 @@
 struct MessageService::OpenChannelParams {
   ChannelEndpoint source;
   std::unique_ptr<base::DictionaryValue> source_tab;
-  int source_frame_id;
-  ExtensionApiFrameIdMap::DocumentId source_document_id;
+  ExtensionApiFrameIdMap::FrameData source_frame;
   std::unique_ptr<MessagePort> receiver;
   PortId receiver_port_id;
   MessagingEndpoint source_endpoint;
@@ -162,24 +161,21 @@
   bool include_guest_process_info;
 
   // Takes ownership of receiver.
-  OpenChannelParams(
-      const ChannelEndpoint& source,
-      std::unique_ptr<base::DictionaryValue> source_tab,
-      int source_frame_id,
-      const ExtensionApiFrameIdMap::DocumentId& source_document_id,
-      MessagePort* receiver,
-      const PortId& receiver_port_id,
-      const MessagingEndpoint& source_endpoint,
-      std::unique_ptr<MessagePort> opener_port,
-      const std::string& target_extension_id,
-      const GURL& source_url,
-      absl::optional<url::Origin> source_origin,
-      const std::string& channel_name,
-      bool include_guest_process_info)
+  OpenChannelParams(const ChannelEndpoint& source,
+                    std::unique_ptr<base::DictionaryValue> source_tab,
+                    const ExtensionApiFrameIdMap::FrameData& source_frame,
+                    MessagePort* receiver,
+                    const PortId& receiver_port_id,
+                    const MessagingEndpoint& source_endpoint,
+                    std::unique_ptr<MessagePort> opener_port,
+                    const std::string& target_extension_id,
+                    const GURL& source_url,
+                    absl::optional<url::Origin> source_origin,
+                    const std::string& channel_name,
+                    bool include_guest_process_info)
       : source(source),
         source_tab(std::move(source_tab)),
-        source_frame_id(source_frame_id),
-        source_document_id(source_document_id),
+        source_frame(source_frame),
         receiver(receiver),
         receiver_port_id(receiver_port_id),
         source_endpoint(source_endpoint),
@@ -345,8 +341,7 @@
         WebContents::FromRenderFrameHost(source_render_frame_host);
   }
 
-  int source_frame_id = -1;
-  ExtensionApiFrameIdMap::DocumentId source_document_id;
+  ExtensionApiFrameIdMap::FrameData source_frame;
   bool include_guest_process_info = false;
 
   // Get information about the opener's tab, if applicable.
@@ -359,10 +354,8 @@
 
   if (source_tab.get()) {
     DCHECK(source_render_frame_host);
-    source_frame_id =
-        ExtensionApiFrameIdMap::GetFrameId(source_render_frame_host);
-    source_document_id =
-        ExtensionApiFrameIdMap::GetDocumentId(source_render_frame_host);
+    source_frame = ExtensionApiFrameIdMap::Get()->GetFrameData(
+        source_render_frame_host->GetGlobalId());
   } else {
     // Check to see if it was a WebView making the request.
     // Sending messages from WebViews to extensions breaks webview isolation,
@@ -376,8 +369,8 @@
 
   std::unique_ptr<OpenChannelParams> params =
       std::make_unique<OpenChannelParams>(
-          source, std::move(source_tab), source_frame_id, source_document_id,
-          nullptr, source_port_id.GetOppositePortId(), source_endpoint,
+          source, std::move(source_tab), source_frame, nullptr,
+          source_port_id.GetOppositePortId(), source_endpoint,
           std::move(opener_port), target_extension_id, source_url,
           std::move(source_origin), channel_name, include_guest_process_info);
   pending_incognito_channels_[params->receiver_port_id.GetChannelId()] =
@@ -573,9 +566,7 @@
           std::unique_ptr<base::DictionaryValue>(),  // Source tab doesn't make
                                                      // sense
                                                      // for opening to tabs.
-          -1,  // If there is no tab, then there is no frame either.
-          ExtensionApiFrameIdMap::DocumentId(),  // If there is no frame, there
-                                                 // is no document either.
+          ExtensionApiFrameIdMap::FrameData(),       // There is no frame.
           receiver.release(), receiver_port_id,
           MessagingEndpoint::ForExtension(extension_id), std::move(opener_port),
           extension_id,
@@ -659,9 +650,8 @@
   // Send the connect event to the receiver.  Give it the opener's port ID (the
   // opener has the opposite port ID).
   channel->receiver->DispatchOnConnect(
-      params->channel_name, std::move(params->source_tab),
-      params->source_frame_id, params->source_document_id, guest_process_id,
-      guest_render_frame_routing_id, params->source_endpoint,
+      params->channel_name, std::move(params->source_tab), params->source_frame,
+      guest_process_id, guest_render_frame_routing_id, params->source_endpoint,
       params->target_extension_id, params->source_url, params->source_origin);
 
   // Report the event to the event router, if the target is an extension.
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index 26c062e..796e2bb 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -71,7 +71,8 @@
           "nativeApplication": {"type": "string", "optional": true, "description": "The name of the native application that opened the connection, if any."},
           "tlsChannelId": {"type": "string", "optional": true, "description": "The TLS channel ID of the page or frame that opened the connection, if requested by the extension or app, and if available."},
           "origin": {"type": "string", "optional": true, "description": "The origin of the page or frame that opened the connection. It can vary from the url property (e.g., about:blank) or can be opaque (e.g., sandboxed iframes). This is useful for identifying if the origin can be trusted if we can't immediately tell from the URL."},
-          "documentId": {"type": "string", "optional": true, "description": "A UUID of the document that opened the connection.", "nodoc": true}
+          "documentId": {"type": "string", "optional": true, "description": "A UUID of the document that opened the connection.", "nodoc": true},
+          "documentLifecycle": {"type": "string", "optional": true, "description": "The lifecycle the document that opened the connection is in at the time the port was created. Note that the lifecycle state of the document may have changed since port creation.","nodoc": true}
         }
       },
       {
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 5791dd8..76ff3a4 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -149,6 +149,9 @@
 
   // The unique ID of the document of the frame that initiated the connection.
   IPC_STRUCT_MEMBER(std::string, document_id)
+
+  // The lifecycle of the frame that initiated the connection.
+  IPC_STRUCT_MEMBER(std::string, document_lifecycle)
 IPC_STRUCT_END()
 
 // Struct containing information about the destination of tab.connect().
diff --git a/extensions/renderer/native_renderer_messaging_service.cc b/extensions/renderer/native_renderer_messaging_service.cc
index 80df703e3..69119cffd 100644
--- a/extensions/renderer/native_renderer_messaging_service.cc
+++ b/extensions/renderer/native_renderer_messaging_service.cc
@@ -449,6 +449,8 @@
     sender_builder.Set("frameId", source->frame_id);
   if (!source->document_id.empty())
     sender_builder.Set("documentId", source->document_id);
+  if (!source->document_lifecycle.empty())
+    sender_builder.Set("documentLifecycle", source->document_lifecycle);
 
   const Extension* extension = script_context->extension();
   if (extension) {
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn
index 9ad43ba7..0043b344 100644
--- a/fuchsia/runners/BUILD.gn
+++ b/fuchsia/runners/BUILD.gn
@@ -160,15 +160,24 @@
   }
 }
 
-fuchsia_component("cast_runner_component") {
+fuchsia_component("cast_runner_component_v1") {
   manifest = "cast/cast_runner.cmx"
   data_deps = [ ":cast_runner_exe" ]
   visibility = [ ":*" ]
 }
 
+fuchsia_component("cast_runner_component") {
+  manifest = "cast/cast_runner.cml"
+  data_deps = [ ":cast_runner_exe" ]
+  visibility = [ ":*" ]
+}
+
 fuchsia_package("cast_runner_pkg") {
   package_name = "cast_runner"
-  deps = [ ":cast_runner_component" ]
+  deps = [
+    ":cast_runner_component",
+    ":cast_runner_component_v1",
+  ]
   excluded_files = _web_instance_host_deps_files_to_exclude
 }
 
diff --git a/fuchsia/runners/cast/OWNERS b/fuchsia/runners/cast/OWNERS
index d0de80c..3eee5cb 100644
--- a/fuchsia/runners/cast/OWNERS
+++ b/fuchsia/runners/cast/OWNERS
@@ -1,2 +1,4 @@
+per-file *.cml=set noparent
+per-file *.cml=file://build/fuchsia/SECURITY_OWNERS
 per-file *.cmx=set noparent
 per-file *.cmx=file://build/fuchsia/SECURITY_OWNERS
diff --git a/fuchsia/runners/cast/cast_runner.cml b/fuchsia/runners/cast/cast_runner.cml
new file mode 100644
index 0000000..184963c
--- /dev/null
+++ b/fuchsia/runners/cast/cast_runner.cml
@@ -0,0 +1,74 @@
+{
+  include: [
+    "inspect/client.shard.cml",
+    "syslog/client.shard.cml",
+    "vulkan/client.shard.cml",
+  ],
+  program: {
+    runner: "elf",
+    binary: "cast_runner_exe",
+    args: [ "--enable-cfv2" ],
+    // TODO(crbug.com/1280703): Remove once web_instance is a CFv2 component.
+    lifecycle: { stop_event: "notify" },
+  },
+  capabilities: [
+    {
+      protocol: [
+        "fuchsia.sys.Runner",
+      ]
+    }
+  ],
+  expose: [
+    {
+      protocol: [
+        "fuchsia.sys.Runner",
+      ],
+      from: "self",
+    }
+  ],
+  use: [
+    {
+      directory: "config-data",
+      rights: [ "r*" ],
+      path: "/config/data",
+    },
+    {
+      protocol: [
+        // Capabilities to be passed to WebEngine instances.
+        "fuchsia.accessibility.semantics.SemanticsManager",
+        "fuchsia.buildinfo.Provider",
+        // "fuchsia.camera3.DeviceWatcher",
+        "fuchsia.device.NameProvider",
+        "fuchsia.feedback.ComponentDataRegister",
+        "fuchsia.feedback.CrashReportingProductRegister",
+        "fuchsia.fonts.Provider",
+        "fuchsia.input.virtualkeyboard.ControllerCreator",
+        "fuchsia.intl.PropertyProvider",
+        // "fuchsia.legacymetrics.MetricsRecorder",
+        // "fuchsia.media.Audio",
+        "fuchsia.media.AudioDeviceEnumerator",
+        "fuchsia.media.ProfileProvider",
+        "fuchsia.media.SessionAudioConsumerFactory",
+        "fuchsia.media.drm.PlayReady",
+        "fuchsia.media.drm.Widevine",
+        "fuchsia.mediacodec.CodecFactory",
+        "fuchsia.memorypressure.Provider",
+        "fuchsia.net.interfaces.State",
+        "fuchsia.net.name.Lookup",
+        "fuchsia.posix.socket.Provider",
+        "fuchsia.process.Launcher",
+        "fuchsia.settings.Display",
+        "fuchsia.sys.Environment",
+        "fuchsia.sys.Loader",
+        "fuchsia.ui.composition.Allocator",
+        "fuchsia.ui.composition.Flatland",
+        "fuchsia.ui.input3.Keyboard",
+        "fuchsia.ui.scenic.Scenic",
+      ]
+    },
+    {
+      storage: "cache",
+      path: "/cache",
+    },
+  ]
+}
diff --git a/fuchsia/runners/cast/cast_runner_switches.cc b/fuchsia/runners/cast/cast_runner_switches.cc
index 1a8a7e2..f207f2f 100644
--- a/fuchsia/runners/cast/cast_runner_switches.cc
+++ b/fuchsia/runners/cast/cast_runner_switches.cc
@@ -4,6 +4,8 @@
 
 #include "fuchsia/runners/cast/cast_runner_switches.h"
 
-const char kForceHeadlessForTestsSwitch[] = "force-headless-for-tests";
-
 const char kDisableVulkanForTestsSwitch[] = "disable-vulkan-for-tests";
+
+const char kEnableCfv2[] = "enable-cfv2";
+
+const char kForceHeadlessForTestsSwitch[] = "force-headless-for-tests";
diff --git a/fuchsia/runners/cast/cast_runner_switches.h b/fuchsia/runners/cast/cast_runner_switches.h
index e75bc13..b7d9a63 100644
--- a/fuchsia/runners/cast/cast_runner_switches.h
+++ b/fuchsia/runners/cast/cast_runner_switches.h
@@ -5,10 +5,14 @@
 #ifndef FUCHSIA_RUNNERS_CAST_CAST_RUNNER_SWITCHES_H_
 #define FUCHSIA_RUNNERS_CAST_CAST_RUNNER_SWITCHES_H_
 
-// Force headless mode.
-extern const char kForceHeadlessForTestsSwitch[];
-
 // Disable Vulkan flag for the cast runner. Used for tests.
 extern const char kDisableVulkanForTestsSwitch[];
 
-#endif  // FUCHSIA_RUNNERS_CAST_CAST_RUNNER_SWITCHES_H_
\ No newline at end of file
+// Run as a CFv2 component, processing StartComponent requests from the CFv1
+// shim.
+extern const char kEnableCfv2[];
+
+// Force headless mode.
+extern const char kForceHeadlessForTestsSwitch[];
+
+#endif  // FUCHSIA_RUNNERS_CAST_CAST_RUNNER_SWITCHES_H_
diff --git a/fuchsia/runners/cast/main.cc b/fuchsia/runners/cast/main.cc
index e02c979..f05c28966 100644
--- a/fuchsia/runners/cast/main.cc
+++ b/fuchsia/runners/cast/main.cc
@@ -7,10 +7,13 @@
 
 #include "base/command_line.h"
 #include "base/fuchsia/process_context.h"
+#include "base/fuchsia/process_lifecycle.h"
 #include "base/fuchsia/scoped_service_binding.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/notreached.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "base/task/single_thread_task_executor.h"
 #include "base/values.h"
 #include "fuchsia/base/config_reader.h"
@@ -25,17 +28,15 @@
 
 namespace {
 
-constexpr char kCrashProductName[] = "FuchsiaCastRunner";
-// TODO(https://fxbug.dev/51490): Use a programmatic mechanism to obtain this.
-constexpr char kComponentUrl[] =
-    "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx";
-
 // Config-data key for launching Cast content without using Scenic.
 constexpr char kHeadlessConfigKey[] = "headless";
 
 // Config-data key to enable the fuchsia.web.FrameHost provider component.
 constexpr char kFrameHostConfigKey[] = "enable-frame-host-component";
 
+// Config-data key to run the CFv1 runner as a shim to the CFv2 runner.
+constexpr char kRunCfv1ShimConfigKey[] = "run-cfv1-shim";
+
 // Returns the value of |config_key| or false if it is not set.
 bool GetConfigBool(base::StringPiece config_key) {
   const absl::optional<base::Value>& config = cr_fuchsia::LoadPackageConfig();
@@ -49,12 +50,18 @@
 int main(int argc, char** argv) {
   base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
 
-  cr_fuchsia::RegisterProductDataForCrashReporting(kComponentUrl,
-                                                   kCrashProductName);
-
   base::CommandLine::Init(argc, argv);
-  const base::CommandLine* command_line =
+  const base::CommandLine* const command_line =
       base::CommandLine::ForCurrentProcess();
+  const bool enable_cfv2 = command_line->HasSwitch(kEnableCfv2);
+
+  static constexpr base::StringPiece kComponentUrl(
+      "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cm");
+  static constexpr base::StringPiece kComponentUrlCfv1(
+      "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx");
+  cr_fuchsia::RegisterProductDataForCrashReporting(
+      enable_cfv2 ? kComponentUrl : kComponentUrlCfv1, "FuchsiaCastRunner");
+
   CHECK(cr_fuchsia::InitLoggingFromCommandLine(*command_line))
       << "Failed to initialize logging.";
 
@@ -65,6 +72,12 @@
   sys::OutgoingDirectory* const outgoing_directory =
       base::ComponentContextForProcess()->outgoing().get();
 
+  if (!enable_cfv2 && GetConfigBool(kRunCfv1ShimConfigKey)) {
+    // TODO(crbug.com/1065707): Delegate the Runner protocol to the CFv2 runner.
+    NOTIMPLEMENTED();
+    return 1;
+  }
+
   // Publish the fuchsia.sys.Runner implementation for Cast applications.
   cr_fuchsia::WebInstanceHost web_instance_host;
   const bool enable_headless =
@@ -92,8 +105,12 @@
 
   outgoing_directory->ServeFromStartupInfo();
 
-  // TODO(https://crbug.com/952560): Implement Components v2 graceful exit.
   base::RunLoop run_loop;
+  absl::optional<base::ProcessLifecycle> process_lifecycle;
+
+  if (enable_cfv2)
+    process_lifecycle.emplace(run_loop.QuitClosure());
+
   run_loop.Run();
 
   return 0;
diff --git "a/infra/config/generated/builders/ci/Cast Android \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Cast Android \050dbg\051/properties.json"
index 8252801..1a6c408 100644
--- "a/infra/config/generated/builders/ci/Cast Android \050dbg\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Cast Android \050dbg\051/properties.json"
@@ -1,4 +1,56 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Cast Android (dbg)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "cast_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Debug",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Cast Android (dbg)",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "cast_shell_android",
+          "group": "tryserver.chromium.android"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 500,
diff --git a/infra/config/generated/builders/ci/android-marshmallow-x86-rel/properties.json b/infra/config/generated/builders/ci/android-marshmallow-x86-rel/properties.json
index 8252801..72c59c8 100644
--- a/infra/config/generated/builders/ci/android-marshmallow-x86-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-marshmallow-x86-rel/properties.json
@@ -1,4 +1,57 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-marshmallow-x86-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x86_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_wpr_tests",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-marshmallow-x86-rel",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "android-marshmallow-x86-rel",
+          "group": "tryserver.chromium.android"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 500,
diff --git a/infra/config/generated/builders/ci/android-pie-arm64-rel/properties.json b/infra/config/generated/builders/ci/android-pie-arm64-rel/properties.json
index 8252801..2624c51 100644
--- a/infra/config/generated/builders/ci/android-pie-arm64-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-pie-arm64-rel/properties.json
@@ -1,4 +1,56 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-pie-arm64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-pie-arm64-rel",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "android-pie-arm64-rel",
+          "group": "tryserver.chromium.android"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 500,
diff --git a/infra/config/generated/builders/try/android-marshmallow-x86-rel-compilator/properties.json b/infra/config/generated/builders/try/android-marshmallow-x86-rel-compilator/properties.json
index 02aa1bf..f13c7fc4 100644
--- a/infra/config/generated/builders/try/android-marshmallow-x86-rel-compilator/properties.json
+++ b/infra/config/generated/builders/try/android-marshmallow-x86-rel-compilator/properties.json
@@ -1,4 +1,54 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-marshmallow-x86-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x86_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_wpr_tests",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-marshmallow-x86-rel",
+          "project": "chromium"
+        }
+      ],
+      "rts_config": {
+        "condition": "QUICK_RUN_ONLY"
+      }
+    }
+  },
   "$build/code_coverage": {
     "coverage_test_types": [
       "unit",
diff --git a/infra/config/generated/builders/try/android-marshmallow-x86-rel/properties.json b/infra/config/generated/builders/try/android-marshmallow-x86-rel/properties.json
index fbb5b4e..16ad27f3 100644
--- a/infra/config/generated/builders/try/android-marshmallow-x86-rel/properties.json
+++ b/infra/config/generated/builders/try/android-marshmallow-x86-rel/properties.json
@@ -3,6 +3,56 @@
     "compilator": "android-marshmallow-x86-rel-compilator",
     "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
   },
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-marshmallow-x86-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x86_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_wpr_tests",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-marshmallow-x86-rel",
+          "project": "chromium"
+        }
+      ],
+      "rts_config": {
+        "condition": "QUICK_RUN_ONLY"
+      }
+    }
+  },
   "$build/code_coverage": {
     "coverage_test_types": [
       "unit",
diff --git a/infra/config/generated/builders/try/android-pie-arm64-rel-compilator/properties.json b/infra/config/generated/builders/try/android-pie-arm64-rel-compilator/properties.json
index 4dd4b378..357fabad 100644
--- a/infra/config/generated/builders/try/android-pie-arm64-rel-compilator/properties.json
+++ b/infra/config/generated/builders/try/android-pie-arm64-rel-compilator/properties.json
@@ -1,4 +1,53 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-pie-arm64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-pie-arm64-rel",
+          "project": "chromium"
+        }
+      ],
+      "rts_config": {
+        "condition": "QUICK_RUN_ONLY"
+      }
+    }
+  },
   "$build/flakiness": {
     "check_for_flakiness": true
   },
diff --git a/infra/config/generated/builders/try/android-pie-arm64-rel/properties.json b/infra/config/generated/builders/try/android-pie-arm64-rel/properties.json
index d21918d..3ae606b 100644
--- a/infra/config/generated/builders/try/android-pie-arm64-rel/properties.json
+++ b/infra/config/generated/builders/try/android-pie-arm64-rel/properties.json
@@ -3,6 +3,55 @@
     "compilator": "android-pie-arm64-rel-compilator",
     "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
   },
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-pie-arm64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-pie-arm64-rel",
+          "project": "chromium"
+        }
+      ],
+      "rts_config": {
+        "condition": "QUICK_RUN_ONLY"
+      }
+    }
+  },
   "$build/flakiness": {
     "check_for_flakiness": true
   },
diff --git a/infra/config/generated/builders/try/cast_shell_android/properties.json b/infra/config/generated/builders/try/cast_shell_android/properties.json
index d90599c..89a424c 100644
--- a/infra/config/generated/builders/try/cast_shell_android/properties.json
+++ b/infra/config/generated/builders/try/cast_shell_android/properties.json
@@ -1,4 +1,50 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Cast Android (dbg)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "cast_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Debug",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Cast Android (dbg)",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
   "$build/goma": {
     "enable_ats": true,
     "rpc_extra_params": "?prod",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index 5b36cb5..ec72fef 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -455,6 +455,28 @@
 ci.builder(
     name = "Cast Android (dbg)",
     branch_selector = branches.STANDARD_MILESTONE,
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "android",
+                "enable_reclient",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "android",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 32,
+            target_platform = builder_config.target_platform.ANDROID,
+        ),
+        android_config = builder_config.android_config(
+            config = "cast_builder",
+        ),
+        build_gs_bucket = "chromium-android-archive",
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "on_cq",
         short_name = "cst",
@@ -1131,6 +1153,29 @@
 ci.builder(
     name = "android-marshmallow-x86-rel",
     branch_selector = branches.STANDARD_MILESTONE,
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "android",
+                "enable_wpr_tests",
+                "enable_reclient",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "android",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 32,
+            target_platform = builder_config.target_platform.ANDROID,
+        ),
+        android_config = builder_config.android_config(
+            config = "x86_builder",
+        ),
+        build_gs_bucket = "chromium-android-archive",
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "on_cq|x86",
         short_name = "M",
@@ -1217,6 +1262,28 @@
 ci.builder(
     name = "android-pie-arm64-rel",
     branch_selector = branches.STANDARD_MILESTONE,
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "android",
+                "enable_reclient",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "android",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.ANDROID,
+        ),
+        android_config = builder_config.android_config(
+            config = "main_builder",
+        ),
+        build_gs_bucket = "chromium-android-archive",
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "on_cq",
         short_name = "P",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 51d4f77..50bb408 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -258,6 +258,14 @@
 
 try_.orchestrator_builder(
     name = "android-marshmallow-x86-rel",
+    mirrors = [
+        "ci/android-marshmallow-x86-rel",
+    ],
+    try_settings = builder_config.try_settings(
+        rts_config = builder_config.rts_config(
+            condition = builder_config.rts_condition.QUICK_RUN_ONLY,
+        ),
+    ),
     check_for_flakiness = True,
     compilator = "android-marshmallow-x86-rel-compilator",
     branch_selector = branches.STANDARD_MILESTONE,
@@ -347,6 +355,14 @@
 
 try_.orchestrator_builder(
     name = "android-pie-arm64-rel",
+    mirrors = [
+        "ci/android-pie-arm64-rel",
+    ],
+    try_settings = builder_config.try_settings(
+        rts_config = builder_config.rts_config(
+            condition = builder_config.rts_condition.QUICK_RUN_ONLY,
+        ),
+    ),
     compilator = "android-pie-arm64-rel-compilator",
     check_for_flakiness = True,
     branch_selector = branches.STANDARD_MILESTONE,
@@ -602,6 +618,9 @@
 try_.builder(
     name = "cast_shell_android",
     branch_selector = branches.STANDARD_MILESTONE,
+    mirrors = [
+        "ci/Cast Android (dbg)",
+    ],
     builderless = not settings.is_main,
     main_list_view = "try",
     tryjob = try_.job(),
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 55bdf27..563733e3 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -320,21 +320,21 @@
      std::size(kStartSurfaceOneHourHideShortcutsReturnToRecentTab), nullptr},
 };
 
-const FeatureEntry::FeatureParam kFREDefaultPromoTestingDefaultDelay[] = {
+const FeatureEntry::FeatureParam kFREDefaultBrowserPromoDefaultDelay[] = {
     {kFREDefaultBrowserPromoParam, kFREDefaultBrowserPromoDefaultDelayParam}};
-const FeatureEntry::FeatureParam kFREDefaultPromoTestingOnly[] = {
+const FeatureEntry::FeatureParam kFREDefaultBrowserPromoFirstRunOnly[] = {
     {kFREDefaultBrowserPromoParam, kFREDefaultBrowserPromoFirstRunOnlyParam}};
-const FeatureEntry::FeatureParam kFREDefaultPromoTestingShortDelay[] = {
+const FeatureEntry::FeatureParam kFREDefaultBrowserPromoShortDelay[] = {
     {kFREDefaultBrowserPromoParam, kFREDefaultBrowserPromoShortDelayParam}};
-const FeatureEntry::FeatureVariation kFREDefaultPromoTestingVariations[] = {
+const FeatureEntry::FeatureVariation kFREDefaultBrowserPromoVariations[] = {
     {"Wait 14 days after FRE default browser promo",
-     kFREDefaultPromoTestingDefaultDelay,
-     std::size(kFREDefaultPromoTestingDefaultDelay), nullptr},
-    {"FRE default browser promo only", kFREDefaultPromoTestingOnly,
-     std::size(kFREDefaultPromoTestingOnly), nullptr},
+     kFREDefaultBrowserPromoDefaultDelay,
+     std::size(kFREDefaultBrowserPromoDefaultDelay), nullptr},
+    {"FRE default browser promo only", kFREDefaultBrowserPromoFirstRunOnly,
+     std::size(kFREDefaultBrowserPromoFirstRunOnly), nullptr},
     {"Wait 3 days after FRE default browser promo",
-     kFREDefaultPromoTestingShortDelay,
-     std::size(kFREDefaultPromoTestingShortDelay), nullptr},
+     kFREDefaultBrowserPromoShortDelay,
+     std::size(kFREDefaultBrowserPromoShortDelay), nullptr},
 };
 
 const FeatureEntry::FeatureParam kNewMICEFREWithUMADialog[] = {
@@ -722,11 +722,11 @@
                                     kDiscoverFeedTopSyncPromoVariations,
                                     "EnableDiscoverFeedTopSyncPromo")},
     {"enable-fre-default-browser-screen-testing",
-     flag_descriptions::kEnableFREDefaultBrowserScreenTestingName,
-     flag_descriptions::kEnableFREDefaultBrowserScreenTestingDescription,
+     flag_descriptions::kEnableFREDefaultBrowserPromoScreenName,
+     flag_descriptions::kEnableFREDefaultBrowserPromoScreenDescription,
      flags_ui::kOsIos,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(kEnableFREDefaultBrowserScreenTesting,
-                                    kFREDefaultPromoTestingVariations,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(kEnableFREDefaultBrowserPromoScreen,
+                                    kFREDefaultBrowserPromoVariations,
                                     kIOSMICeAndDefaultBrowserTrialName)},
     {"enable-discover-feed-shorter-cache",
      flag_descriptions::kEnableDiscoverFeedShorterCacheName,
@@ -832,6 +832,10 @@
      flag_descriptions::kEnhancedProtectionName,
      flag_descriptions::kEnhancedProtectionDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(safe_browsing::kEnhancedProtection)},
+    {"enable-enhanced-safe-browsing-phase-2",
+     flag_descriptions::kEnhancedProtectionPhase2Name,
+     flag_descriptions::kEnhancedProtectionPhase2Description, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(safe_browsing::kEnhancedProtectionPhase2IOS)},
     {"context-menu-phase2-screenshot",
      flag_descriptions::kWebViewNativeContextMenuPhase2ScreenshotName,
      flag_descriptions::kWebViewNativeContextMenuPhase2ScreenshotDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 6614c5bf..defa479 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -230,11 +230,11 @@
     "When enabled the discover feed will optimize the request of resources "
     "coming from the server.";
 
-const char kEnableFREDefaultBrowserScreenTestingName[] =
-    "Enable FRE default browser screen testing";
-const char kEnableFREDefaultBrowserScreenTestingDescription[] =
-    "This test display the FRE default browser screen and other default "
-    "browser promo depending on experiment.";
+const char kEnableFREDefaultBrowserPromoScreenName[] =
+    "Enable FRE default browser screen";
+const char kEnableFREDefaultBrowserPromoScreenDescription[] =
+    "Display the FRE default browser screen and other default browser promo "
+    "depending on experiment.";
 
 const char kEnableFaviconForPasswordsName[] =
     "Enable favicons for the Password Manager and for the Credential Provider "
@@ -290,6 +290,11 @@
 const char kEnhancedProtectionDescription[] =
     "Allows users to opt-in to Enhanced Safe Browsing";
 
+const char kEnhancedProtectionPhase2Name[] =
+    "Enable Enhanced Safe Browsing Phase 2";
+const char kEnhancedProtectionPhase2Description[] =
+    "Enable additional improvements related to Enhanced Safe Browsing";
+
 const char kExpandedTabStripName[] = "Enable expanded tabstrip";
 const char kExpandedTabStripDescription[] =
     "Enables the new expanded tabstrip. Activated by swiping down the tabstrip"
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index c058597..fd02ef7b 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -179,6 +179,11 @@
 extern const char kEnhancedProtectionName[];
 extern const char kEnhancedProtectionDescription[];
 
+// Title and description for the flag to enable kEnhancedProtectionPhase2
+// flag on iOS.
+extern const char kEnhancedProtectionPhase2Name[];
+extern const char kEnhancedProtectionPhase2Description[];
+
 // Title and description for the flag to enable address verification support in
 // autofill address save prompts.
 extern const char kEnableAutofillAddressSavePromptAddressVerificationName[];
@@ -219,8 +224,8 @@
 
 // Title and description for the flag to test the FRE default browser promo
 // experiment.
-extern const char kEnableFREDefaultBrowserScreenTestingName[];
-extern const char kEnableFREDefaultBrowserScreenTestingDescription[];
+extern const char kEnableFREDefaultBrowserPromoScreenName[];
+extern const char kEnableFREDefaultBrowserPromoScreenDescription[];
 
 // Title and description for the flag to enable FRE UI module.
 extern const char kEnableFREUIModuleIOSName[];
diff --git a/ios/chrome/browser/reading_list/reading_list_distiller_page.mm b/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
index 5ae820e..929e5abc 100644
--- a/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
+++ b/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
@@ -36,8 +36,8 @@
 // This script retrieve the href parameter of the <link rel="amphtml"> element
 // of the page if it exists. If it does not exist, it returns the src of the
 // first iframe of the page.
-const char* kGetIframeURLJavaScript =
-    "(() => {"
+const char16_t* kGetIframeURLJavaScript =
+    u"(() => {"
     "  var link = document.evaluate('//link[@rel=\"amphtml\"]',"
     "                               document,"
     "                               null,"
@@ -49,8 +49,8 @@
     "  return document.getElementsByTagName('iframe')[0].src;"
     "})()";
 
-const char* kWikipediaWorkaround =
-    "(() => {"
+const char16_t* kWikipediaWorkaround =
+    u"(() => {"
     "  var s = document.createElement('style');"
     "  s.innerHTML='.client-js .collapsible-block { display: block }';"
     "  document.head.appendChild(s);"
diff --git a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
index 27034fe..25da579 100644
--- a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
@@ -470,7 +470,8 @@
 
 // Tests turning on sync for an account different from the one that is
 // currently signed in.
-- (void)testSignInWithOneAccountStartSyncWithAnotherAccount {
+// TODO(crbug.com/1328822): flaky.
+- (void)DISABLED_testSignInWithOneAccountStartSyncWithAnotherAccount {
   FakeChromeIdentity* fakeIdentity1 = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity1];
   FakeChromeIdentity* fakeIdentity2 = [FakeChromeIdentity fakeIdentity2];
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 4ae9a663..8b7a153 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -389,7 +389,7 @@
 // If the user says no during the FRE, then they should be re-prompted at the
 // end of the FRE.
 // TODO(crbug.com/1282047): Re-enable when fixed.
-- (void)testSignInScreenUIWhenForcedByPolicy {
+- (void)DISABLED_testSignInScreenUIWhenForcedByPolicy {
   AppLaunchConfiguration configToSetPolicy = self.appConfigurationForTestCase;
 
   // Configure the policy to force sign-in.
@@ -553,7 +553,7 @@
 
 // Tests that the FRE sign in screen is not displayed when sign in is disabled
 // by policy.
-- (void)testSignInDisabled {
+- (void)DISABLED_testSignInDisabled {
   AppLaunchConfiguration config = self.appConfigurationForTestCase;
 
   // Configure the policy to disable SignIn.
@@ -650,7 +650,7 @@
 
 // Checks that the user is signed in and that sync is turned on after the user
 // chooses to turn on sync.
-- (void)testSignInAndTurnOnSync {
+- (void)DISABLED_testSignInAndTurnOnSync {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -676,7 +676,7 @@
 
 // Checks that pressing "No thanks" on sign-in & sync screen doesn't sign in the
 // user and doesn't sync.
-- (void)testNoSignInNoSync {
+- (void)DISABLED_testNoSignInNoSync {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -704,7 +704,7 @@
 // The browser should only be signed in temporarily while the advanced settings
 // prompt is opened and then signed out when the user selects "No thanks".
 // Sync is also turned off.
-- (void)testAdvancedSettingsSignoutSyncOff {
+- (void)DISABLED_testAdvancedSettingsSignoutSyncOff {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -747,7 +747,7 @@
 // If browser is already signed in and the user opens the advanced settings then
 // selects "No thanks", the user should stay signed in, but sync should be
 // turned off.
-- (void)testAdvancedSettingsSignedInSyncOff {
+- (void)DISABLED_testAdvancedSettingsSignedInSyncOff {
   // Sign-in browser.
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity enableSync:NO];
@@ -806,7 +806,7 @@
 
 // Checks that sync is turned on after the user chose to turn on sync in the
 // advanced sync settings screen and that the correct sync options are selected.
-- (void)testCustomSyncOn {
+- (void)DISABLED_testCustomSyncOn {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -868,7 +868,7 @@
 }
 
 // Checks that the user is signed in, but no sync options is selected.
-- (void)testCustomSyncOff {
+- (void)DISABLED_testCustomSyncOff {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -941,7 +941,7 @@
 // Checks that the user is not signed in and that sync is turned off after the
 // user chose to not sign-in even though they selected some sync options in the
 // advanced sync settings screen.
-- (void)testCustomSyncSignout {
+- (void)DISABLED_testCustomSyncSignout {
   FakeChromeIdentity* fakeIdentity = [FakeChromeIdentity fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
@@ -1060,7 +1060,7 @@
 
 // Checks that the sync screen doesn't appear when the SyncDisabled policy is
 // enabled.
-- (void)testSyncDisabled {
+- (void)DISABLED_testSyncDisabled {
   policy_test_utils::SetPolicy(true, policy::key::kSyncDisabled);
 
   // Go to the sign-in screen.
diff --git a/ios/chrome/browser/ui/first_run/fre_field_trial.cc b/ios/chrome/browser/ui/first_run/fre_field_trial.cc
index 7c48023e..3b86c1c3 100644
--- a/ios/chrome/browser/ui/first_run/fre_field_trial.cc
+++ b/ios/chrome/browser/ui/first_run/fre_field_trial.cc
@@ -91,9 +91,9 @@
         {NewDefaultBrowserPromoFRE::kShortDelay,
          kFREDefaultBrowserPromoShortDelayParam}};
 
-// Parameter for kEnableFREDefaultBrowserScreenTesting feature.
+// Parameter for kEnableFREDefaultBrowserPromoScreen feature.
 constexpr base::FeatureParam<NewDefaultBrowserPromoFRE>
-    kNewDefaultBrowserPromoFREParam{&kEnableFREDefaultBrowserScreenTesting,
+    kNewDefaultBrowserPromoFREParam{&kEnableFREDefaultBrowserPromoScreen,
                                     kFREDefaultBrowserPromoParam,
                                     NewDefaultBrowserPromoFRE::kDefaultDelay,
                                     &kNewDefaultBrowserPromoFREOptions};
@@ -143,7 +143,7 @@
 
 NewDefaultBrowserPromoFRE GetFREDefaultBrowserScreenPromoFRE() {
   if (base::FeatureList::IsEnabled(kEnableFREUIModuleIOS) &&
-      base::FeatureList::IsEnabled(kEnableFREDefaultBrowserScreenTesting)) {
+      base::FeatureList::IsEnabled(kEnableFREDefaultBrowserPromoScreen)) {
     return kNewDefaultBrowserPromoFREParam.Get();
   }
   return NewDefaultBrowserPromoFRE::kDisabled;
@@ -270,7 +270,7 @@
         signin::kNewMobileIdentityConsistencyFRE.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
     feature_list->RegisterFieldTrialOverride(
-        kEnableFREDefaultBrowserScreenTesting.name,
+        kEnableFREDefaultBrowserPromoScreen.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
     return kCurrentTrialVersion;
   }
@@ -282,7 +282,7 @@
         signin::kNewMobileIdentityConsistencyFRE.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
     feature_list->RegisterFieldTrialOverride(
-        kEnableFREDefaultBrowserScreenTesting.name,
+        kEnableFREDefaultBrowserPromoScreen.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
     return kCurrentTrialVersion;
   }
@@ -296,7 +296,7 @@
         signin::kNewMobileIdentityConsistencyFRE.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
     feature_list->RegisterFieldTrialOverride(
-        kEnableFREDefaultBrowserScreenTesting.name,
+        kEnableFREDefaultBrowserPromoScreen.name,
         base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
     return kCurrentTrialVersion;
   }
@@ -307,7 +307,7 @@
         signin::kNewMobileIdentityConsistencyFRE.name,
         base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
     feature_list->RegisterFieldTrialOverride(
-        kEnableFREDefaultBrowserScreenTesting.name,
+        kEnableFREDefaultBrowserPromoScreen.name,
         base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial.get());
   }
   return kCurrentTrialVersion;
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 7fce2e0..5f233a2 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -13,8 +13,8 @@
 const base::Feature kSharedHighlightingIOS{"SharedHighlightingIOS",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kEnableFREDefaultBrowserScreenTesting{
-    "EnableFREDefaultBrowserScreenTesting", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableFREDefaultBrowserPromoScreen{
+    "EnableFREDefaultBrowserPromoScreen", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kEnableFREUIModuleIOS{"EnableFREUIModuleIOSV3",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index ce7097e..3d34efea 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -22,7 +22,7 @@
 // Feature flag for testing the 'default browser' screen in FRE and different
 // experiments to suggest the users to update the default browser in the
 // Settings.app.
-extern const base::Feature kEnableFREDefaultBrowserScreenTesting;
+extern const base::Feature kEnableFREDefaultBrowserPromoScreen;
 
 // Feature flag that enables using the FRE UI module to show first run screens.
 extern const base::Feature kEnableFREUIModuleIOS;
diff --git a/ios/chrome/browser/voice/text_to_speech_parser.mm b/ios/chrome/browser/voice/text_to_speech_parser.mm
index 62650cb1..8e23e39 100644
--- a/ios/chrome/browser/voice/text_to_speech_parser.mm
+++ b/ios/chrome/browser/voice/text_to_speech_parser.mm
@@ -119,7 +119,7 @@
     return;
   }
 
-  std::string script = base::SysNSStringToUTF8(tts_extraction_script);
+  std::u16string script = base::SysNSStringToUTF16(tts_extraction_script);
   web_frame->ExecuteJavaScript(
       script, BindOnce(^(const base::Value* value) {
         if (value->is_string()) {
diff --git a/ios/chrome/browser/web_state_list/view_source_browser_agent.mm b/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
index 4866538..d1d32e35 100644
--- a/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
+++ b/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
@@ -29,7 +29,7 @@
   DCHECK(web_state);
 
   web::WebFrame* web_frame = web::GetMainFrame(web_state);
-  static const char kScript[] = "document.documentElement.outerHTML;";
+  static const char16_t kScript[] = u"document.documentElement.outerHTML;";
 
   web_frame->ExecuteJavaScript(
       kScript,
diff --git a/ios/chrome/common/ui/promo_style/promo_style_view_controller.mm b/ios/chrome/common/ui/promo_style/promo_style_view_controller.mm
index 6c0e4f40..ca71420 100644
--- a/ios/chrome/common/ui/promo_style/promo_style_view_controller.mm
+++ b/ios/chrome/common/ui/promo_style/promo_style_view_controller.mm
@@ -326,7 +326,7 @@
     BOOL isScrolledToBottom = [self isScrolledToBottom];
     self.separator.hidden = isScrolledToBottom;
     if (self.didReachBottom || isScrolledToBottom) {
-      [self updateActionButtonsAndPushUpScrollView];
+      [self updateActionButtonsAndPushUpScrollViewIfMandatory];
     } else {
       [self setReadMoreText];
     }
@@ -747,16 +747,17 @@
   BOOL isScrolledToBottom = [self isScrolledToBottom];
   self.separator.hidden = isScrolledToBottom;
   if (self.scrollToEndMandatory && !self.didReachBottom && isScrolledToBottom) {
-    [self updateActionButtonsAndPushUpScrollView];
+    [self updateActionButtonsAndPushUpScrollViewIfMandatory];
   }
 }
 
 // This method should be called right before the view is scrolled to the bottom.
 // It updates the primary button's label and adds secondary and/or tertiary
 // buttons, and as a result, pushing the scroll view up by updating the bottom
-// offset of the scroll view and scroll to the new offset. It also sets
-// |didReachBottom| to YES.
-- (void)updateActionButtonsAndPushUpScrollView {
+// offset of the scroll view and scroll to the new offset if the change in
+// action buttons is triggered by a scroll in a view that sets
+// `self.scrollToEndMandatory=YES`. It also sets `self.didReachBottom` to YES.
+- (void)updateActionButtonsAndPushUpScrollViewIfMandatory {
   [self.primaryActionButton setAttributedTitle:nil
                                       forState:UIControlStateNormal];
   [self.primaryActionButton setTitle:self.primaryActionString
@@ -813,7 +814,7 @@
     [NSLayoutConstraint
         activateConstraints:self.buttonsVerticalAnchorConstraints];
   }
-  if (![self isScrolledToBottom]) {
+  if (self.scrollToEndMandatory && ![self isScrolledToBottom]) {
     [self.scrollView setContentOffset:updatedBottomOffset animated:YES];
   }
   self.didReachBottom = YES;
@@ -832,7 +833,7 @@
     if (targetOffset.y < self.scrollViewBottomOffsetY + 1) {
       [self.scrollView setContentOffset:targetOffset animated:YES];
     } else {
-      [self updateActionButtonsAndPushUpScrollView];
+      [self updateActionButtonsAndPushUpScrollViewIfMandatory];
     }
   } else if ([self.delegate
                  respondsToSelector:@selector(didTapPrimaryActionButton)]) {
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 95a7094..c5116a56 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -996,7 +996,7 @@
       web::GetMainFrame(chrome_test_util::GetCurrentWebState());
 
   if (web_frame) {
-    std::string script = base::SysNSStringToUTF8(javaScript);
+    std::u16string script = base::SysNSStringToUTF16(javaScript);
     web_frame->ExecuteJavaScript(
         script, base::BindOnce(^(const base::Value* value, bool error) {
           handlerCalled = true;
diff --git a/ios/web/js_messaging/web_frame_impl.h b/ios/web/js_messaging/web_frame_impl.h
index c7fd85f3..8d7c231e9 100644
--- a/ios/web/js_messaging/web_frame_impl.h
+++ b/ios/web/js_messaging/web_frame_impl.h
@@ -67,11 +67,11 @@
       base::OnceCallback<void(const base::Value*)> callback,
       base::TimeDelta timeout) override;
 
-  bool ExecuteJavaScript(const std::string& script) override;
+  bool ExecuteJavaScript(const std::u16string& script) override;
   bool ExecuteJavaScript(
-      const std::string& script,
+      const std::u16string& script,
       base::OnceCallback<void(const base::Value*)> callback) override;
-  bool ExecuteJavaScript(const std::string& script,
+  bool ExecuteJavaScript(const std::u16string& script,
                          ExecuteJavaScriptCallbackWithError callback) override;
 
   // WebFrameContentWorldAPI:
diff --git a/ios/web/js_messaging/web_frame_impl.mm b/ios/web/js_messaging/web_frame_impl.mm
index faa98533..10bc355 100644
--- a/ios/web/js_messaging/web_frame_impl.mm
+++ b/ios/web/js_messaging/web_frame_impl.mm
@@ -256,13 +256,13 @@
   return called;
 }
 
-bool WebFrameImpl::ExecuteJavaScript(const std::string& script) {
+bool WebFrameImpl::ExecuteJavaScript(const std::u16string& script) {
   return ExecuteJavaScript(script,
                            base::DoNothingAs<void(const base::Value*)>());
 }
 
 bool WebFrameImpl::ExecuteJavaScript(
-    const std::string& script,
+    const std::u16string& script,
     base::OnceCallback<void(const base::Value*)> callback) {
   ExecuteJavaScriptCallbackWithError callback_with_error =
       ExecuteJavaScriptCallbackAdapter(std::move(callback));
@@ -271,7 +271,7 @@
 }
 
 bool WebFrameImpl::ExecuteJavaScript(
-    const std::string& script,
+    const std::u16string& script,
     ExecuteJavaScriptCallbackWithError callback) {
   DCHECK(frame_info_);
 
@@ -279,7 +279,7 @@
     return false;
   }
 
-  NSString* ns_script = base::SysUTF8ToNSString(script);
+  NSString* ns_script = base::SysUTF16ToNSString(script);
   __block auto internal_callback = std::move(callback);
   void (^completion_handler)(id, NSError*) = ^void(id value, NSError* error) {
     if (error) {
diff --git a/ios/web/js_messaging/web_frame_impl_unittest.mm b/ios/web/js_messaging/web_frame_impl_unittest.mm
index db1a2ff..9db06c7 100644
--- a/ios/web/js_messaging/web_frame_impl_unittest.mm
+++ b/ios/web/js_messaging/web_frame_impl_unittest.mm
@@ -464,13 +464,13 @@
                          /*is_main_frame=*/true, security_origin,
                          &fake_web_state);
 
-  EXPECT_TRUE(web_frame.ExecuteJavaScript(base::SysNSStringToUTF8(script)));
+  EXPECT_TRUE(web_frame.ExecuteJavaScript(base::SysNSStringToUTF16(script)));
 
   WebFrameImpl web_frame2([[WKFrameInfo alloc] init], kFrameId,
                           /*is_main_frame=*/false, security_origin,
                           &fake_web_state);
   // Executing arbitrary code on an iframe should return false.
-  EXPECT_FALSE(web_frame2.ExecuteJavaScript(base::SysNSStringToUTF8(script)));
+  EXPECT_FALSE(web_frame2.ExecuteJavaScript(base::SysNSStringToUTF16(script)));
 }
 
 // Tests that the WebFrame can execute arbitrary JavaScript given
@@ -489,7 +489,7 @@
                          &fake_web_state);
 
   EXPECT_TRUE(
-      web_frame.ExecuteJavaScript(base::SysNSStringToUTF8(script),
+      web_frame.ExecuteJavaScript(base::SysNSStringToUTF16(script),
                                   base::BindOnce(^(const base::Value* value){
                                   })));
 
@@ -498,7 +498,7 @@
                           &fake_web_state);
 
   EXPECT_FALSE(
-      web_frame2.ExecuteJavaScript(base::SysNSStringToUTF8(script),
+      web_frame2.ExecuteJavaScript(base::SysNSStringToUTF16(script),
                                    base::BindOnce(^(const base::Value* value){
                                    })));
 }
diff --git a/ios/web/public/js_messaging/web_frame.h b/ios/web/public/js_messaging/web_frame.h
index 653885a..f2ddeea 100644
--- a/ios/web/public/js_messaging/web_frame.h
+++ b/ios/web/public/js_messaging/web_frame.h
@@ -72,13 +72,13 @@
       base::TimeDelta timeout) = 0;
 
   // Executes the given |script| and returns whether the script was run.
-  virtual bool ExecuteJavaScript(const std::string& script) = 0;
+  virtual bool ExecuteJavaScript(const std::u16string& script) = 0;
 
   // Executes the given |script| and returns whether the script was run.
   // If the script is successfully executed, |callback| is called with
   // the result.
   virtual bool ExecuteJavaScript(
-      const std::string& script,
+      const std::u16string& script,
       base::OnceCallback<void(const base::Value*)> callback) = 0;
 
   using ExecuteJavaScriptCallbackWithError =
@@ -89,7 +89,7 @@
   // bool parameter in the |callback| is used to signal that an error
   // during the execution of the |script| occurred.
   virtual bool ExecuteJavaScript(
-      const std::string& script,
+      const std::u16string& script,
       ExecuteJavaScriptCallbackWithError callback) = 0;
 
   // Returns the WebFrameInternal instance for this object.
diff --git a/ios/web/test/fakes/fake_web_frame_impl.cc b/ios/web/test/fakes/fake_web_frame_impl.cc
index fcf466a4..c0787cdf 100644
--- a/ios/web/test/fakes/fake_web_frame_impl.cc
+++ b/ios/web/test/fakes/fake_web_frame_impl.cc
@@ -144,18 +144,18 @@
   return CallJavaScriptFunction(name, parameters, std::move(callback), timeout);
 }
 
-bool FakeWebFrameImpl::ExecuteJavaScript(const std::string& script) {
+bool FakeWebFrameImpl::ExecuteJavaScript(const std::u16string& script) {
   return false;
 }
 
 bool FakeWebFrameImpl::ExecuteJavaScript(
-    const std::string& script,
+    const std::u16string& script,
     base::OnceCallback<void(const base::Value*)> callback) {
   return false;
 }
 
 bool FakeWebFrameImpl::ExecuteJavaScript(
-    const std::string& script,
+    const std::u16string& script,
     base::OnceCallback<void(const base::Value*, bool)> callback) {
   return false;
 }
diff --git a/ios/web/test/fakes/fake_web_frame_impl.h b/ios/web/test/fakes/fake_web_frame_impl.h
index e8f8b86..f21d3de 100644
--- a/ios/web/test/fakes/fake_web_frame_impl.h
+++ b/ios/web/test/fakes/fake_web_frame_impl.h
@@ -41,12 +41,12 @@
       const std::vector<base::Value>& parameters,
       base::OnceCallback<void(const base::Value*)> callback,
       base::TimeDelta timeout) override;
-  bool ExecuteJavaScript(const std::string& script) override;
+  bool ExecuteJavaScript(const std::u16string& script) override;
   bool ExecuteJavaScript(
-      const std::string& script,
+      const std::u16string& script,
       base::OnceCallback<void(const base::Value*)> callback) override;
   bool ExecuteJavaScript(
-      const std::string& script,
+      const std::u16string& script,
       base::OnceCallback<void(const base::Value*, bool)> callback) override;
 
   // FakeWebFrame:
diff --git a/net/cookies/cookie_deletion_info.h b/net/cookies/cookie_deletion_info.h
index 5158f45..c066f13 100644
--- a/net/cookies/cookie_deletion_info.h
+++ b/net/cookies/cookie_deletion_info.h
@@ -141,9 +141,9 @@
   // Used only for testing purposes.
   absl::optional<std::string> value_for_testing;
 
-  // Cookie partition keychain. Partitioned cookies are not deleted if their
-  // partition key is not in the keychain. By default, it clears cookies in all
-  // partitions.
+  // Cookie partition collection. Partitioned cookies are not deleted if their
+  // partition key is not in the collection. By default, it clears cookies in
+  // all partitions.
   CookiePartitionKeyCollection cookie_partition_key_collection =
       CookiePartitionKeyCollection::ContainsAll();
 };
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 2e15f94..582f8ff 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -3862,8 +3862,9 @@
     case SecureDnsMode::kSecure:
       DCHECK(!allow_cache ||
              out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP);
-      DCHECK(dns_client_->CanUseSecureDnsTransactions());
-      if (dns_tasks_allowed)
+      // Policy misconfiguration can put us in secure DNS mode without any DoH
+      // servers to query. See https://crbug.com/1326526.
+      if (dns_tasks_allowed && dns_client_->CanUseSecureDnsTransactions())
         out_tasks->push_back(TaskType::SECURE_DNS);
       break;
     case SecureDnsMode::kAutomatic:
diff --git a/net/quic/platform/impl/DEPS b/net/quic/platform/impl/DEPS
new file mode 100644
index 0000000..2c12fec5
--- /dev/null
+++ b/net/quic/platform/impl/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # Allow string_view.h since absl::string_view is widely used in QUICHE API.
+  "+third_party/abseil-cpp/absl/strings",
+]
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 72efabc..bf8ec99 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5865,21 +5865,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5081.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -5892,7 +5892,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "isolate_profile_data": true,
@@ -6030,21 +6030,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -6056,7 +6056,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "args": [
@@ -6176,21 +6176,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -6202,7 +6202,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 9803cb05a..7790f1f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -87946,21 +87946,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5081.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -87968,7 +87968,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "isolate_profile_data": true,
@@ -88081,28 +88081,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "args": [
@@ -88202,28 +88202,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "isolate_profile_data": true,
@@ -89561,20 +89561,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5081.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -89588,7 +89588,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "merge": {
@@ -89726,20 +89726,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -89752,7 +89752,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "args": [
@@ -89872,20 +89872,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -89898,7 +89898,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "merge": {
@@ -91394,20 +91394,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5081.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -91421,7 +91421,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "merge": {
@@ -91559,20 +91559,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -91585,7 +91585,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "args": [
@@ -91705,20 +91705,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5081.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -91731,7 +91731,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       },
       {
         "merge": {
@@ -92466,20 +92466,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5081.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5082.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5081.0",
-              "revision": "version:104.0.5081.0"
+              "location": "lacros_version_skew_tests_v104.0.5082.0",
+              "revision": "version:104.0.5082.0"
             }
           ],
           "dimension_sets": [
@@ -92492,7 +92492,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5081.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5082.0"
       }
     ]
   },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 2dce507..c2ff8b3e 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5081.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5082.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 104.0.5081.0',
+    'identifier': 'Lacros version skew testing ash 104.0.5082.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v104.0.5081.0',
-          'revision': 'version:104.0.5081.0',
+          'location': 'lacros_version_skew_tests_v104.0.5082.0',
+          'revision': 'version:104.0.5082.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d2f50fb..6ebfb36 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1245,6 +1245,9 @@
             "experiments": [
                 {
                     "name": "Enabled",
+                    "params": {
+                        "remove_inaccessible_fields_on_startup": "false"
+                    },
                     "enable_features": [
                         "AutofillRemoveInaccessibleProfileValues"
                     ]
@@ -1714,6 +1717,27 @@
             ]
         }
     ],
+    "BlinkSchedulerPrioritizeRenderingAfterInput": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20220523",
+                    "enable_features": [
+                        "PrioritizeCompositingAfterInput"
+                    ]
+                }
+            ]
+        }
+    ],
     "BrowserSwitcherNoneIsGreylist": [
         {
             "platforms": [
@@ -3923,6 +3947,31 @@
             ]
         }
     ],
+    "GreedyWakeupsStrategy": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "strategy": "exponential-wakeups"
+                    },
+                    "enable_features": [
+                        "WakeUpStrategyFeature"
+                    ]
+                }
+            ]
+        }
+    ],
     "GridAndGroupAndroidM5": [
         {
             "platforms": [
@@ -4693,6 +4742,30 @@
             ]
         }
     ],
+    "JourneysArPlRuZh": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20220502",
+                    "params": {
+                        "JourneysLocaleOrLanguageAllowlist": "de,en,en-AU,en-CA,en-GB,en-US,es,fr,it,nl,pt,tr,ar,pl,ru,zh",
+                        "supported_locales": "de,en,en-AU,en-CA,en-GB,en-US,es,fr,it,nl,pt,tr,ar,pl,ru,zh"
+                    },
+                    "enable_features": [
+                        "Journeys",
+                        "PageEntitiesPageContentAnnotations"
+                    ]
+                }
+            ]
+        }
+    ],
     "KeyPinningComponentUpdater": [
         {
             "platforms": [
@@ -5245,6 +5318,26 @@
             ]
         }
     ],
+    "NaviOnboarding": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "NoBackground-ForYou1",
+                    "params": {
+                        "new-user-modules": "nux-google-apps,nux-set-as-default,signin-view",
+                        "onboarding-group": "NoBackgroundSynthetic-ForYou1",
+                        "returning-user-modules": "nux-set-as-default"
+                    },
+                    "enable_features": [
+                        "NuxOnboarding"
+                    ]
+                }
+            ]
+        }
+    ],
     "NearbySharingOnePageOnboarding": [
         {
             "platforms": [
@@ -5480,9 +5573,9 @@
                     "name": "AndroidExperiments",
                     "params": {
                         "OmniboxDynamicMaxAutocompleteIncreasedLimit": "15",
-                        "OmniboxLocalZeroSuggestAgeThreshold": "14",
+                        "OmniboxLocalZeroSuggestAgeThreshold": "30",
                         "OmniboxMaxURLMatches": "8",
-                        "UIMaxAutocompleteMatches": "15",
+                        "UIMaxAutocompleteMatches": "10",
                         "ZeroSuggestCacheCounterfactual": "true",
                         "ZeroSuggestCacheDurationSec": "180",
                         "ZeroSuggestPrefetchBypassCache": "true"
@@ -5826,30 +5919,6 @@
             ]
         }
     ],
-    "PageContentAnnotationsDesktopNonstableM102Plus": [
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20220502",
-                    "params": {
-                        "JourneysLocaleOrLanguageAllowlist": "de,en,en-AU,en-CA,en-GB,en-US,es,fr,it,nl,pt,tr,ar,pl,ru,zh",
-                        "supported_locales": "de,en,en-AU,en-CA,en-GB,en-US,es,fr,it,nl,pt,tr,ar,pl,ru,zh"
-                    },
-                    "enable_features": [
-                        "Journeys",
-                        "PageEntitiesPageContentAnnotations"
-                    ]
-                }
-            ]
-        }
-    ],
     "PageEntitiesModelBypassFilters": [
         {
             "platforms": [
diff --git a/third_party/blink/common/privacy_budget/BUILD.gn b/third_party/blink/common/privacy_budget/BUILD.gn
index 35d2a792..0e8611c 100644
--- a/third_party/blink/common/privacy_budget/BUILD.gn
+++ b/third_party/blink/common/privacy_budget/BUILD.gn
@@ -20,7 +20,6 @@
     "identifiability_metrics.cc",
     "identifiability_sample_collector.cc",
     "identifiability_sample_collector_test_utils.h",
-    "identifiability_study_document_created.cc",
     "identifiability_study_settings.cc",
     "identifiable_token_builder.cc",
   ]
diff --git a/third_party/blink/common/privacy_budget/identifiability_study_document_created.cc b/third_party/blink/common/privacy_budget/identifiability_study_document_created.cc
deleted file mode 100644
index 3881883..0000000
--- a/third_party/blink/common/privacy_budget/identifiability_study_document_created.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h"
-
-#include "services/metrics/public/cpp/metrics_export.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
-
-namespace blink {
-
-IdentifiabilityStudyDocumentCreated::IdentifiabilityStudyDocumentCreated(
-    ukm::SourceIdObj source_id)
-    : source_id_(source_id.ToInt64()) {}
-
-IdentifiabilityStudyDocumentCreated::IdentifiabilityStudyDocumentCreated(
-    ukm::SourceId source_id)
-    : source_id_(source_id) {}
-
-IdentifiabilityStudyDocumentCreated::~IdentifiabilityStudyDocumentCreated() =
-    default;
-
-IdentifiabilityStudyDocumentCreated&
-IdentifiabilityStudyDocumentCreated::SetNavigationSourceId(
-    ukm::SourceId navigation_source_id) {
-  navigation_source_id_ = navigation_source_id;
-  return *this;
-}
-
-IdentifiabilityStudyDocumentCreated&
-IdentifiabilityStudyDocumentCreated::SetIsMainFrame(bool is_main_frame) {
-  is_main_frame_ = is_main_frame;
-  return *this;
-}
-
-IdentifiabilityStudyDocumentCreated&
-IdentifiabilityStudyDocumentCreated::SetIsCrossSiteFrame(
-    bool is_cross_site_frame) {
-  is_cross_site_frame_ = is_cross_site_frame;
-  return *this;
-}
-
-IdentifiabilityStudyDocumentCreated&
-IdentifiabilityStudyDocumentCreated::SetIsCrossOriginFrame(
-    bool is_cross_origin_frame) {
-  is_cross_origin_frame_ = is_cross_origin_frame;
-  return *this;
-}
-
-void IdentifiabilityStudyDocumentCreated::Record(ukm::UkmRecorder* recorder) {
-  using Metrics = blink::IdentifiableSurface::ReservedSurfaceMetrics;
-  base::flat_map<uint64_t, int64_t> metrics = {
-      {IdentifiableSurface::FromTypeAndToken(
-           blink::IdentifiableSurface::Type::kReservedInternal,
-           Metrics::kDocumentCreated_IsCrossOriginFrame)
-           .ToUkmMetricHash(),
-       is_cross_origin_frame_},
-      {IdentifiableSurface::FromTypeAndToken(
-           blink::IdentifiableSurface::Type::kReservedInternal,
-           Metrics::kDocumentCreated_IsCrossSiteFrame)
-           .ToUkmMetricHash(),
-       is_cross_site_frame_},
-      {IdentifiableSurface::FromTypeAndToken(
-           blink::IdentifiableSurface::Type::kReservedInternal,
-           Metrics::kDocumentCreated_IsMainFrame)
-           .ToUkmMetricHash(),
-       is_main_frame_},
-      {IdentifiableSurface::FromTypeAndToken(
-           blink::IdentifiableSurface::Type::kReservedInternal,
-           Metrics::kDocumentCreated_NavigationSourceId)
-           .ToUkmMetricHash(),
-       navigation_source_id_}};
-
-  recorder->AddEntry(ukm::mojom::UkmEntry::New(
-      source_id_, ukm::builders::Identifiability::kEntryNameHash, metrics));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/public/common/privacy_budget/BUILD.gn b/third_party/blink/public/common/privacy_budget/BUILD.gn
index 1e0d6ea..49a07de 100644
--- a/third_party/blink/public/common/privacy_budget/BUILD.gn
+++ b/third_party/blink/public/common/privacy_budget/BUILD.gn
@@ -18,7 +18,6 @@
     "identifiability_metric_builder.h",
     "identifiability_metrics.h",
     "identifiability_sample_collector.h",
-    "identifiability_study_document_created.h",
     "identifiability_study_settings.h",
     "identifiability_study_settings_provider.h",
     "identifiable_sample.h",
@@ -31,7 +30,6 @@
     ":internal",
     "//base",
     "//services/metrics/public/cpp:metrics_cpp",
-    "//services/metrics/public/cpp:ukm_builders",
     "//services/network/public/cpp:cpp",
     "//third_party/blink/public/common:common_export",
     "//third_party/blink/public/mojom:web_feature_mojo_bindings",
diff --git a/third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h b/third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h
deleted file mode 100644
index 30dd9980..0000000
--- a/third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABILITY_STUDY_DOCUMENT_CREATED_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABILITY_STUDY_DOCUMENT_CREATED_H_
-
-#include "services/metrics/public/cpp/ukm_recorder.h"
-#include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/blink/public/common/common_export.h"
-
-namespace blink {
-
-class BLINK_COMMON_EXPORT IdentifiabilityStudyDocumentCreated {
- public:
-  // Construct an IdentifiabilityStudyDocumentCreated for the given |source_id|.
-  // The source must be known to UKM.
-  explicit IdentifiabilityStudyDocumentCreated(ukm::SourceIdObj source_id);
-
-  // Same as previous constructor, but uses SourceId instead of SourceIdObj.
-  explicit IdentifiabilityStudyDocumentCreated(ukm::SourceId source_id);
-
-  ~IdentifiabilityStudyDocumentCreated();
-
-  // Record collected metrics to `recorder`.
-  void Record(ukm::UkmRecorder* recorder);
-
-  IdentifiabilityStudyDocumentCreated& SetNavigationSourceId(
-      ukm::SourceId navigation_source_id);
-
-  IdentifiabilityStudyDocumentCreated& SetIsMainFrame(bool is_main_frame);
-
-  IdentifiabilityStudyDocumentCreated& SetIsCrossOriginFrame(
-      bool is_cross_origin_frame);
-
-  IdentifiabilityStudyDocumentCreated& SetIsCrossSiteFrame(
-      bool is_cross_site_frame);
-
- private:
-  const ukm::SourceId source_id_;
-  ukm::SourceId navigation_source_id_;
-  bool is_main_frame_;
-  bool is_cross_origin_frame_;
-  bool is_cross_site_frame_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABILITY_STUDY_DOCUMENT_CREATED_H_
diff --git a/third_party/blink/public/common/privacy_budget/identifiable_surface.h b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
index f4e099a0..a1eacf9 100644
--- a/third_party/blink/public/common/privacy_budget/identifiable_surface.h
+++ b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
@@ -11,7 +11,6 @@
 #include <functional>
 #include <tuple>
 
-#include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
 
 namespace blink {
@@ -252,31 +251,13 @@
     kMax = (1 << kTypeBits) - 1
   };
 
-  // These are metrics names of type 0 and are always reported when the study is
-  // enabled.
-  enum class ReservedSurfaceMetrics : uint64_t {
-    kDocumentCreated_IsCrossOriginFrame = 0,
-    kDocumentCreated_IsCrossSiteFrame = 1,
-    kDocumentCreated_IsMainFrame = 2,
-    kDocumentCreated_NavigationSourceId = 3,
-    kMax = kDocumentCreated_NavigationSourceId
-  };
-  static_assert(
-      static_cast<uint64_t>(ReservedSurfaceMetrics::kMax) <
-          std::min(
-              ukm::builders::Identifiability::kGeneratorVersion_926NameHash,
-              ukm::builders::Identifiability::kStudyGeneration_626NameHash),
-      "All the ReservedSurfaceMetrics enum values should be strictly smaller "
-      "than kGeneratorVersion_926NameHash and kStudyGeneration_626NameHash to "
-      "avoid collisions.");
-
   // HTML canvas readback -- bits [0-3] of the 64-bit input are the context type
   // (Type::kCanvasReadback), bits [4-6] are skipped ops, sensitive ops, and
   // partial image ops bits, respectively. The remaining bits are for the canvas
   // operations digest. If the digest wasn't calculated (there's no digest for
   // WebGL, for instance), the digest field is 0.
   enum CanvasTaintBit : uint64_t {
-    // At least one drawing operation didn't update the digest -- this is either
+    // At least one drawing operation didn't update the digest -- this is ether
     // due to performance or resource consumption reasons.
     kSkipped = UINT64_C(0x10),
 
diff --git a/third_party/blink/public/web/web_performance.h b/third_party/blink/public/web/web_performance.h
index c791af5..ed52cebe 100644
--- a/third_party/blink/public/web/web_performance.h
+++ b/third_party/blink/public/web/web_performance.h
@@ -128,6 +128,8 @@
   BLINK_EXPORT double FirstInputOrScrollNotifiedTimestamp() const;
   BLINK_EXPORT absl::optional<base::TimeDelta> FirstInputDelay() const;
   BLINK_EXPORT absl::optional<base::TimeDelta> FirstInputTimestamp() const;
+  BLINK_EXPORT absl::optional<base::TimeTicks>
+  FirstInputTimestampAsMonotonicTime() const;
   BLINK_EXPORT absl::optional<base::TimeDelta> LongestInputDelay() const;
   BLINK_EXPORT absl::optional<base::TimeDelta> LongestInputTimestamp() const;
   BLINK_EXPORT absl::optional<base::TimeDelta> FirstInputProcessingTime() const;
diff --git a/third_party/blink/renderer/core/css/binary_data_font_face_source.cc b/third_party/blink/renderer/core/css/binary_data_font_face_source.cc
index 546c8d5..203696a 100644
--- a/third_party/blink/renderer/core/css/binary_data_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/binary_data_font_face_source.cc
@@ -46,7 +46,8 @@
               font_description.SyntheticItalicAllowed(),
           font_description.GetFontSelectionRequest(),
           font_selection_capabilities, font_description.FontOpticalSizing(),
-          font_description.Orientation(), font_description.VariationSettings(),
+          font_description.TextRendering(), font_description.Orientation(),
+          font_description.VariationSettings(),
           font_description.GetFontPalette()),
       CustomFontData::Create());
 }
diff --git a/third_party/blink/renderer/core/css/css_font_face_source_test.cc b/third_party/blink/renderer/core/css/css_font_face_source_test.cc
index d409742..091aab0 100644
--- a/third_party/blink/renderer/core/css/css_font_face_source_test.cc
+++ b/third_party/blink/renderer/core/css/css_font_face_source_test.cc
@@ -19,7 +19,9 @@
       const FontDescription&,
       const FontSelectionCapabilities&) override {
     return SimpleFontData::Create(FontPlatformData(
-        SkTypeface::MakeDefault(), std::string(), 0, false, false));
+        SkTypeface::MakeDefault(), /* name */ std::string(),
+        /* text_size */ 0, /* synthetic_bold */ false,
+        /* synthetic_italic */ false, TextRenderingMode::kAutoTextRendering));
   }
 
   DummyFontFaceSource() = default;
diff --git a/third_party/blink/renderer/core/css/css_selector_test.cc b/third_party/blink/renderer/core/css/css_selector_test.cc
index eace926..16f79d0 100644
--- a/third_party/blink/renderer/core/css/css_selector_test.cc
+++ b/third_party/blink/renderer/core/css/css_selector_test.cc
@@ -204,7 +204,7 @@
   )HTML");
 
   const CSSSelector& cue_selector =
-      (*sheet.GetRuleSet().CuePseudoRules())[0]->Selector();
+      (*sheet.GetRuleSet().CuePseudoRules())[0].Selector();
   EXPECT_EQ(cue_selector.GetPseudoType(), CSSSelector::kPseudoCue);
 
   const CSSSelectorList* cue_arguments = cue_selector.SelectorList();
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc
index 9758db522..87f9755 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -111,10 +111,43 @@
   return false;
 }
 
-// Sequentially scans a sorted list of RuleSet::LayerInterval and seeks for the
-// cascade layer for a rule (given by its position). SeekLayerOrder() must be
-// called with non-decreasing rule positions, so that we only need to go through
-// the layer list at most once for all SeekLayerOrder() calls.
+// Sequentially scans a sorted list of RuleSet::Interval<T> and seeks
+// for the value for a rule (given by its position). Seek() must be called
+// with non-decreasing rule positions, so that we only need to go
+// through the layer list at most once for all Seek() calls.
+template <class T>
+class Seeker {
+  STACK_ALLOCATED();
+
+ public:
+  explicit Seeker(const HeapVector<RuleSet::Interval<T>>& intervals)
+      : intervals_(intervals), iter_(intervals_.begin()) {}
+
+  const T* Seek(unsigned rule_position) {
+#if DCHECK_IS_ON()
+    DCHECK_GE(rule_position, last_rule_position_);
+    last_rule_position_ = rule_position;
+#endif
+
+    while (iter_ != intervals_.end() &&
+           iter_->start_position <= rule_position) {
+      ++iter_;
+    }
+    if (iter_ == intervals_.begin())
+      return nullptr;
+    return std::prev(iter_)->value;
+  }
+
+ private:
+  const HeapVector<RuleSet::Interval<T>>& intervals_;
+  const RuleSet::Interval<T>* iter_;
+#if DCHECK_IS_ON()
+  unsigned last_rule_position_ = 0;
+#endif
+};
+
+// A wrapper around Seeker<CascadeLayer> that also translates through the layer
+// map.
 class CascadeLayerSeeker {
   STACK_ALLOCATED();
 
@@ -123,25 +156,20 @@
                      Element* vtt_originating_element,
                      const CSSStyleSheet* style_sheet,
                      const RuleSet* rule_set)
-      : layers_(rule_set->LayerIntervals()),
-        layer_iter_(layers_.begin()),
+      : seeker_(rule_set->LayerIntervals()),
         layer_map_(FindLayerMap(scope, vtt_originating_element, style_sheet)) {}
 
   unsigned SeekLayerOrder(unsigned rule_position) {
-#if DCHECK_IS_ON()
-    DCHECK_GE(rule_position, last_rule_position_);
-    last_rule_position_ = rule_position;
-#endif
-
-    if (!layer_map_)
+    if (!layer_map_) {
       return CascadeLayerMap::kImplicitOuterLayerOrder;
+    }
 
-    while (layer_iter_ != layers_.end() &&
-           layer_iter_->start_position <= rule_position)
-      ++layer_iter_;
-    if (layer_iter_ == layers_.begin())
+    const CascadeLayer* layer = seeker_.Seek(rule_position);
+    if (layer == nullptr) {
       return CascadeLayerMap::kImplicitOuterLayerOrder;
-    return layer_map_->GetLayerOrder(*std::prev(layer_iter_)->layer);
+    } else {
+      return layer_map_->GetLayerOrder(*layer);
+    }
   }
 
  private:
@@ -165,12 +193,8 @@
     return document->GetStyleEngine().GetUserCascadeLayerMap();
   }
 
-  const HeapVector<RuleSet::LayerInterval>& layers_;
-  const RuleSet::LayerInterval* layer_iter_;
+  Seeker<CascadeLayer> seeker_;
   const CascadeLayerMap* layer_map_ = nullptr;
-#if DCHECK_IS_ON()
-  unsigned last_rule_position_ = 0;
-#endif
 };
 
 // The below `rule_map` is designed to aggregate the following values per-rule
@@ -185,7 +209,7 @@
 };
 
 using SelectorStatisticsRuleMap =
-    HashMap<Member<const RuleData>, CumulativeRulePerfData>;
+    HashMap<const RuleData*, CumulativeRulePerfData>;
 SelectorStatisticsRuleMap& GetSelectorStatisticsRuleMap() {
   DEFINE_STATIC_LOCAL(SelectorStatisticsRuleMap, rule_map, {});
   return rule_map;
@@ -307,7 +331,7 @@
 
 template <bool perf_trace_enabled>
 void ElementRuleCollector::CollectMatchingRulesForListInternal(
-    const HeapVector<Member<const RuleData>>* rules,
+    const HeapVector<RuleData>* rules,
     const MatchRequest& match_request,
     const RuleSet* rule_set,
     const CSSStyleSheet* style_sheet,
@@ -327,6 +351,9 @@
 
   CascadeLayerSeeker layer_seeker(
       context.scope, context.vtt_originating_element, style_sheet, rule_set);
+  Seeker<ContainerQuery> container_query_seeker(
+      rule_set->ContainerQueryIntervals());
+  Seeker<StyleScope> scope_seeker(rule_set->ScopeIntervals());
 
   unsigned rejected = 0;
   unsigned fast_rejected = 0;
@@ -338,12 +365,12 @@
   for (const auto& rule_data : *rules) {
     if (perf_trace_enabled) {
       selector_statistics_collector.EndCollectionForCurrentRule();
-      selector_statistics_collector.BeginCollectionForRule(rule_data);
+      selector_statistics_collector.BeginCollectionForRule(&rule_data);
     }
 
     if (can_use_fast_reject_ &&
         selector_filter_.FastRejectSelector<RuleData::kMaximumIdentifierCount>(
-            rule_data->DescendantSelectorIdentifierHashes())) {
+            rule_data.DescendantSelectorIdentifierHashes())) {
       fast_rejected++;
       if (perf_trace_enabled)
         selector_statistics_collector.SetWasFastRejected();
@@ -352,10 +379,10 @@
 
     // Don't return cross-origin rules if we did not explicitly ask for them
     // through SetSameOriginOnly.
-    if (same_origin_only_ && !rule_data->HasDocumentSecurityOrigin())
+    if (same_origin_only_ && !rule_data.HasDocumentSecurityOrigin())
       continue;
 
-    const auto& selector = rule_data->Selector();
+    const auto& selector = rule_data.Selector();
     if (UNLIKELY(part_request && part_request->for_shadow_pseudo)) {
       if (!selector.IsAllowedAfterPart()) {
         DCHECK_EQ(selector.GetPseudoType(), CSSSelector::kPseudoPart);
@@ -367,9 +394,9 @@
 
     SelectorChecker::MatchResult result;
     context.selector = &selector;
-    context.style_scope = rule_data->GetStyleScope();
+    context.style_scope = scope_seeker.Seek(rule_data.GetPosition());
     context.is_inside_visited_link =
-        rule_data->LinkMatchType() == CSSSelector::kMatchVisited;
+        rule_data.LinkMatchType() == CSSSelector::kMatchVisited;
     DCHECK(!context.is_inside_visited_link ||
            inside_link_ != EInsideLink::kNotInsideLink);
     if (!checker.Match(context, result)) {
@@ -381,7 +408,9 @@
       rejected++;
       continue;
     }
-    if (auto* container_query = rule_data->GetContainerQuery()) {
+    const ContainerQuery* container_query =
+        container_query_seeker.Seek(rule_data.GetPosition());
+    if (container_query) {
       result_.SetDependsOnContainerQueries();
 
       // If we are matching pseudo elements like a ::before rule when computing
@@ -396,7 +425,7 @@
         if (!EvaluateAndAddContainerQueries(*container_query,
                                             style_recalc_context_, result_)) {
           rejected++;
-          if (AffectsAnimations(*rule_data))
+          if (AffectsAnimations(rule_data))
             result_.SetConditionallyAffectsAnimations();
           continue;
         }
@@ -415,17 +444,16 @@
     // cache line as the StyleRule, to reduce the impact further. Also, consider
     // just taking empty rules out of the RuleSet altogether, although that
     // would entail doing something to get them back for debug mode.
-    StyleRule* rule = rule_data->Rule();
+    StyleRule* rule = rule_data.Rule();
     if (!rule->ShouldConsiderForMatchingRules(include_empty_rules_))
       continue;
 
     matched++;
     if (perf_trace_enabled)
       selector_statistics_collector.SetDidMatch();
-    unsigned layer_order =
-        layer_seeker.SeekLayerOrder(rule_data->GetPosition());
-    DidMatchRule(rule_data, layer_order, result.proximity, result, style_sheet,
-                 style_sheet_index);
+    unsigned layer_order = layer_seeker.SeekLayerOrder(rule_data.GetPosition());
+    DidMatchRule(&rule_data, layer_order, container_query, result.proximity,
+                 result, style_sheet, style_sheet_index);
   }
 
   if (perf_trace_enabled) {
@@ -445,7 +473,7 @@
 }
 
 void ElementRuleCollector::CollectMatchingRulesForList(
-    const HeapVector<Member<const RuleData>>* rules,
+    const HeapVector<RuleData>* rules,
     const MatchRequest& match_request,
     const RuleSet* rule_set,
     const CSSStyleSheet* style_sheet,
@@ -598,7 +626,7 @@
               : attribute_name;
       for (const auto bundle : match_request.AllRuleSets()) {
         if (bundle.rule_set->HasAnyAttrRules()) {
-          const HeapVector<Member<const RuleData>>* list =
+          const HeapVector<RuleData>* list =
               bundle.rule_set->AttrRules(lower_name);
           if (list && !bundle.rule_set->CanIgnoreEntireList(
                           list, lower_name, attributes[attr_idx].Value())) {
@@ -810,6 +838,7 @@
 void ElementRuleCollector::DidMatchRule(
     const RuleData* rule_data,
     unsigned layer_order,
+    const ContainerQuery* container_query,
     unsigned proximity,
     const SelectorChecker::MatchResult& result,
     const CSSStyleSheet* style_sheet,
@@ -865,8 +894,7 @@
         DCHECK(result.custom_highlight_name);
         style_->SetHasCustomHighlightName(result.custom_highlight_name);
       }
-    } else if (dynamic_pseudo == kPseudoIdFirstLine &&
-               rule_data->GetContainerQuery()) {
+    } else if (dynamic_pseudo == kPseudoIdFirstLine && container_query) {
       style_->SetFirstLineDependsOnContainerQueries(true);
     }
   } else {
@@ -898,8 +926,9 @@
   GetSelectorStatisticsRuleMap().clear();
 }
 
-static inline bool CompareRules(const MatchedRule& matched_rule1,
-                                const MatchedRule& matched_rule2) {
+inline bool ElementRuleCollector::CompareRules(
+    const MatchedRule& matched_rule1,
+    const MatchedRule& matched_rule2) {
   unsigned layer1 = matched_rule1.LayerOrder();
   unsigned layer2 = matched_rule2.LayerOrder();
   if (layer1 != layer2)
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.h b/third_party/blink/renderer/core/css/element_rule_collector.h
index 49625f0..b4f3123 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.h
+++ b/third_party/blink/renderer/core/css/element_rule_collector.h
@@ -41,6 +41,7 @@
 
 class CSSStyleSheet;
 class Element;
+class ElementRuleCollector;
 class HTMLSlotElement;
 class PartNames;
 class RuleData;
@@ -50,7 +51,19 @@
 class MatchedRule {
   DISALLOW_NEW();
 
- public:
+  // Everything in this class is private to ElementRuleCollector, since it
+  // contains non-owned references to RuleData (see the constructor), but we
+  // cannot make the class itself private, since
+  // WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS() needs it to be visible from
+  // the outside.
+ private:
+  // Does not take overship of rule_data (it is owned by the appropriate
+  // bucket in RuleSet), so the RuleData must live for at least as long as
+  // the MatchedRule, ie., those buckets must not be modified (which would
+  // invalidate the RuleData pointers). This is fine, because MatchedRule
+  // is only used during matching (in ElementRuleCollector), and the
+  // RuleData itself never escapes SortAndTransferMatchedRules() -- only
+  // the other elements that it points to.
   MatchedRule(const RuleData* rule_data,
               unsigned layer_order,
               unsigned proximity,
@@ -73,18 +86,17 @@
   unsigned LayerOrder() const { return layer_order_; }
   unsigned Proximity() const { return proximity_; }
   const CSSStyleSheet* ParentStyleSheet() const { return parent_style_sheet_; }
-  void Trace(Visitor* visitor) const {
-    visitor->Trace(parent_style_sheet_);
-    visitor->Trace(rule_data_);
-  }
+  void Trace(Visitor* visitor) const { visitor->Trace(parent_style_sheet_); }
 
  private:
-  Member<const RuleData> rule_data_;
+  const RuleData* rule_data_;
   unsigned layer_order_;
   // https://drafts.csswg.org/css-cascade-6/#weak-scoping-proximity
   unsigned proximity_;
   uint64_t position_;
   Member<const CSSStyleSheet> parent_style_sheet_;
+
+  friend class ElementRuleCollector;
 };
 
 }  // namespace blink
@@ -101,7 +113,11 @@
 //
 // ElementRuleCollector is designed to be used as a stack object.
 // Create one, ask what rules the ElementResolveContext matches
-// and then let it go out of scope.
+// and then let it go out of scope. In particular, do not change
+// values in the RuleSet buckets (which would invalidate the RuleData
+// pointers) before you have extracted the results, typically with
+// SortAndTransferMatchedRules().
+//
 // FIXME: Currently it modifies the ComputedStyle but should not!
 class CORE_EXPORT ElementRuleCollector {
   STACK_ALLOCATED();
@@ -198,16 +214,15 @@
   };
 
   template <bool perf_trace_enabled>
-  void CollectMatchingRulesForListInternal(
-      const HeapVector<Member<const RuleData>>*,
-      const MatchRequest&,
-      const RuleSet*,
-      const CSSStyleSheet*,
-      int,
-      const SelectorChecker&,
-      PartRequest* = nullptr);
+  void CollectMatchingRulesForListInternal(const HeapVector<RuleData>*,
+                                           const MatchRequest&,
+                                           const RuleSet*,
+                                           const CSSStyleSheet*,
+                                           int,
+                                           const SelectorChecker&,
+                                           PartRequest* = nullptr);
 
-  void CollectMatchingRulesForList(const HeapVector<Member<const RuleData>>*,
+  void CollectMatchingRulesForList(const HeapVector<RuleData>*,
                                    const MatchRequest&,
                                    const RuleSet*,
                                    const CSSStyleSheet*,
@@ -220,6 +235,7 @@
              MatchResult&);
   void DidMatchRule(const RuleData*,
                     unsigned layer_order,
+                    const ContainerQuery*,
                     unsigned proximity,
                     const SelectorChecker::MatchResult&,
                     const CSSStyleSheet* style_sheet,
@@ -235,6 +251,9 @@
   StyleRuleList* EnsureStyleRuleList();
 
  private:
+  static inline bool CompareRules(const MatchedRule& matched_rule1,
+                                  const MatchedRule& matched_rule2);
+
   const ElementResolveContext& context_;
   StyleRecalcContext style_recalc_context_;
   const SelectorFilter& selector_filter_;
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 4504f93..80fb7de 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -564,14 +564,7 @@
 bool IsPseudoClassValidWithinHasArgument(CSSParserSelector& selector) {
   DCHECK_EQ(selector.Match(), CSSSelector::kPseudoClass);
   switch (selector.GetPseudoType()) {
-    // Limited these logical combinations inside :has() to avoid increasing
-    // the :has() invalidation complexity.
-    // Currently, SelectorChecker supports these cases, but StyleEngine doesn't
-    // support invalidation for these cases yet because it can increase
-    // invalidation complexity.
-    case CSSSelector::kPseudoIs:
-    case CSSSelector::kPseudoWhere:
-    case CSSSelector::kPseudoAny:
+    // Limited nested :has() to avoid increasing :has() invalidation complexity.
     case CSSSelector::kPseudoHas:
       return false;
     default:
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index 839f134..85cf671f 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -915,11 +915,8 @@
 
 static const SelectorTestCase invalid_pseudo_has_arguments_data[] = {
     // clang-format off
-    // restrict use of :is(), :where() and :has() inside :has()
+    // restrict use of nested :has()
     {":has(:has(.a))", ":has()"},
-    {":has(:is(.a))", ":has()"},
-    {":has(:where(.a))", ":has()"},
-    {":has(:-webkit-any(.a, .b))", ":has()"},
 
     // restrict use of pseudo element inside :has()
     {":has(::-webkit-progress-bar)", ":has()"},
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index c162b1f..11d1ef1 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -347,7 +347,8 @@
               font_description.SyntheticItalicAllowed(),
           font_description.GetFontSelectionRequest(),
           font_selection_capabilities, font_description.FontOpticalSizing(),
-          font_description.Orientation(), font_description.VariationSettings(),
+          font_description.TextRendering(), font_description.Orientation(),
+          font_description.VariationSettings(),
           font_description.GetFontPalette()),
       CustomFontData::Create());
 }
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index e97ac0f..9762b05 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -701,11 +701,11 @@
 }
 
 static void AdjustStyleForInert(ComputedStyle& style, Element* element) {
-  if (!element || style.IsForcedInert())
+  if (!element)
     return;
 
   if (element->IsInertRoot()) {
-    style.SetIsForcedInert();
+    style.SetIsInert(true);
     return;
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index 34d9b4c4..ae79b99 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -1744,8 +1744,8 @@
   EXPECT_TRUE(body->GetComputedStyle()->IsInert());
   EXPECT_TRUE(div->GetComputedStyle()->IsInert());
   EXPECT_TRUE(div_text->GetComputedStyle()->IsInert());
-  EXPECT_TRUE(dialog->GetComputedStyle()->IsInert());
-  EXPECT_TRUE(dialog_text->GetComputedStyle()->IsInert());
+  EXPECT_FALSE(dialog->GetComputedStyle()->IsInert());
+  EXPECT_FALSE(dialog_text->GetComputedStyle()->IsInert());
 
   dialog->close();
   UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/css/rule_feature_set.cc b/third_party/blink/renderer/core/css/rule_feature_set.cc
index 26ef003..84c26e0 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set.cc
@@ -415,6 +415,7 @@
   ids_in_has_argument_.clear();
   tag_names_in_has_argument_.clear();
   universal_in_has_argument_ = false;
+  not_pseudo_in_has_argument_ = false;
   pseudos_in_has_argument_.clear();
 
   is_alive_ = false;
@@ -448,6 +449,7 @@
          ids_in_has_argument_ == other.ids_in_has_argument_ &&
          tag_names_in_has_argument_ == other.tag_names_in_has_argument_ &&
          universal_in_has_argument_ == other.universal_in_has_argument_ &&
+         not_pseudo_in_has_argument_ == other.not_pseudo_in_has_argument_ &&
          pseudos_in_has_argument_ == other.pseudos_in_has_argument_ &&
          is_alive_ == other.is_alive_;
 }
@@ -678,17 +680,18 @@
   return nullptr;
 }
 
-void RuleFeatureSet::UpdateInvalidationSets(const RuleData* rule_data) {
+void RuleFeatureSet::UpdateInvalidationSets(const RuleData* rule_data,
+                                            const StyleScope* style_scope) {
   InvalidationSetFeatures features;
   FeatureInvalidationType feature_invalidation_type =
-      UpdateInvalidationSetsForComplex(rule_data->Selector(),
-                                       rule_data->GetStyleScope(), features,
-                                       kSubject, CSSSelector::kPseudoUnknown);
+      UpdateInvalidationSetsForComplex(rule_data->Selector(), style_scope,
+                                       features, kSubject,
+                                       CSSSelector::kPseudoUnknown);
   if (feature_invalidation_type ==
       FeatureInvalidationType::kRequiresSubtreeInvalidation) {
     features.invalidation_flags.SetWholeSubtreeInvalid(true);
   }
-  if (const StyleScope* style_scope = rule_data->GetStyleScope())
+  if (style_scope)
     UpdateFeaturesFromStyleScope(*style_scope, features);
   UpdateRuleSetInvalidation(features);
 }
@@ -918,6 +921,20 @@
   }
 }
 
+void RuleFeatureSet::AddValuesInComplexSelectorInsideIsWhereNot(
+    const CSSSelectorList* selector_list) {
+  DCHECK(selector_list);
+  for (const CSSSelector* complex = selector_list->First(); complex;
+       complex = CSSSelectorList::Next(*complex)) {
+    DCHECK(complex);
+
+    for (const CSSSelector* simple = complex; simple;
+         simple = simple->TagHistory()) {
+      AddValueOfSimpleSelectorInHasArgument(*simple);
+    }
+  }
+}
+
 bool RuleFeatureSet::AddValueOfSimpleSelectorInHasArgument(
     const CSSSelector& selector) {
   if (selector.Match() == CSSSelector::kClass) {
@@ -940,9 +957,21 @@
   if (selector.Match() == CSSSelector::kPseudoClass) {
     CSSSelector::PseudoType pseudo_type = selector.GetPseudoType();
 
-    // Ignore :visited to prevent history leakage.
-    if (pseudo_type != CSSSelector::kPseudoVisited)
-      pseudos_in_has_argument_.insert(pseudo_type);
+    switch (pseudo_type) {
+      case CSSSelector::kPseudoNot:
+        not_pseudo_in_has_argument_ = true;
+        [[fallthrough]];
+      case CSSSelector::kPseudoIs:
+      case CSSSelector::kPseudoWhere:
+        AddValuesInComplexSelectorInsideIsWhereNot(selector.SelectorList());
+        break;
+      case CSSSelector::kPseudoVisited:
+        // Ignore :visited to prevent history leakage.
+        break;
+      default:
+        pseudos_in_has_argument_.insert(pseudo_type);
+        break;
+    }
     return true;
   }
   return false;
@@ -1166,7 +1195,8 @@
 }
 
 RuleFeatureSet::SelectorPreMatch RuleFeatureSet::CollectFeaturesFromRuleData(
-    const RuleData* rule_data) {
+    const RuleData* rule_data,
+    const StyleScope* style_scope) {
   CHECK(is_alive_);
   FeatureMetadata metadata;
   const unsigned max_direct_adjacent_selectors = 0;
@@ -1178,7 +1208,7 @@
 
   metadata_.Add(metadata);
 
-  UpdateInvalidationSets(rule_data);
+  UpdateInvalidationSets(rule_data, style_scope);
   return kSelectorMayMatch;
 }
 
@@ -1326,6 +1356,7 @@
   for (const auto& tag_name : other.tag_names_in_has_argument_)
     tag_names_in_has_argument_.insert(tag_name);
   universal_in_has_argument_ |= other.universal_in_has_argument_;
+  not_pseudo_in_has_argument_ |= other.not_pseudo_in_has_argument_;
   for (const auto& pseudo_type : other.pseudos_in_has_argument_)
     pseudos_in_has_argument_.insert(pseudo_type);
 }
@@ -1348,6 +1379,7 @@
   ids_in_has_argument_.clear();
   tag_names_in_has_argument_.clear();
   universal_in_has_argument_ = false;
+  not_pseudo_in_has_argument_ = false;
   pseudos_in_has_argument_.clear();
 }
 
@@ -1611,7 +1643,11 @@
          tag_names_in_has_argument_.Contains(tag_name);
 }
 
-bool RuleFeatureSet::NeedsHasInvalidationForElement(Element& element) const {
+bool RuleFeatureSet::NeedsHasInvalidationForInsertedOrRemovedElement(
+    Element& element) const {
+  if (not_pseudo_in_has_argument_)
+    return true;
+
   if (element.HasID()) {
     if (NeedsHasInvalidationForId(element.IdForStyleResolution()))
       return true;
diff --git a/third_party/blink/renderer/core/css/rule_feature_set.h b/third_party/blink/renderer/core/css/rule_feature_set.h
index 048a509..0b0497d 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set.h
+++ b/third_party/blink/renderer/core/css/rule_feature_set.h
@@ -63,7 +63,8 @@
 
   enum SelectorPreMatch { kSelectorNeverMatches, kSelectorMayMatch };
 
-  SelectorPreMatch CollectFeaturesFromRuleData(const RuleData*);
+  SelectorPreMatch CollectFeaturesFromRuleData(const RuleData*,
+                                               const StyleScope*);
 
   // Methods for accessing the data in this object.
   bool UsesFirstLineRules() const { return metadata_.uses_first_line_rules; }
@@ -152,7 +153,7 @@
       const QualifiedName& attribute_name) const;
   bool NeedsHasInvalidationForId(const AtomicString& id) const;
   bool NeedsHasInvalidationForTagName(const AtomicString& tag_name) const;
-  bool NeedsHasInvalidationForElement(Element&) const;
+  bool NeedsHasInvalidationForInsertedOrRemovedElement(Element&) const;
   bool NeedsHasInvalidationForPseudoClass(
       CSSSelector::PseudoType pseudo_type) const;
 
@@ -168,8 +169,8 @@
   inline bool NeedsHasInvalidationForPseudoStateChange() const {
     return !pseudos_in_has_argument_.IsEmpty();
   }
-  inline bool NeedsHasInvalidation() const {
-    return universal_in_has_argument_ ||
+  inline bool NeedsHasInvalidationForInsertionOrRemoval() const {
+    return not_pseudo_in_has_argument_ || universal_in_has_argument_ ||
            !tag_names_in_has_argument_.IsEmpty() ||
            NeedsHasInvalidationForClassChange() ||
            NeedsHasInvalidationForAttributeChange() ||
@@ -268,7 +269,7 @@
   DescendantInvalidationSet& EnsureTypeRuleInvalidationSet();
   DescendantInvalidationSet& EnsurePartInvalidationSet();
 
-  void UpdateInvalidationSets(const RuleData*);
+  void UpdateInvalidationSets(const RuleData*, const StyleScope*);
 
   struct InvalidationSetFeatures {
     DISALLOW_NEW();
@@ -473,6 +474,7 @@
   void AddFeaturesToUniversalSiblingInvalidationSet(
       const InvalidationSetFeatures& sibling_features,
       const InvalidationSetFeatures& descendant_features);
+  void AddValuesInComplexSelectorInsideIsWhereNot(const CSSSelectorList*);
   bool AddValueOfSimpleSelectorInHasArgument(
       const CSSSelector& has_pseudo_class);
 
@@ -523,6 +525,9 @@
   ValuesInHasArgument ids_in_has_argument_;
   ValuesInHasArgument tag_names_in_has_argument_;
   bool universal_in_has_argument_{false};
+  // We always need to invalidate on insertion/removal when we have :not()
+  // inside :has().
+  bool not_pseudo_in_has_argument_{false};
   PseudosInHasArgument pseudos_in_has_argument_;
 
   // If true, the RuleFeatureSet is alive and can be used.
diff --git a/third_party/blink/renderer/core/css/rule_feature_set_test.cc b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
index 2f2e733..d7dfe6d 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
@@ -67,11 +67,8 @@
     RuleFeatureSet::SelectorPreMatch result =
         RuleFeatureSet::SelectorPreMatch::kSelectorNeverMatches;
     for (unsigned i = 0; i < indices.size(); ++i) {
-      RuleData* rule_data = RuleData::MaybeCreate(
-          style_rule, indices[i], 0, kRuleHasNoSpecialState,
-          nullptr /* container_query */, style_scope);
-      DCHECK(rule_data);
-      if (set.CollectFeaturesFromRuleData(rule_data))
+      RuleData rule_data(style_rule, indices[i], 0, 0, kRuleHasNoSpecialState);
+      if (set.CollectFeaturesFromRuleData(&rule_data, style_scope))
         result = RuleFeatureSet::SelectorPreMatch::kSelectorMayMatch;
     }
     return result;
@@ -1730,7 +1727,6 @@
 
 class RuleFeatureSetRefTest : public RuleFeatureSetTest {
  public:
-
   void Run(const RefTestData& data) {
     RuleFeatureSet main_set;
     RuleFeatureSet ref_set;
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 2d8ccd1..29defac 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -53,6 +53,11 @@
 
 namespace blink {
 
+template <class T>
+static void AddRuleToIntervals(const T* value,
+                               unsigned position,
+                               HeapVector<RuleSet::Interval<T>>& intervals);
+
 static inline ValidPropertyFilter DetermineValidPropertyFilter(
     const AddRuleFlags add_rule_flags,
     const CSSSelector& selector) {
@@ -91,43 +96,9 @@
   return CSSSelector::kMatchAll;
 }
 
-RuleData* RuleData::MaybeCreate(StyleRule* rule,
-                                unsigned selector_index,
-                                unsigned position,
-                                AddRuleFlags add_rule_flags,
-                                const ContainerQuery* container_query,
-                                const StyleScope* style_scope) {
-  // The selector index field in RuleData is only 13 bits so we can't support
-  // selectors at index 8192 or beyond.
-  // See https://crbug.com/804179
-  if (selector_index >= (1 << RuleData::kSelectorIndexBits))
-    return nullptr;
-  if (position >= (1 << RuleData::kPositionBits))
-    return nullptr;
-  if (container_query || style_scope) {
-    return MakeGarbageCollected<ExtendedRuleData>(
-        base::PassKey<RuleData>(), rule, selector_index, position,
-        add_rule_flags, container_query, style_scope);
-  }
-  return MakeGarbageCollected<RuleData>(rule, selector_index, position,
-                                        add_rule_flags);
-}
-
 RuleData::RuleData(StyleRule* rule,
                    unsigned selector_index,
                    unsigned position,
-                   AddRuleFlags add_rule_flags)
-    : RuleData(Type::kNormal,
-               rule,
-               selector_index,
-               position,
-               0 /* extra_specificity */,
-               add_rule_flags) {}
-
-RuleData::RuleData(Type type,
-                   StyleRule* rule,
-                   unsigned selector_index,
-                   unsigned position,
                    unsigned extra_specificity,
                    AddRuleFlags add_rule_flags)
     : rule_(rule),
@@ -140,7 +111,6 @@
       valid_property_filter_(
           static_cast<std::underlying_type_t<ValidPropertyFilter>>(
               DetermineValidPropertyFilter(add_rule_flags, Selector()))),
-      type_(static_cast<unsigned>(type)),
       descendant_selector_identifier_hashes_() {
   SelectorFilter::CollectIdentifierHashes(
       Selector(), descendant_selector_identifier_hashes_,
@@ -149,11 +119,11 @@
 
 void RuleSet::AddToRuleSet(const AtomicString& key,
                            RuleMap& map,
-                           const RuleData* rule_data) {
-  Member<HeapVector<Member<const RuleData>>>& rules =
+                           const RuleData& rule_data) {
+  Member<HeapVector<RuleData>>& rules =
       map.insert(key, nullptr).stored_value->value;
   if (!rules)
-    rules = MakeGarbageCollected<HeapVector<Member<const RuleData>>>();
+    rules = MakeGarbageCollected<HeapVector<RuleData>>();
   rules->push_back(rule_data);
 }
 
@@ -268,7 +238,7 @@
 }
 
 bool RuleSet::FindBestRuleSetAndAdd(const CSSSelector& component,
-                                    RuleData* rule_data) {
+                                    const RuleData& rule_data) {
   AtomicString id;
   AtomicString class_name;
   AtomicString attr_name;
@@ -389,19 +359,24 @@
                       const ContainerQuery* container_query,
                       const CascadeLayer* cascade_layer,
                       const StyleScope* style_scope) {
-  RuleData* rule_data =
-      RuleData::MaybeCreate(rule, selector_index, rule_count_, add_rule_flags,
-                            container_query, style_scope);
-  if (!rule_data) {
-    // This can happen if selector_index or position is out of range.
+  // The selector index field in RuleData is only 13 bits so we can't support
+  // selectors at index 8192 or beyond.
+  // See https://crbug.com/804179
+  if (selector_index >= (1 << RuleData::kSelectorIndexBits)) {
     return;
   }
+  if (rule_count_ >= (1 << RuleData::kPositionBits)) {
+    return;
+  }
+  const int extra_specificity = style_scope ? style_scope->Specificity() : 0;
+  RuleData rule_data(rule, selector_index, rule_count_, extra_specificity,
+                     add_rule_flags);
   ++rule_count_;
-  if (features_.CollectFeaturesFromRuleData(rule_data) ==
+  if (features_.CollectFeaturesFromRuleData(&rule_data, style_scope) ==
       RuleFeatureSet::kSelectorNeverMatches)
     return;
 
-  if (!FindBestRuleSetAndAdd(rule_data->Selector(), rule_data)) {
+  if (!FindBestRuleSetAndAdd(rule_data.Selector(), rule_data)) {
     // If we didn't find a specialized map to stick it in, file under universal
     // rules.
     universal_rules_.push_back(rule_data);
@@ -412,15 +387,17 @@
   // effectively split the rule into two: one which covers the situation
   // where we are in an unvisited link (kMatchLink), and another which covers
   // the visited link case (kMatchVisited).
-  if (rule_data->LinkMatchType() == CSSSelector::kMatchLink) {
-    RuleData* visited_dependent = RuleData::MaybeCreate(
-        rule, rule_data->SelectorIndex(), rule_data->GetPosition(),
-        add_rule_flags | kRuleIsVisitedDependent, container_query, style_scope);
-    DCHECK(visited_dependent);
+  if (rule_data.LinkMatchType() == CSSSelector::kMatchLink) {
+    RuleData visited_dependent(rule, rule_data.SelectorIndex(),
+                               rule_data.GetPosition(), extra_specificity,
+                               add_rule_flags | kRuleIsVisitedDependent);
     visited_dependent_rules_.push_back(visited_dependent);
   }
 
-  AddRuleToLayerIntervals(cascade_layer, rule_data->GetPosition());
+  AddRuleToLayerIntervals(cascade_layer, rule_data.GetPosition());
+  AddRuleToIntervals(container_query, rule_data.GetPosition(),
+                     container_query_intervals_);
+  AddRuleToIntervals(style_scope, rule_data.GetPosition(), scope_intervals_);
 }
 
 void RuleSet::AddRuleToLayerIntervals(const CascadeLayer* cascade_layer,
@@ -429,8 +406,8 @@
   // interval's layer. Note that the implicit outer layer may also be
   // represented by a nullptr.
   const CascadeLayer* last_interval_layer =
-      layer_intervals_.size() ? layer_intervals_.back().layer.Get()
-                              : implicit_outer_layer_.Get();
+      layer_intervals_.IsEmpty() ? implicit_outer_layer_.Get()
+                                 : layer_intervals_.back().value.Get();
   if (!cascade_layer)
     cascade_layer = implicit_outer_layer_;
   if (cascade_layer == last_interval_layer)
@@ -438,7 +415,21 @@
 
   if (!cascade_layer)
     cascade_layer = EnsureImplicitOuterLayer();
-  layer_intervals_.push_back(LayerInterval(cascade_layer, position));
+  layer_intervals_.push_back(Interval<CascadeLayer>(cascade_layer, position));
+}
+
+// Similar to AddRuleToLayerIntervals, but for container queries and @style
+// scopes.
+template <class T>
+static void AddRuleToIntervals(const T* value,
+                               unsigned position,
+                               HeapVector<RuleSet::Interval<T>>& intervals) {
+  const T* last_value =
+      intervals.IsEmpty() ? nullptr : intervals.back().value.Get();
+  if (value == last_value)
+    return;
+
+  intervals.push_back(RuleSet::Interval<T>(value, position));
 }
 
 void RuleSet::AddPageRule(StyleRulePage* rule) {
@@ -646,10 +637,9 @@
              : std::numeric_limits<wtf_size_t>::max();
 }
 
-bool RuleSet::CanIgnoreEntireList(
-    const HeapVector<Member<const RuleData>>* list,
-    const AtomicString& key,
-    const AtomicString& value) const {
+bool RuleSet::CanIgnoreEntireList(const HeapVector<RuleData>* list,
+                                  const AtomicString& key,
+                                  const AtomicString& value) const {
   DCHECK_EQ(attr_rules_.find(key)->value, list);
   if (list->size() < GetMinimumRulesetSizeForSubstringMatcher()) {
     // Too small to build up a tree, so always check.
@@ -681,7 +671,7 @@
     }
     std::vector<MatcherStringPattern> patterns;
     int rule_index = 0;
-    for (const Member<const RuleData>& rule : *ruleset) {
+    for (const RuleData& rule : *ruleset) {
       AtomicString id;
       AtomicString class_name;
       AtomicString attr_name;
@@ -690,7 +680,7 @@
       AtomicString tag_name;
       AtomicString part_name;
       CSSSelector::PseudoType pseudo_type = CSSSelector::kPseudoUnknown;
-      ExtractBestSelectorValues(rule->Selector(), id, class_name, attr_name,
+      ExtractBestSelectorValues(rule.Selector(), id, class_name, attr_name,
                                 attr_value, custom_pseudo_element_name,
                                 tag_name, part_name, pseudo_type);
       DCHECK(!attr_name.IsEmpty());
@@ -769,11 +759,11 @@
 bool IsRuleListSorted(const RuleList& rules) {
   unsigned last_position = 0;
   bool first_rule = true;
-  for (const auto& rule : rules) {
-    if (!first_rule && rule->GetPosition() <= last_position)
+  for (const RuleData& rule : rules) {
+    if (!first_rule && rule.GetPosition() <= last_position)
       return false;
     first_rule = false;
-    last_position = rule->GetPosition();
+    last_position = rule.GetPosition();
   }
   return true;
 }
@@ -808,68 +798,24 @@
   return evaluator.DidResultsChange(media_query_set_results_);
 }
 
-const ContainerQuery* RuleData::GetContainerQuery() const {
-  if (auto* extended = DynamicTo<ExtendedRuleData>(this))
-    return extended->container_query_;
-  return nullptr;
-}
-
-const StyleScope* RuleData::GetStyleScope() const {
-  if (auto* extended = DynamicTo<ExtendedRuleData>(this))
-    return extended->style_scope_;
-  return nullptr;
-}
-
 const CascadeLayer* RuleSet::GetLayerForTest(const RuleData& rule) const {
   if (!layer_intervals_.size() ||
       layer_intervals_[0].start_position > rule.GetPosition())
     return implicit_outer_layer_;
   for (unsigned i = 1; i < layer_intervals_.size(); ++i) {
     if (layer_intervals_[i].start_position > rule.GetPosition())
-      return layer_intervals_[i - 1].layer;
+      return layer_intervals_[i - 1].value;
   }
-  return layer_intervals_.back().layer;
+  return layer_intervals_.back().value;
 }
 
 void RuleData::Trace(Visitor* visitor) const {
-  switch (static_cast<Type>(type_)) {
-    case Type::kNormal:
-      TraceAfterDispatch(visitor);
-      break;
-    case Type::kExtended:
-      To<ExtendedRuleData>(*this).TraceAfterDispatch(visitor);
-      break;
-  }
-}
-
-void RuleData::TraceAfterDispatch(blink::Visitor* visitor) const {
   visitor->Trace(rule_);
 }
 
-ExtendedRuleData::ExtendedRuleData(base::PassKey<RuleData>,
-                                   StyleRule* rule,
-                                   unsigned selector_index,
-                                   unsigned position,
-                                   AddRuleFlags flags,
-                                   const ContainerQuery* container_query,
-                                   const StyleScope* style_scope)
-    : RuleData(Type::kExtended,
-               rule,
-               selector_index,
-               position,
-               (style_scope ? style_scope->Specificity() : 0),
-               flags),
-      container_query_(container_query),
-      style_scope_(style_scope) {}
-
-void ExtendedRuleData::TraceAfterDispatch(Visitor* visitor) const {
-  RuleData::TraceAfterDispatch(visitor);
-  visitor->Trace(container_query_);
-  visitor->Trace(style_scope_);
-}
-
-void RuleSet::LayerInterval::Trace(Visitor* visitor) const {
-  visitor->Trace(layer);
+template <class T>
+void RuleSet::Interval<T>::Trace(Visitor* visitor) const {
+  visitor->Trace(value);
 }
 
 void RuleSet::Trace(Visitor* visitor) const {
@@ -898,6 +844,8 @@
   visitor->Trace(scroll_timeline_rules_);
   visitor->Trace(implicit_outer_layer_);
   visitor->Trace(layer_intervals_);
+  visitor->Trace(container_query_intervals_);
+  visitor->Trace(scope_intervals_);
 #ifndef NDEBUG
   visitor->Trace(all_rules_);
 #endif
@@ -905,8 +853,8 @@
 
 #ifndef NDEBUG
 void RuleSet::Show() const {
-  for (const auto& rule : all_rules_)
-    rule->Selector().Show();
+  for (const RuleData& rule : all_rules_)
+    rule.Selector().Show();
 }
 #endif
 
diff --git a/third_party/blink/renderer/core/css/rule_set.h b/third_party/blink/renderer/core/css/rule_set.h
index c4103d6c..ace07df 100644
--- a/third_party/blink/renderer/core/css/rule_set.h
+++ b/third_party/blink/renderer/core/css/rule_set.h
@@ -83,34 +83,22 @@
 // selectors from a single rule match the same element we can see that as one
 // match for the rule. It computes some information about the wrapped selector
 // and makes it accessible cheaply.
-class CORE_EXPORT RuleData : public GarbageCollected<RuleData> {
+class CORE_EXPORT RuleData {
+  DISALLOW_NEW();
+
  public:
-  enum class Type {
-    kNormal = 0,
-    kExtended = 1,
-    // Note that the above values are stored in a 1-bit field.
-    // See RuleData::type_.
-  };
-
-  static RuleData* MaybeCreate(StyleRule*,
-                               unsigned selector_index,
-                               unsigned position,
-                               AddRuleFlags,
-                               const ContainerQuery*,
-                               const StyleScope*);
-
+  // The `extra_specificity` parameter is added to the specificity of the
+  // RuleData. This is useful for @scope, where inner selectors must gain
+  // additional specificity from the <scope-start> of the enclosing @scope.
+  // https://drafts.csswg.org/css-cascade-6/#scope-atrule
   RuleData(StyleRule*,
            unsigned selector_index,
            unsigned position,
+           unsigned extra_specificity,
            AddRuleFlags);
 
-  bool IsExtended() const {
-    return static_cast<Type>(type_) == Type::kExtended;
-  }
   unsigned GetPosition() const { return position_; }
   StyleRule* Rule() const { return rule_; }
-  const ContainerQuery* GetContainerQuery() const;
-  const StyleScope* GetStyleScope() const;
   const CSSSelector& Selector() const {
     return rule_->SelectorList().SelectorAt(selector_index_);
   }
@@ -138,7 +126,6 @@
   }
 
   void Trace(Visitor*) const;
-  void TraceAfterDispatch(blink::Visitor* visitor) const;
 
   // This number is picked fairly arbitrary. If lowered, be aware that there
   // might be sites and extensions using style rules with selector lists
@@ -150,18 +137,6 @@
   // need to. Some simple testing showed <100,000 RuleData's on large sites.
   static constexpr size_t kPositionBits = 18;
 
- protected:
-  // The `extra_specificity` parameter is added to the specificity of the
-  // RuleData. This is useful for @scope, where inner selectors must gain
-  // additional specificity from the <scope-start> of the enclosing @scope.
-  // https://drafts.csswg.org/css-cascade-6/#scope-atrule
-  RuleData(Type type,
-           StyleRule*,
-           unsigned selector_index,
-           unsigned position,
-           unsigned extra_specificity,
-           AddRuleFlags);
-
  private:
   Member<StyleRule> rule_;
   unsigned selector_index_ : kSelectorIndexBits;
@@ -172,39 +147,11 @@
   unsigned link_match_type_ : 2;
   unsigned has_document_security_origin_ : 1;
   unsigned valid_property_filter_ : 3;
-  unsigned type_ : 1;  // RuleData::Type
-  // 31 bits above
+  // 30 bits above
   // Use plain array instead of a Vector to minimize memory overhead.
   unsigned descendant_selector_identifier_hashes_[kMaximumIdentifierCount];
 };
 
-// Big websites can have a large number of RuleData objects (30k+). This class
-// exists to avoid allocating unnecessary memory for "rare" fields.
-class CORE_EXPORT ExtendedRuleData : public RuleData {
- public:
-  // Do not create ExtendedRuleData objects directly; RuleData::MaybeCreate
-  // will decide if ExtendedRuleData is needed or not.
-  ExtendedRuleData(base::PassKey<RuleData>,
-                   StyleRule*,
-                   unsigned selector_index,
-                   unsigned position,
-                   AddRuleFlags,
-                   const ContainerQuery*,
-                   const StyleScope*);
-  void TraceAfterDispatch(Visitor*) const;
-
- private:
-  friend class RuleData;
-
-  Member<const ContainerQuery> container_query_;
-  Member<const StyleScope> style_scope_;
-};
-
-template <>
-struct DowncastTraits<ExtendedRuleData> {
-  static bool AllowFrom(const RuleData& data) { return data.IsExtended(); }
-};
-
 }  // namespace blink
 
 WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::RuleData)
@@ -242,66 +189,60 @@
 
   const RuleFeatureSet& Features() const { return features_; }
 
-  const HeapVector<Member<const RuleData>>* IdRules(
-      const AtomicString& key) const {
+  const HeapVector<RuleData>* IdRules(const AtomicString& key) const {
     auto it = id_rules_.find(key);
     return it != id_rules_.end() ? it->value : nullptr;
   }
-  const HeapVector<Member<const RuleData>>* ClassRules(
-      const AtomicString& key) const {
+  const HeapVector<RuleData>* ClassRules(const AtomicString& key) const {
     auto it = class_rules_.find(key);
     return it != class_rules_.end() ? it->value : nullptr;
   }
   bool HasAnyAttrRules() const { return !attr_rules_.IsEmpty(); }
-  const HeapVector<Member<const RuleData>>* AttrRules(
-      const AtomicString& key) const {
+  const HeapVector<RuleData>* AttrRules(const AtomicString& key) const {
     auto it = attr_rules_.find(key);
     return it != attr_rules_.end() ? it->value : nullptr;
   }
-  bool CanIgnoreEntireList(const HeapVector<Member<const RuleData>>* list,
+  bool CanIgnoreEntireList(const HeapVector<RuleData>* list,
                            const AtomicString& key,
                            const AtomicString& value) const;
-  const HeapVector<Member<const RuleData>>* TagRules(
-      const AtomicString& key) const {
+  const HeapVector<RuleData>* TagRules(const AtomicString& key) const {
     auto it = tag_rules_.find(key);
     return it != tag_rules_.end() ? it->value : nullptr;
   }
-  const HeapVector<Member<const RuleData>>* UAShadowPseudoElementRules(
+  const HeapVector<RuleData>* UAShadowPseudoElementRules(
       const AtomicString& key) const {
     auto it = ua_shadow_pseudo_element_rules_.find(key);
     return it != ua_shadow_pseudo_element_rules_.end() ? it->value : nullptr;
   }
-  const HeapVector<Member<const RuleData>>* LinkPseudoClassRules() const {
+  const HeapVector<RuleData>* LinkPseudoClassRules() const {
     return &link_pseudo_class_rules_;
   }
-  const HeapVector<Member<const RuleData>>* CuePseudoRules() const {
+  const HeapVector<RuleData>* CuePseudoRules() const {
     return &cue_pseudo_rules_;
   }
-  const HeapVector<Member<const RuleData>>* FocusPseudoClassRules() const {
+  const HeapVector<RuleData>* FocusPseudoClassRules() const {
     return &focus_pseudo_class_rules_;
   }
-  const HeapVector<Member<const RuleData>>* FocusVisiblePseudoClassRules()
-      const {
+  const HeapVector<RuleData>* FocusVisiblePseudoClassRules() const {
     return &focus_visible_pseudo_class_rules_;
   }
-  const HeapVector<Member<const RuleData>>*
-  SpatialNavigationInterestPseudoClassRules() const {
+  const HeapVector<RuleData>* SpatialNavigationInterestPseudoClassRules()
+      const {
     return &spatial_navigation_interest_class_rules_;
   }
-  const HeapVector<Member<const RuleData>>* UniversalRules() const {
+  const HeapVector<RuleData>* UniversalRules() const {
     return &universal_rules_;
   }
-  const HeapVector<Member<const RuleData>>* ShadowHostRules() const {
+  const HeapVector<RuleData>* ShadowHostRules() const {
     return &shadow_host_rules_;
   }
-  const HeapVector<Member<const RuleData>>* PartPseudoRules() const {
+  const HeapVector<RuleData>* PartPseudoRules() const {
     return &part_pseudo_rules_;
   }
-  const HeapVector<Member<const RuleData>>* VisitedDependentRules() const {
+  const HeapVector<RuleData>* VisitedDependentRules() const {
     return &visited_dependent_rules_;
   }
-  const HeapVector<Member<const RuleData>>* SelectorFragmentAnchorRules()
-      const {
+  const HeapVector<RuleData>* SelectorFragmentAnchorRules() const {
     return &selector_fragment_anchor_rules_;
   }
   const HeapVector<Member<StyleRulePage>>& PageRules() const {
@@ -327,7 +268,7 @@
       const {
     return scroll_timeline_rules_;
   }
-  const HeapVector<Member<const RuleData>>* SlottedPseudoElementRules() const {
+  const HeapVector<RuleData>* SlottedPseudoElementRules() const {
     return &slotted_pseudo_element_rules_;
   }
 
@@ -356,24 +297,41 @@
 
   bool DidMediaQueryResultsChange(const MediaQueryEvaluator& evaluator) const;
 
-  // We use a vector of LayerInterval to represent that rules with positions
-  // between start_position (inclusive) and the next LayerInterval's
-  // start_position (exclusive) belong to the given layer.
-  class LayerInterval {
+  // We use a vector of Interval<T> to represent that rules with positions
+  // between start_position (inclusive) and the next Interval<T>'s
+  // start_position (exclusive) share some property:
+  //
+  //   - If T = CascadeLayer, belong to the given layer.
+  //   - If T = ContainerQuery, are predicated on the given container query.
+  //   - If T = StyleScope, are declared in the given @style scope.
+  //
+  // We do this instead of putting the data directly onto the RuleData,
+  // because most rules don't need these fields and websites can have a large
+  // number of RuleData objects (30k+). Since neighboring rules tend to have the
+  // same values for these (often nullptr), we save memory and cache space at
+  // the cost of a some extra seeking through these lists when matching rules.
+  template <class T>
+  class Interval {
     DISALLOW_NEW();
 
    public:
-    LayerInterval(const CascadeLayer* passed_layer, unsigned passed_position)
-        : layer(passed_layer), start_position(passed_position) {}
-    const Member<const CascadeLayer> layer;
+    Interval(const T* passed_value, unsigned passed_position)
+        : value(passed_value), start_position(passed_position) {}
+    const Member<const T> value;
     const unsigned start_position = 0;
 
     void Trace(Visitor*) const;
   };
 
-  const HeapVector<LayerInterval>& LayerIntervals() const {
+  const HeapVector<Interval<CascadeLayer>>& LayerIntervals() const {
     return layer_intervals_;
   }
+  const HeapVector<Interval<ContainerQuery>>& ContainerQueryIntervals() const {
+    return container_query_intervals_;
+  }
+  const HeapVector<Interval<StyleScope>>& ScopeIntervals() const {
+    return scope_intervals_;
+  }
 
 #ifndef NDEBUG
   void Show() const;
@@ -383,14 +341,14 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(RuleSetTest, RuleCountNotIncreasedByInvalidRuleData);
+  FRIEND_TEST_ALL_PREFIXES(RuleSetTest, RuleDataPositionLimit);
   friend class RuleSetCascadeLayerTest;
 
-  using RuleMap =
-      HeapHashMap<AtomicString, Member<HeapVector<Member<const RuleData>>>>;
+  using RuleMap = HeapHashMap<AtomicString, Member<HeapVector<RuleData>>>;
   using SubstringMatcherMap =
       HashMap<AtomicString, std::unique_ptr<base::SubstringSetMatcher>>;
 
-  void AddToRuleSet(const AtomicString& key, RuleMap&, const RuleData*);
+  void AddToRuleSet(const AtomicString& key, RuleMap&, const RuleData&);
   void AddPageRule(StyleRulePage*);
   void AddViewportRule(StyleRuleViewport*);
   void AddFontFaceRule(StyleRuleFontFace*);
@@ -408,7 +366,7 @@
                      const ContainerQuery*,
                      CascadeLayer*,
                      const StyleScope*);
-  bool FindBestRuleSetAndAdd(const CSSSelector&, RuleData*);
+  bool FindBestRuleSetAndAdd(const CSSSelector&, const RuleData&);
   void AddRule(StyleRule*,
                unsigned selector_index,
                AddRuleFlags,
@@ -468,17 +426,17 @@
   SubstringMatcherMap attr_substring_matchers_;
   RuleMap tag_rules_;
   RuleMap ua_shadow_pseudo_element_rules_;
-  HeapVector<Member<const RuleData>> link_pseudo_class_rules_;
-  HeapVector<Member<const RuleData>> cue_pseudo_rules_;
-  HeapVector<Member<const RuleData>> focus_pseudo_class_rules_;
-  HeapVector<Member<const RuleData>> focus_visible_pseudo_class_rules_;
-  HeapVector<Member<const RuleData>> spatial_navigation_interest_class_rules_;
-  HeapVector<Member<const RuleData>> universal_rules_;
-  HeapVector<Member<const RuleData>> shadow_host_rules_;
-  HeapVector<Member<const RuleData>> part_pseudo_rules_;
-  HeapVector<Member<const RuleData>> slotted_pseudo_element_rules_;
-  HeapVector<Member<const RuleData>> visited_dependent_rules_;
-  HeapVector<Member<const RuleData>> selector_fragment_anchor_rules_;
+  HeapVector<RuleData> link_pseudo_class_rules_;
+  HeapVector<RuleData> cue_pseudo_rules_;
+  HeapVector<RuleData> focus_pseudo_class_rules_;
+  HeapVector<RuleData> focus_visible_pseudo_class_rules_;
+  HeapVector<RuleData> spatial_navigation_interest_class_rules_;
+  HeapVector<RuleData> universal_rules_;
+  HeapVector<RuleData> shadow_host_rules_;
+  HeapVector<RuleData> part_pseudo_rules_;
+  HeapVector<RuleData> slotted_pseudo_element_rules_;
+  HeapVector<RuleData> visited_dependent_rules_;
+  HeapVector<RuleData> selector_fragment_anchor_rules_;
   RuleFeatureSet features_;
   HeapVector<Member<StyleRulePage>> page_rules_;
   HeapVector<Member<StyleRuleFontFace>> font_face_rules_;
@@ -501,15 +459,24 @@
   // nullptr if the stylesheet doesn't explicitly declare any layer.
   Member<CascadeLayer> implicit_outer_layer_;
   // Empty vector if the stylesheet doesn't explicitly declare any layer.
-  HeapVector<LayerInterval> layer_intervals_;
+  HeapVector<Interval<CascadeLayer>> layer_intervals_;
+  // Empty vector if the stylesheet doesn't use any container queries.
+  HeapVector<Interval<ContainerQuery>> container_query_intervals_;
+  // Empty vector if the stylesheet doesn't use any @scopes.
+  HeapVector<Interval<StyleScope>> scope_intervals_;
 
 #ifndef NDEBUG
-  HeapVector<Member<const RuleData>> all_rules_;
+  HeapVector<RuleData> all_rules_;
 #endif
 };
 
 }  // namespace blink
 
-WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::RuleSet::LayerInterval)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
+    blink::RuleSet::Interval<blink::CascadeLayer>)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
+    blink::RuleSet::Interval<blink::ContainerQuery>)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
+    blink::RuleSet::Interval<blink::StyleScope>)
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_
diff --git a/third_party/blink/renderer/core/css/rule_set_test.cc b/third_party/blink/renderer/core/css/rule_set_test.cc
index 3b0b6b8..e42b5377 100644
--- a/third_party/blink/renderer/core/css/rule_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_set_test.cc
@@ -52,9 +52,9 @@
   css_test_helpers::TestStyleSheet sheet;
   sheet.AddCSSRules("#id { color: tomato; }");
   const RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules("id");
+  const HeapVector<RuleData>* rules = rule_set.IdRules("id");
   DCHECK_EQ(1u, rules->size());
-  return rules->at(0)->Rule();
+  return rules->at(0).Rule();
 }
 
 }  // namespace
@@ -65,10 +65,9 @@
   sheet.AddCSSRules("summary::-webkit-details-marker { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("-webkit-details-marker");
-  const HeapVector<Member<const RuleData>>* rules =
-      rule_set.UAShadowPseudoElementRules(str);
+  const HeapVector<RuleData>* rules = rule_set.UAShadowPseudoElementRules(str);
   ASSERT_EQ(1u, rules->size());
-  ASSERT_EQ(str, rules->at(0)->Selector().Value());
+  ASSERT_EQ(str, rules->at(0).Selector().Value());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_Id) {
@@ -77,9 +76,9 @@
   sheet.AddCSSRules("#id { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("id");
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules(str);
+  const HeapVector<RuleData>* rules = rule_set.IdRules(str);
   ASSERT_EQ(1u, rules->size());
-  ASSERT_EQ(str, rules->at(0)->Selector().Value());
+  ASSERT_EQ(str, rules->at(0).Selector().Value());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_NthChild) {
@@ -88,9 +87,9 @@
   sheet.AddCSSRules("div:nth-child(2) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("div");
-  const HeapVector<Member<const RuleData>>* rules = rule_set.TagRules(str);
+  const HeapVector<RuleData>* rules = rule_set.TagRules(str);
   ASSERT_EQ(1u, rules->size());
-  ASSERT_EQ(str, rules->at(0)->Selector().TagQName().LocalName());
+  ASSERT_EQ(str, rules->at(0).Selector().TagQName().LocalName());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_ClassThenId) {
@@ -100,10 +99,10 @@
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("id");
   // id is prefered over class even if class preceeds it in the selector.
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules(str);
+  const HeapVector<RuleData>* rules = rule_set.IdRules(str);
   ASSERT_EQ(1u, rules->size());
   AtomicString class_str("class");
-  ASSERT_EQ(class_str, rules->at(0)->Selector().Value());
+  ASSERT_EQ(class_str, rules->at(0).Selector().Value());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_IdThenClass) {
@@ -112,9 +111,9 @@
   sheet.AddCSSRules("#id.class { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("id");
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules(str);
+  const HeapVector<RuleData>* rules = rule_set.IdRules(str);
   ASSERT_EQ(1u, rules->size());
-  ASSERT_EQ(str, rules->at(0)->Selector().Value());
+  ASSERT_EQ(str, rules->at(0).Selector().Value());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_AttrThenId) {
@@ -123,10 +122,10 @@
   sheet.AddCSSRules("[attr]#id { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("id");
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules(str);
+  const HeapVector<RuleData>* rules = rule_set.IdRules(str);
   ASSERT_EQ(1u, rules->size());
   AtomicString attr_str("attr");
-  ASSERT_EQ(attr_str, rules->at(0)->Selector().Attribute().LocalName());
+  ASSERT_EQ(attr_str, rules->at(0).Selector().Attribute().LocalName());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_TagThenAttrThenId) {
@@ -135,10 +134,10 @@
   sheet.AddCSSRules("div[attr]#id { }");
   RuleSet& rule_set = sheet.GetRuleSet();
   AtomicString str("id");
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules(str);
+  const HeapVector<RuleData>* rules = rule_set.IdRules(str);
   ASSERT_EQ(1u, rules->size());
   AtomicString tag_str("div");
-  ASSERT_EQ(tag_str, rules->at(0)->Selector().TagQName().LocalName());
+  ASSERT_EQ(tag_str, rules->at(0).Selector().TagQName().LocalName());
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_TagThenAttr) {
@@ -164,7 +163,7 @@
 
   sheet.AddCSSRules(":host { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(1u, rules->size());
 }
 
@@ -173,7 +172,7 @@
 
   sheet.AddCSSRules(":host(#x) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(1u, rules->size());
 }
 
@@ -182,7 +181,7 @@
 
   sheet.AddCSSRules(":host-context(*) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(1u, rules->size());
 }
 
@@ -191,7 +190,7 @@
 
   sheet.AddCSSRules(":host-context(#x) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(1u, rules->size());
 }
 
@@ -200,11 +199,9 @@
 
   sheet.AddCSSRules(":host-context(#x) .y, :host(.a) > #b  { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* shadow_rules =
-      rule_set.ShadowHostRules();
-  const HeapVector<Member<const RuleData>>* id_rules = rule_set.IdRules("b");
-  const HeapVector<Member<const RuleData>>* class_rules =
-      rule_set.ClassRules("y");
+  const HeapVector<RuleData>* shadow_rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* id_rules = rule_set.IdRules("b");
+  const HeapVector<RuleData>* class_rules = rule_set.ClassRules("y");
   ASSERT_EQ(0u, shadow_rules->size());
   ASSERT_EQ(1u, id_rules->size());
   ASSERT_EQ(1u, class_rules->size());
@@ -215,7 +212,7 @@
 
   sheet.AddCSSRules(".foo:host { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(0u, rules->size());
 }
 
@@ -224,7 +221,7 @@
 
   sheet.AddCSSRules(".foo:host-context(*) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ShadowHostRules();
+  const HeapVector<RuleData>* rules = rule_set.ShadowHostRules();
   ASSERT_EQ(0u, rules->size());
 }
 
@@ -258,7 +255,7 @@
   sheet.AddCSSRules("::cue(b) { }");
   sheet.AddCSSRules("video::cue(u) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.CuePseudoRules();
+  const HeapVector<RuleData>* rules = rule_set.CuePseudoRules();
   ASSERT_EQ(2u, rules->size());
 }
 
@@ -278,7 +275,7 @@
 
   sheet.AddCSSRules("::part(dummy):focus, #id::part(dummy) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.PartPseudoRules();
+  const HeapVector<RuleData>* rules = rule_set.PartPseudoRules();
   ASSERT_EQ(2u, rules->size());
 }
 
@@ -287,7 +284,7 @@
 
   sheet.AddCSSRules(":is(.a) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ClassRules("a");
+  const HeapVector<RuleData>* rules = rule_set.ClassRules("a");
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
 }
@@ -297,7 +294,7 @@
 
   sheet.AddCSSRules(":where(.a) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ClassRules("a");
+  const HeapVector<RuleData>* rules = rule_set.ClassRules("a");
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
 }
@@ -307,7 +304,7 @@
 
   sheet.AddCSSRules(":where(:is(.a)) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.ClassRules("a");
+  const HeapVector<RuleData>* rules = rule_set.ClassRules("a");
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
 }
@@ -317,7 +314,7 @@
 
   sheet.AddCSSRules(":is(.a, .b) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.UniversalRules();
+  const HeapVector<RuleData>* rules = rule_set.UniversalRules();
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
 }
@@ -327,7 +324,7 @@
 
   sheet.AddCSSRules(":where(.a, .b) { }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.UniversalRules();
+  const HeapVector<RuleData>* rules = rule_set.UniversalRules();
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
 }
@@ -351,7 +348,7 @@
   sheet.AddCSSRules("[otherattr=\"value\"] {}");
 
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* list = rule_set.AttrRules("attr");
+  const HeapVector<RuleData>* list = rule_set.AttrRules("attr");
   ASSERT_NE(nullptr, list);
 
   EXPECT_TRUE(rule_set.CanIgnoreEntireList(list, "attr", "notfound"));
@@ -364,8 +361,7 @@
 
   // One rule is not enough to build a tree, so we will not mass-reject
   // anything on otherattr.
-  const HeapVector<Member<const RuleData>>* list2 =
-      rule_set.AttrRules("otherattr");
+  const HeapVector<RuleData>* list2 = rule_set.AttrRules("otherattr");
   EXPECT_FALSE(rule_set.CanIgnoreEntireList(list2, "otherattr", "notfound"));
 }
 
@@ -388,7 +384,7 @@
   sheet.AddCSSRules("[attr=\"\"] {}");
 
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* list = rule_set.AttrRules("attr");
+  const HeapVector<RuleData>* list = rule_set.AttrRules("attr");
   ASSERT_NE(nullptr, list);
   EXPECT_TRUE(rule_set.CanIgnoreEntireList(list, "attr", "notfound"));
   EXPECT_FALSE(rule_set.CanIgnoreEntireList(list, "attr", ""));
@@ -415,40 +411,26 @@
   css_test_helpers::TestStyleSheet sheet;
   sheet.AddCSSRules(builder.ToString());
   const RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.TagRules("b");
+  const HeapVector<RuleData>* rules = rule_set.TagRules("b");
   ASSERT_EQ(1u, rules->size());
-  EXPECT_EQ("b", rules->at(0)->Selector().TagQName().LocalName());
+  EXPECT_EQ("b", rules->at(0).Selector().TagQName().LocalName());
   EXPECT_FALSE(rule_set.TagRules("span"));
 }
 
-TEST(RuleSetTest, RuleDataSelectorIndexLimit) {
-  StyleRule* rule = CreateDummyStyleRule();
-  AddRuleFlags flags = kRuleHasNoSpecialState;
-  const unsigned position = 0;
-  EXPECT_TRUE(RuleData::MaybeCreate(rule, 0, position, flags,
-                                    nullptr /* container_query */,
-                                    nullptr /* scope */));
-  EXPECT_FALSE(RuleData::MaybeCreate(
-      rule, (1 << RuleData::kSelectorIndexBits), position, flags,
-      nullptr /* container_query */, nullptr /* scope */));
-  EXPECT_FALSE(RuleData::MaybeCreate(
-      rule, (1 << RuleData::kSelectorIndexBits) + 1, position, flags,
-      nullptr /* container_query */, nullptr /* scope */));
-}
-
 TEST(RuleSetTest, RuleDataPositionLimit) {
   StyleRule* rule = CreateDummyStyleRule();
   AddRuleFlags flags = kRuleHasNoSpecialState;
   const unsigned selector_index = 0;
-  EXPECT_TRUE(RuleData::MaybeCreate(rule, selector_index, 0, flags,
-                                    nullptr /* container_query */,
-                                    nullptr /* scope */));
-  EXPECT_FALSE(RuleData::MaybeCreate(
-      rule, selector_index, (1 << RuleData::kPositionBits), flags,
-      nullptr /* container_query */, nullptr /* scope */));
-  EXPECT_FALSE(RuleData::MaybeCreate(
-      rule, selector_index, (1 << RuleData::kPositionBits) + 1, flags,
-      nullptr /* container_query */, nullptr /* scope */));
+  const ContainerQuery* container_query = nullptr;
+  const CascadeLayer* cascade_layer = nullptr;
+  const StyleScope* style_scope = nullptr;
+
+  auto* rule_set = MakeGarbageCollected<RuleSet>();
+  for (int i = 0; i < (1 << RuleData::kPositionBits) + 1; ++i) {
+    rule_set->AddRule(rule, selector_index, flags, container_query,
+                      cascade_layer, style_scope);
+  }
+  EXPECT_EQ(1u << RuleData::kPositionBits, rule_set->RuleCount());
 }
 
 TEST(RuleSetTest, RuleCountNotIncreasedByInvalidRuleData) {
@@ -475,10 +457,10 @@
 
   sheet.AddCSSRules("#b {}");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules("b");
+  const HeapVector<RuleData>* rules = rule_set.IdRules("b");
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
-  EXPECT_FALSE(rules->at(0)->GetStyleScope());
+  EXPECT_EQ(0u, rule_set.ScopeIntervals().size());
 }
 
 TEST(RuleSetTest, StyleScope) {
@@ -486,10 +468,10 @@
 
   sheet.AddCSSRules("@scope (.a) { #b {} }");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* rules = rule_set.IdRules("b");
+  const HeapVector<RuleData>* rules = rule_set.IdRules("b");
   ASSERT_TRUE(rules);
   ASSERT_EQ(1u, rules->size());
-  EXPECT_TRUE(rules->at(0)->GetStyleScope());
+  EXPECT_EQ(1u, rule_set.ScopeIntervals().size());
 }
 
 TEST(RuleSetTest, NestedStyleScope) {
@@ -504,8 +486,8 @@
     }
   )CSS");
   RuleSet& rule_set = sheet.GetRuleSet();
-  const HeapVector<Member<const RuleData>>* a_rules = rule_set.IdRules("a");
-  const HeapVector<Member<const RuleData>>* b_rules = rule_set.IdRules("b");
+  const HeapVector<RuleData>* a_rules = rule_set.IdRules("a");
+  const HeapVector<RuleData>* b_rules = rule_set.IdRules("b");
 
   ASSERT_TRUE(a_rules);
   ASSERT_TRUE(b_rules);
@@ -513,15 +495,24 @@
   ASSERT_EQ(1u, a_rules->size());
   ASSERT_EQ(1u, b_rules->size());
 
-  EXPECT_TRUE(a_rules->at(0)->GetStyleScope());
-  EXPECT_FALSE(a_rules->at(0)->GetStyleScope()->Parent());
+  ASSERT_EQ(2u, rule_set.ScopeIntervals().size());
 
-  EXPECT_TRUE(b_rules->at(0)->GetStyleScope());
-  EXPECT_EQ(a_rules->at(0)->GetStyleScope(),
-            b_rules->at(0)->GetStyleScope()->Parent());
+  EXPECT_EQ(a_rules->at(0).GetPosition(),
+            rule_set.ScopeIntervals()[0].start_position);
+  const StyleScope* a_rule_scope = rule_set.ScopeIntervals()[0].value;
 
-  ASSERT_TRUE(b_rules->at(0)->GetStyleScope()->Parent());
-  EXPECT_FALSE(b_rules->at(0)->GetStyleScope()->Parent()->Parent());
+  EXPECT_EQ(b_rules->at(0).GetPosition(),
+            rule_set.ScopeIntervals()[1].start_position);
+  const StyleScope* b_rule_scope = rule_set.ScopeIntervals()[1].value;
+
+  EXPECT_NE(nullptr, a_rule_scope);
+  EXPECT_EQ(nullptr, a_rule_scope->Parent());
+
+  EXPECT_NE(nullptr, b_rule_scope);
+  EXPECT_EQ(a_rule_scope, b_rule_scope->Parent());
+
+  EXPECT_NE(nullptr, b_rule_scope->Parent());
+  EXPECT_EQ(nullptr, b_rule_scope->Parent()->Parent());
 }
 
 class RuleSetCascadeLayerTest : public SimTest {
@@ -551,7 +542,7 @@
   }
 
   const RuleData& GetIdRule(const AtomicString& key) {
-    return *GetRuleSet().IdRules(key)->front();
+    return GetRuleSet().IdRules(key)->front();
   }
 
   const CascadeLayer* GetLayerByIdRule(const AtomicString& key) {
diff --git a/third_party/blink/renderer/core/css/selector_statistics.h b/third_party/blink/renderer/core/css/selector_statistics.h
index 7539d30..7ccbec35 100644
--- a/third_party/blink/renderer/core/css/selector_statistics.h
+++ b/third_party/blink/renderer/core/css/selector_statistics.h
@@ -15,12 +15,11 @@
 struct RulePerfDataPerRequest {
   RulePerfDataPerRequest(const RuleData* r, bool f, bool m, base::TimeDelta e)
       : rule(r), fast_reject(f), did_match(m), elapsed(e) {}
-  Member<const RuleData> rule;
+  const RuleData* const rule;
   bool fast_reject;
   bool did_match;
   base::TimeDelta elapsed;
 
-  void Trace(Visitor* visitor) const { visitor->Trace(rule); }
   DISALLOW_NEW();
 };
 
@@ -39,6 +38,12 @@
  public:
   void ReserveCapacity(wtf_size_t size);
 
+  // NOTE: The rule must live for at least as long as the
+  // SelectorStatisticsCollector, as it is returned back in
+  // PerRuleStatistics. This is fine, because we throw away
+  // the statistics set at the end of CollectMatchingRulesForList
+  // to do our aggregation (on selectors), and in that time,
+  // we do not modify the rule buckets.
   void BeginCollectionForRule(const RuleData* rule);
   void EndCollectionForCurrentRule();
 
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index f8bce14..01be3ec 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1539,7 +1539,7 @@
     return;
 
   const RuleFeatureSet& features = GetRuleFeatureSet();
-  if (!features.NeedsHasInvalidation())
+  if (!features.NeedsHasInvalidationForInsertionOrRemoval())
     return;
 
   Element* previous_sibling = SelfOrPreviousSibling(node_before_change);
@@ -1568,14 +1568,15 @@
 
   if (descendants_possibly_affecting_has_state) {
     bool needs_has_invalidation_for_inserted_subtree =
-        features.NeedsHasInvalidationForElement(inserted_element);
+        features.NeedsHasInvalidationForInsertedOrRemovedElement(
+            inserted_element);
 
     // Do not stop subtree traversal early so that all the descendants have the
     // AncestorsOrAncestorSiblingsAffectedByHas flag set.
     for (Element& element : ElementTraversal::DescendantsOf(inserted_element)) {
       element.SetAncestorsOrAncestorSiblingsAffectedByHas();
       if (!needs_has_invalidation_for_inserted_subtree &&
-          features.NeedsHasInvalidationForElement(element)) {
+          features.NeedsHasInvalidationForInsertedOrRemovedElement(element)) {
         needs_has_invalidation_for_inserted_subtree = true;
       }
     }
@@ -1585,7 +1586,8 @@
       return;
     }
   } else {
-    if (features.NeedsHasInvalidationForElement(inserted_element)) {
+    if (features.NeedsHasInvalidationForInsertedOrRemovedElement(
+            inserted_element)) {
       InvalidateAncestorsOrSiblingsAffectedByHas(parent, previous_sibling);
       return;
     }
@@ -1608,7 +1610,7 @@
     return;
 
   const RuleFeatureSet& features = GetRuleFeatureSet();
-  if (!features.NeedsHasInvalidation())
+  if (!features.NeedsHasInvalidationForInsertionOrRemoval())
     return;
 
   Element* previous_sibling = SelfOrPreviousSibling(node_before_change);
@@ -1623,7 +1625,7 @@
 
   for (Element& element :
        ElementTraversal::InclusiveDescendantsOf(removed_element)) {
-    if (features.NeedsHasInvalidationForElement(element)) {
+    if (features.NeedsHasInvalidationForInsertedOrRemovedElement(element)) {
       InvalidateAncestorsOrSiblingsAffectedByHas(parent, previous_sibling);
       return;
     }
diff --git a/third_party/blink/renderer/core/exported/web_performance.cc b/third_party/blink/renderer/core/exported/web_performance.cc
index 8e86a750..9043f82d 100644
--- a/third_party/blink/renderer/core/exported/web_performance.cc
+++ b/third_party/blink/renderer/core/exported/web_performance.cc
@@ -265,6 +265,11 @@
   return private_->timing()->FirstInputTimestamp();
 }
 
+absl::optional<base::TimeTicks>
+WebPerformance::FirstInputTimestampAsMonotonicTime() const {
+  return private_->timing()->FirstInputTimestampAsMonotonicTime();
+}
+
 absl::optional<base::TimeDelta> WebPerformance::LongestInputDelay() const {
   return private_->timing()->LongestInputDelay();
 }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index d61feb8..711995e 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -244,6 +244,7 @@
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/ignore_paint_timing_scope.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
@@ -1828,6 +1829,11 @@
                                             bool skip_accelerated_content) {
   bool success = false;
   {
+    // Ignore paint timing while capturing a paint preview as it can change LCP
+    // see crbug.com/1323073.
+    IgnorePaintTimingScope scope;
+    IgnorePaintTimingScope::IncrementIgnoreDepth();
+
     Document::PaintPreviewScope paint_preview(
         *GetFrame()->GetDocument(),
         skip_accelerated_content
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 97a2863..6c69170 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2230,20 +2230,6 @@
     }
   }
 
-  // Inertness utility functions.
-  bool IsInert() const { return InertnessInternal() != Inertness::kNone; }
-  void SetIsInert(bool is_inert) {
-    if (InertnessInternal() == Inertness::kForced) {
-      DCHECK(is_inert);
-      return;
-    }
-    SetInertnessInternal(is_inert ? Inertness::kOverridable : Inertness::kNone);
-  }
-  bool IsForcedInert() const {
-    return InertnessInternal() == Inertness::kForced;
-  }
-  void SetIsForcedInert() { SetInertnessInternal(Inertness::kForced); }
-
   // Pointer-events utility functions.
   EPointerEvents UsedPointerEvents() const {
     if (IsInert())
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 2e85622..86b1e5c 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -1141,22 +1141,12 @@
     },
     {
       // https://html.spec.whatwg.org/multipage/interaction.html#inert
-      // The inertness is represented with one of these values:
-      // - 'none' means that the node is not inert (the default).
-      // - 'overridable' means that the node is inert, and descendants will
-      //    inherit by default, but may change their inertness to e.g. 'none'.
-      // - 'forced' means that that the node is inert, and its decendants
-      //   won't be able to change their inertness to something else.
-      // ComputedStyle doesn't directly expose the inertness. Instead, use
-      // IsInert(), SetIsInert(bool), IsForcedInert() or SetIsForcedInert().
-      name: "Inertness",
+      name: "IsInert",
       inherited: true,
-      field_template: "keyword",
-      type_name: "Inertness",
-      keywords: ["none", "overridable", "forced"],
-      default_value: "none",
+      field_template: "primitive",
+      type_name: "bool",
+      default_value: "false",
       field_group: "*",
-      computed_style_custom_functions: ["getter", "setter"],
     },
     {
       // We need to go through LayoutObject::SetStyle with
diff --git a/third_party/blink/renderer/core/timing/performance_timing.cc b/third_party/blink/renderer/core/timing/performance_timing.cc
index 539adda..70425d2 100644
--- a/third_party/blink/renderer/core/timing/performance_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_timing.cc
@@ -523,6 +523,15 @@
       interactive_detector->GetFirstInputTimestamp());
 }
 
+absl::optional<base::TimeTicks>
+PerformanceTiming::FirstInputTimestampAsMonotonicTime() const {
+  const InteractiveDetector* interactive_detector = GetInteractiveDetector();
+  if (!interactive_detector)
+    return absl::nullopt;
+
+  return interactive_detector->GetFirstInputTimestamp();
+}
+
 absl::optional<base::TimeDelta> PerformanceTiming::LongestInputDelay() const {
   const InteractiveDetector* interactive_detector = GetInteractiveDetector();
   if (!interactive_detector)
diff --git a/third_party/blink/renderer/core/timing/performance_timing.h b/third_party/blink/renderer/core/timing/performance_timing.h
index ea5da341..a05abd6 100644
--- a/third_party/blink/renderer/core/timing/performance_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_timing.h
@@ -163,6 +163,9 @@
   absl::optional<base::TimeDelta> FirstInputDelay() const;
   // The timestamp of the event whose delay is reported by FirstInputDelay().
   absl::optional<base::TimeDelta> FirstInputTimestamp() const;
+  // The timestamp of the event whose delay is reported by FirstInputDelay().
+  // Intended to be used for correlation with other events internal to blink.
+  absl::optional<base::TimeTicks> FirstInputTimestampAsMonotonicTime() const;
   // The longest duration between the hardware timestamp and being queued on the
   // main thread for the click, tap, key press, cancellable touchstart, or
   // pointer down followed by a pointer up.
diff --git a/third_party/blink/renderer/core/timing/responsiveness_metrics.cc b/third_party/blink/renderer/core/timing/responsiveness_metrics.cc
index 5fb46730..da3ddc2 100644
--- a/third_party/blink/renderer/core/timing/responsiveness_metrics.cc
+++ b/third_party/blink/renderer/core/timing/responsiveness_metrics.cc
@@ -5,7 +5,9 @@
 #include "third_party/blink/renderer/core/timing/responsiveness_metrics.h"
 #include <memory>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
+#include "base/strings/strcat.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -44,6 +46,14 @@
     blink::DOMHighResTimeStamp(500);
 // The length of the timer to flush entries from the time pointerup occurs.
 constexpr base::TimeDelta kFlushTimerLength = base::Seconds(1);
+// The name for the histogram which records interaction timings, and the names
+// of the variants for keyboard, click/tap, and drag interactions.
+const char kHistogramMaxEventDuration[] =
+    "Blink.Responsiveness.UserInteraction.MaxEventDuration";
+const char kHistogramAllTypes[] = ".AllTypes";
+const char kHistogramKeyboard[] = ".Keyboard";
+const char kHistogramTapOrClick[] = ".TapOrClick";
+const char kHistogramDrag[] = ".Drag";
 
 base::TimeDelta MaxEventDuration(
     const WTF::Vector<ResponsivenessMetrics::EventTimestamps>& timestamps) {
@@ -105,6 +115,13 @@
   return traced_value;
 }
 
+void LogResponsivenessHistogram(base::TimeDelta max_event_duration,
+                                const char* suffix) {
+  base::UmaHistogramCustomTimes(
+      base::StrCat({kHistogramMaxEventDuration, suffix}), max_event_duration,
+      base::Milliseconds(1), base::Seconds(60), 50);
+}
+
 }  // namespace
 
 ResponsivenessMetrics::ResponsivenessMetrics(
@@ -149,6 +166,19 @@
                                         total_event_duration, interaction_type),
                "frame", ToTraceValue(window->GetFrame()));
 
+  LogResponsivenessHistogram(max_event_duration, kHistogramAllTypes);
+  switch (interaction_type) {
+    case UserInteractionType::kKeyboard:
+      LogResponsivenessHistogram(max_event_duration, kHistogramKeyboard);
+      break;
+    case UserInteractionType::kTapOrClick:
+      LogResponsivenessHistogram(max_event_duration, kHistogramTapOrClick);
+      break;
+    case UserInteractionType::kDrag:
+      LogResponsivenessHistogram(max_event_duration, kHistogramDrag);
+      break;
+  }
+
   ukm::UkmRecorder* ukm_recorder = window->UkmRecorder();
   ukm::SourceId source_id = window->UkmSourceID();
   if (source_id != ukm::kInvalidSourceId &&
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index 5a49a4b..ca568bd 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/timing/window_performance.h"
 #include <cstdint>
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/test/trace_event_analyzer.h"
 #include "base/time/time.h"
@@ -168,6 +169,10 @@
     return scoped_fake_ukm_recorder_.recorder();
   }
 
+  const base::HistogramTester& GetHistogramTester() const {
+    return histogram_tester_;
+  }
+
   void PageVisibilityChanged(base::TimeTicks timestamp) {
     performance_->last_visibility_change_timestamp_ = timestamp;
   }
@@ -177,6 +182,7 @@
   std::unique_ptr<DummyPageHolder> page_holder_;
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
   ScopedFakeUkmRecorder scoped_fake_ukm_recorder_;
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(WindowPerformanceTest, LongTaskObserverInstrumentation) {
@@ -546,6 +552,16 @@
   GetUkmRecorder()->ExpectEntryMetric(
       ukm_entry,
       ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 0);
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
 }
 
 TEST_F(WindowPerformanceTest, HoldingDownAKey) {
@@ -613,6 +629,16 @@
         entry,
         ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 0);
   }
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 3);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 3);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
 }
 
 TEST_F(WindowPerformanceTest, PressMultipleKeys) {
@@ -729,6 +755,16 @@
   GetUkmRecorder()->ExpectEntryMetric(
       ukm_entry,
       ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 1);
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
 }
 
 TEST_F(WindowPerformanceTest, PageVisibilityChanged) {
@@ -837,6 +873,16 @@
   GetUkmRecorder()->ExpectEntryMetric(
       ukm_entry,
       ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 2);
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 1);
 }
 
 TEST_F(WindowPerformanceTest, Scroll) {
@@ -867,6 +913,16 @@
   auto entries = GetUkmRecorder()->GetEntriesByName(
       ukm::builders::Responsiveness_UserInteraction::kEntryName);
   EXPECT_EQ(0u, entries.size());
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
 }
 
 TEST_F(WindowPerformanceTest, TouchesWithoutClick) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 65968bca..4f0af5a1 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2607,20 +2607,17 @@
   if (style) {
     if (style->IsInert()) {
       if (ignored_reasons) {
-        // The 'inert' attribute sets forced inertness, which cannot be escaped
-        // by descendants (see details in computed_style_extra_fields.json5).
-        // So we only need to check InertRoot() if inertness is forced.
-        if (style->IsForcedInert()) {
-          const AXObject* inert_root_el = InertRoot();
-          if (inert_root_el == this) {
-            ignored_reasons->push_back(IgnoredReason(kAXInertElement));
-          } else {
-            ignored_reasons->push_back(
-                IgnoredReason(kAXInertSubtree, inert_root_el));
-          }
+        const AXObject* ax_inert_root = InertRoot();
+        if (ax_inert_root == this) {
+          ignored_reasons->push_back(IgnoredReason(kAXInertElement));
           return true;
         }
-        // If the inertness is overridable, it must have been set by a modal
+        if (ax_inert_root) {
+          ignored_reasons->push_back(
+              IgnoredReason(kAXInertSubtree, ax_inert_root));
+          return true;
+        }
+        // If there is no inert root, inertness must have been set by a modal
         // dialog or a fullscreen element (see AdjustStyleForInert).
         Document& document = GetNode()->GetDocument();
         if (HTMLDialogElement* dialog = document.ActiveModalDialog()) {
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
index e24ed18..e072fc2 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
@@ -91,21 +91,25 @@
     return base::File::FILE_ERROR_NO_SPACE;
   }
 
-  if (!capacity_tracker_->RequestFileCapacityChangeSync(write_end_offset)) {
-    return base::File::FILE_ERROR_NO_SPACE;
+  int64_t file_size_before = backing_file_.GetLength();
+  if (write_end_offset > file_size_before) {
+    // Attempt to pre-allocate quota. Do not attempt to write unless we have
+    // enough quota for the whole operation.
+    if (!capacity_tracker_->RequestFileCapacityChangeSync(write_end_offset))
+      return base::File::FILE_ERROR_NO_SPACE;
   }
 
   int result = backing_file_.Write(offset, reinterpret_cast<char*>(data.data()),
                                    write_size);
-  if (write_size == result) {
-    capacity_tracker_->CommitFileSizeChange(write_end_offset);
-    return result;
-  }
-  // If the operation failed, the previously requested capacity is not returned
-  // and no change in file size is recorded. This assumes that write operations
-  // either succeed or do not change the file's length, which is consistent with
-  // the way other file operations are implemented in File System Access code.
-  return base::File::GetLastFileError();
+  // The file size may not have changed after the write operation. `CheckAdd()`
+  // is not needed here since `result` is guaranteed to be no more than
+  // `write_size`.
+  int64_t new_file_size = std::max(file_size_before, offset + result);
+  capacity_tracker_->CommitFileSizeChange(new_file_size);
+
+  // Only return an error if no bytes were written. Partial writes should return
+  // the number of bytes written.
+  return result < 0 ? base::File::GetLastFileError() : result;
 }
 
 void FileSystemAccessRegularFileDelegate::GetLength(
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
index 8569cb2..aa84589 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
@@ -203,7 +203,14 @@
   }
 }
 
-TEST_F(ImageDecoderTest, DecoderCreationMixedCaseMimeType) {
+// TODO(1328861) - Test fails on Linux TSAN
+#if BUILDFLAG(IS_LINUX)
+#define MAYBE_DecoderCreationMixedCaseMimeType \
+  DISABLED_DecoderCreationMixedCaseMimeType
+#else
+#define MAYBE_DecoderCreationMixedCaseMimeType DecoderCreationMixedCaseMimeType
+#endif
+TEST_F(ImageDecoderTest, MAYBE_DecoderCreationMixedCaseMimeType) {
   V8TestingScope v8_scope;
   constexpr char kImageType[] = "image/GiF";
   EXPECT_TRUE(IsTypeSupported(&v8_scope, kImageType));
diff --git a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
index 5b67dd5..56d57e3 100644
--- a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
@@ -92,6 +92,7 @@
     const FontSelectionRequest& selection_request,
     const FontSelectionCapabilities& selection_capabilities,
     const OpticalSizing& optical_sizing,
+    TextRenderingMode text_rendering,
     FontOrientation orientation,
     const FontVariationSettings* variation_settings,
     const FontPalette* palette) {
@@ -290,7 +291,7 @@
   return FontPlatformData(std::move(return_typeface), std::string(), size,
                           synthetic_bold && !base_typeface_->isBold(),
                           synthetic_italic && !base_typeface_->isItalic(),
-                          orientation);
+                          text_rendering, orientation);
 }
 
 Vector<VariationAxis> FontCustomPlatformData::GetVariationAxes() const {
diff --git a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
index 8ea9c9f..6a5efce6 100644
--- a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
+++ b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_palette.h"
 #include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
 #include "third_party/blink/renderer/platform/fonts/opentype/variable_axes_names.h"
+#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -69,6 +70,7 @@
       const FontSelectionRequest&,
       const FontSelectionCapabilities&,
       const OpticalSizing& optical_sizing,
+      TextRenderingMode text_rendering,
       FontOrientation = FontOrientation::kHorizontal,
       const FontVariationSettings* = nullptr,
       const FontPalette* = nullptr);
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
index 566db2e..e5bd524 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
@@ -58,6 +58,7 @@
       synthetic_bold_(source.synthetic_bold_),
       synthetic_italic_(source.synthetic_italic_),
       avoid_embedded_bitmaps_(source.avoid_embedded_bitmaps_),
+      text_rendering_(source.text_rendering_),
       orientation_(source.orientation_)
 #if !BUILDFLAG(IS_MAC)
       ,
@@ -76,6 +77,7 @@
                        text_size,
                        src.synthetic_bold_,
                        src.synthetic_italic_,
+                       src.text_rendering_,
                        src.orientation_) {
 }
 
@@ -84,6 +86,7 @@
                                    float text_size,
                                    bool synthetic_bold,
                                    bool synthetic_italic,
+                                   TextRenderingMode text_rendering,
                                    FontOrientation orientation)
     : typeface_(typeface),
 #if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
@@ -92,12 +95,14 @@
       text_size_(text_size),
       synthetic_bold_(synthetic_bold),
       synthetic_italic_(synthetic_italic),
+      text_rendering_(text_rendering),
       orientation_(orientation) {
 #if !BUILDFLAG(IS_MAC)
   style_ = WebFontRenderStyle::GetDefault();
   auto system_style =
 #if !BUILDFLAG(IS_WIN)
-      QuerySystemRenderStyle(family_, text_size_, typeface_->fontStyle());
+      QuerySystemRenderStyle(family_, text_size_, typeface_->fontStyle(),
+                             text_rendering);
 
   // In web tests, ignore system preference for subpixel positioning,
   // or explicitly disable if requested.
@@ -135,11 +140,12 @@
          is_hash_table_deleted_value_ == a.is_hash_table_deleted_value_ &&
          synthetic_bold_ == a.synthetic_bold_ &&
          synthetic_italic_ == a.synthetic_italic_ &&
-         avoid_embedded_bitmaps_ == a.avoid_embedded_bitmaps_
+         avoid_embedded_bitmaps_ == a.avoid_embedded_bitmaps_ &&
+         text_rendering_ == a.text_rendering_ &&
 #if !BUILDFLAG(IS_MAC)
-         && style_ == a.style_
+         style_ == a.style_ &&
 #endif
-         && orientation_ == a.orientation_;
+         orientation_ == a.orientation_;
 }
 
 SkFontID FontPlatformData::UniqueID() const {
@@ -207,7 +213,8 @@
 WebFontRenderStyle FontPlatformData::QuerySystemRenderStyle(
     const std::string& family,
     float text_size,
-    SkFontStyle font_style) {
+    SkFontStyle font_style,
+    TextRenderingMode text_rendering) {
   WebFontRenderStyle result;
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
@@ -220,6 +227,20 @@
         family.data(), text_size, is_bold, is_italic,
         FontCache::DeviceScaleFactor(), &result);
   }
+
+  // If `text-rendering: geometric-precision`, use subpixel positioning.
+  // https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty
+  //
+  // TODO(crbug.com/1327530): For now, do this only for Linux/Cros to minimize
+  // the impact, and because they're the only platform that adjust these
+  // settings by different device scale factors. See the doc link in the crbug.
+  if (text_rendering == TextRenderingMode::kGeometricPrecision &&
+      result.use_anti_alias) {
+    result.use_subpixel_positioning = true;
+    result.use_hinting = false;
+    // 0 means HINTING_NONE, see |ConvertHinting| in font_service_app.cc.
+    result.hint_style = 0;
+  }
 #endif
 
   return result;
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.h b/third_party/blink/renderer/platform/fonts/font_platform_data.h
index 5135777..700ab149 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.h
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.h
@@ -79,6 +79,7 @@
                    float text_size,
                    bool synthetic_bold,
                    bool synthetic_italic,
+                   TextRenderingMode text_rendering,
                    FontOrientation = FontOrientation::kHorizontal);
   ~FontPlatformData();
 
@@ -118,6 +119,7 @@
     avoid_embedded_bitmaps_ = embedded_bitmaps;
   }
   bool operator==(const FontPlatformData&) const;
+  bool operator!=(const FontPlatformData& a) const { return !operator==(a); }
   FontPlatformData& operator=(const FontPlatformData&) = delete;
 
   bool IsHashTableDeletedValue() const { return is_hash_table_deleted_value_; }
@@ -147,7 +149,8 @@
 #if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
   WebFontRenderStyle QuerySystemRenderStyle(const std::string& family,
                                             float text_size,
-                                            SkFontStyle);
+                                            SkFontStyle,
+                                            TextRenderingMode text_rendering);
 #endif
 #if BUILDFLAG(IS_WIN)
   // TODO(https://crbug.com/808221): Remove and use QuerySystemRenderStyle()
@@ -165,6 +168,7 @@
   bool synthetic_bold_ = false;
   bool synthetic_italic_ = false;
   bool avoid_embedded_bitmaps_ = false;
+  TextRenderingMode text_rendering_ = TextRenderingMode::kAutoTextRendering;
   FontOrientation orientation_ = FontOrientation::kHorizontal;
 
  private:
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc b/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc
index 43793314..89a59d88 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc
@@ -114,4 +114,32 @@
   EXPECT_EQ(digest, expected_digest);
 }
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+TEST_F(FontPlatformDataTest, GeometricPrecision) {
+  const float saved_device_scale_factor = FontCache::DeviceScaleFactor();
+  sk_sp<SkTypeface> typeface = SkTypeface::MakeDefault();
+  const std::string name("name");
+  const auto create_font_platform_data = [&]() {
+    return FontPlatformData(typeface, name,
+                            /* text_size */ 10, /* synthetic_bold */ false,
+                            /* synthetic_italic */ false, kGeometricPrecision);
+  };
+
+  FontCache::SetDeviceScaleFactor(1.0f);
+  const FontPlatformData geometric_precision = create_font_platform_data();
+  const WebFontRenderStyle& geometric_precision_style =
+      geometric_precision.GetFontRenderStyle();
+  EXPECT_EQ(geometric_precision_style.use_subpixel_positioning, true);
+  EXPECT_EQ(geometric_precision_style.use_hinting, false);
+
+  // DSF=1.5 means it's high resolution (use_subpixel_positioning) for both
+  // Linux and ChromeOS. See |gfx GetFontRenderParams|.
+  FontCache::SetDeviceScaleFactor(1.5f);
+  const FontPlatformData geometric_precision_high = create_font_platform_data();
+  EXPECT_EQ(geometric_precision, geometric_precision_high);
+
+  FontCache::SetDeviceScaleFactor(saved_device_scale_factor);
+}
+#endif
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc b/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc
index 4ee31be..5e5ccc6 100644
--- a/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc
+++ b/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc
@@ -68,7 +68,8 @@
 
   auto font_data = std::make_unique<FontPlatformData>(
       std::move(typeface), std::string(), font_description.EffectiveFontSize(),
-      synthetic_bold, synthetic_italic, font_description.Orientation());
+      synthetic_bold, synthetic_italic, font_description.TextRendering(),
+      font_description.Orientation());
 
   return FontDataFromFontPlatformData(font_data.get(), kDoNotRetain);
 }
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
index 889c07c..e8b6055 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
@@ -226,7 +226,8 @@
       synthetic_bold,
       (traits & NSFontItalicTrait) &&
           !(substitute_font_traits & NSFontItalicTrait),
-      platform_data.Orientation(), font_description.FontOpticalSizing(),
+      font_description.TextRendering(), platform_data.Orientation(),
+      font_description.FontOpticalSizing(),
       nullptr);  // No variation paramaters in fallback.
 
   if (!alternate_font)
@@ -307,8 +308,8 @@
   // the returned FontPlatformData since it will not have a valid SkTypeface.
   std::unique_ptr<FontPlatformData> platform_data = FontPlatformDataFromNSFont(
       platform_font, size, font_description.SpecifiedSize(), synthetic_bold,
-      synthetic_italic, font_description.Orientation(),
-      font_description.FontOpticalSizing(),
+      synthetic_italic, font_description.TextRendering(),
+      font_description.Orientation(), font_description.FontOpticalSizing(),
       font_description.VariationSettings());
   if (!platform_data || !platform_data->Typeface()) {
     return nullptr;
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h
index ae1d8d6..34115627 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h
+++ b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_PLATFORM_DATA_MAC_H_
 
 #include "third_party/blink/renderer/platform/fonts/font_optical_sizing.h"
+#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
 #include <memory>
@@ -63,6 +64,7 @@
     float specified_size,
     bool synthetic_bold,
     bool synthetic_italic,
+    TextRenderingMode text_rendering,
     FontOrientation,
     OpticalSizing,
     FontVariationSettings*);
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
index 899540ba..ce6e0bf 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
@@ -167,6 +167,7 @@
     float specified_size,
     bool synthetic_bold,
     bool synthetic_italic,
+    TextRenderingMode text_rendering,
     FontOrientation orientation,
     OpticalSizing optical_sizing,
     FontVariationSettings* variation_settings) {
@@ -182,10 +183,11 @@
   }
 
   auto make_typeface_fontplatformdata = [&typeface, &size, &synthetic_bold,
-                                         &synthetic_italic, &orientation]() {
+                                         &synthetic_italic, &text_rendering,
+                                         &orientation]() {
     return std::make_unique<FontPlatformData>(
         std::move(typeface), std::string(), size, synthetic_bold,
-        synthetic_italic, orientation);
+        synthetic_italic, text_rendering, orientation);
   };
 
   wtf_size_t valid_configured_axes =
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.mm b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.mm
index 13afe7c..a1e64a70 100644
--- a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.mm
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.mm
@@ -19,8 +19,10 @@
 void ensureHasNativeSmallCaps(const String& font_family_name) {
   sk_sp<SkTypeface> test_typeface =
       SkTypeface::MakeFromName(font_family_name.Utf8().c_str(), SkFontStyle());
-  FontPlatformData font_platform_data(test_typeface, font_family_name.Utf8(),
-                                      16, false, false);
+  FontPlatformData font_platform_data(
+      test_typeface, font_family_name.Utf8(),
+      /* text_size */ 16, /* synthetic_bold */ false,
+      /* synthetic_italic */ false, TextRenderingMode::kAutoTextRendering);
   // System font names are magical. The family name of the system font in the
   // test below yields ".AppleSystemUIFont", which seems to be a generic role
   // name, because when it's actually instantiated with SkTypeface it ends up as
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
index 8da431d..045db161 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
@@ -28,6 +28,7 @@
     bool force_rotation = false) {
   FontPlatformData platform_data(
       SkTypeface::MakeDefault(), std::string(), 10, false, false,
+      TextRenderingMode::kAutoTextRendering,
       force_rotation ? FontOrientation::kVerticalUpright
                      : FontOrientation::kHorizontal);
   return SimpleFontData::Create(platform_data, nullptr);
diff --git a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
index 549fdd9..9a458ca 100644
--- a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
+++ b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
@@ -304,7 +304,7 @@
            font_description.IsSyntheticItalic()) &&
               font_description.GetFontSynthesisStyle() ==
                   FontDescription::kAutoFontSynthesisStyle,
-          font_description.Orientation());
+          font_description.TextRendering(), font_description.Orientation());
 
   font_platform_data->SetAvoidEmbeddedBitmaps(
       BitmapGlyphsBlockList::ShouldAvoidEmbeddedBitmapsForTypeface(*typeface));
diff --git a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
index 36c4db9..d2bbe79 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
+++ b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
@@ -701,7 +701,7 @@
       typeface, name.data(), font_size,
       synthetic_bold_requested && font_description.SyntheticBoldAllowed(),
       synthetic_italic_requested && font_description.SyntheticItalicAllowed(),
-      font_description.Orientation());
+      font_description.TextRendering(), font_description.Orientation());
 
   result->SetAvoidEmbeddedBitmaps(
       BitmapGlyphsBlockList::ShouldAvoidEmbeddedBitmapsForTypeface(*typeface));
diff --git a/third_party/blink/renderer/platform/testing/font_test_helpers.cc b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
index 043a0f6..8f7e6046 100644
--- a/third_party/blink/renderer/platform/testing/font_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
@@ -45,7 +45,8 @@
         font_description.IsSyntheticItalic() &&
             font_description.SyntheticItalicAllowed(),
         font_description.GetFontSelectionRequest(), normal_capabilities,
-        font_description.FontOpticalSizing(), font_description.Orientation());
+        font_description.FontOpticalSizing(), font_description.TextRendering(),
+        font_description.Orientation());
     return SimpleFontData::Create(platform_data, CustomFontData::Create());
   }
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 619285b..50945f4 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1533,6 +1533,11 @@
 crbug.com/1176933 virtual/gpu/fast/canvas/canvas-formattedtext-1.html [ Skip ]
 crbug.com/1176933 virtual/gpu/fast/canvas/canvas-formattedtext-2.html [ Skip ]
 
+# DPI-independent text
+crbug.com/1327530 fast/css/text-rendering.html [ Failure ]
+crbug.com/1327530 svg/text/current-text-position-initial.html [ Failure ]
+
+
 # Failures accumulated until 2021-3-3
 crbug.com/591099 accessibility/element-role-mapping-normal.html [ Failure ]
 crbug.com/591099 external/wpt/appmanifest/icons-member/icons-member-cors-fail-manual.sub.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has.html
new file mode 100644
index 0000000..4b0225eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>CSS Selectors Invalidation: :is() in :has() argument</title>
+<link rel="author" title="Byungwoo Lee" href="blee@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+div { color: grey }
+.red:has(#descendant:is(.a .b)) { color: red }
+.green:has(#descendant:is(.c ~ .d .e)) { color: green }
+.blue:has(~ #indirect_next:is(.f ~ .g)) { color: blue }
+.yellow:has(~ #indirect_next:is(.h .i)) { color: yellow }
+.purple:has(~ #indirect_next:is(.j ~ .k .l)) { color: purple }
+.orange:has(#descendant:is(:is(.m, .n) .o)) { color: orange }
+</style>
+<div>
+  <div id="parent_previous"></div>
+  <div id="parent" class="d k">
+    <div id="previous"></div>
+    <div id="has_scope" class="d">
+      <div id="child_previous"></div>
+      <div id="child" class="d">
+        <div id="descendant" class="b e o"></div>
+      </div>
+    </div>
+    <div id="direct_next"></div>
+    <div id="indirect_next" class="g i l"></div>
+  </div>
+</div>
+<script>
+const grey = "rgb(128, 128, 128)";
+const red = "rgb(255, 0, 0)";
+const green = "rgb(0, 128, 0)";
+const blue = "rgb(0, 0, 255)";
+const yellow = "rgb(255, 255, 0)";
+const purple = "rgb(128, 0, 128)";
+const orange = "rgb(255, 165, 0)";
+
+function changeAndTest(operation, class_name, element_id,
+                       selector, matches_result, scope_color) {
+  let element = document.getElementById(element_id);
+  assert_equals(element ? element.id : "", element_id);
+  let message_prefix = [
+      "[", selector, "]",
+      ["#", element.id, ".classList.", operation, "('", class_name, "')"].join(""),
+      ": "].join(" ");
+  if (operation == "add") {
+    element.classList.add(class_name);
+  } else {
+    assert_equals(operation, "remove");
+    element.classList.remove(class_name);
+  }
+  test(function() {
+      assert_equals(has_scope.matches(selector), matches_result);
+  }, message_prefix + "check matches (" + matches_result + ")");
+  test(function() {
+      assert_equals(getComputedStyle(has_scope).color, scope_color);
+  }, message_prefix + "check #has_scope color");
+}
+
+assert_equals(getComputedStyle(has_scope).color, grey);
+
+let selector = ".red:has(#descendant:is(.a .b))";
+changeAndTest("add", "red", "has_scope", selector, false, grey);
+changeAndTest("add", "a", "parent", selector, true, red);
+changeAndTest("remove", "a", "parent", selector, false, grey);
+changeAndTest("add", "a", "has_scope", selector, true, red);
+changeAndTest("remove", "a", "has_scope", selector, false, grey);
+changeAndTest("add", "a", "child", selector, true, red);
+changeAndTest("remove", "a", "child", selector, false, grey);
+changeAndTest("remove", "red", "has_scope", selector, false, grey);
+
+selector = ".green:has(#descendant:is(.c ~ .d .e))";
+changeAndTest("add", "green", "has_scope", selector, false, grey);
+changeAndTest("add", "c", "parent_previous", selector, true, green);
+changeAndTest("remove", "c", "parent_previous", selector, false, grey);
+changeAndTest("add", "c", "previous", selector, true, green);
+changeAndTest("remove", "c", "previous", selector, false, grey);
+changeAndTest("add", "c", "child_previous", selector, true, green);
+changeAndTest("remove", "c", "child_previous", selector, false, grey);
+changeAndTest("remove", "green", "has_scope", selector, false, grey);
+
+selector = ".blue:has(~ #indirect_next:is(.f ~ .g))";
+changeAndTest("add", "blue", "has_scope", selector, false, grey);
+changeAndTest("add", "f", "previous", selector, true, blue);
+changeAndTest("remove", "f", "previous", selector, false, grey);
+changeAndTest("add", "f", "has_scope", selector, true, blue);
+changeAndTest("remove", "f", "has_scope", selector, false, grey);
+changeAndTest("add", "f", "direct_next", selector, true, blue);
+changeAndTest("remove", "f", "direct_next", selector, false, grey);
+changeAndTest("remove", "blue", "has_scope", selector, false, grey);
+
+selector = ".yellow:has(~ #indirect_next:is(.h .i))"
+changeAndTest("add", "yellow", "has_scope", selector, false, grey);
+changeAndTest("add", "h", "parent", selector, true, yellow);
+changeAndTest("remove", "h", "parent", selector, false, grey);
+changeAndTest("remove", "yellow", "has_scope", selector, false, grey);
+
+selector = ".purple:has(~ #indirect_next:is(.j ~ .k .l))"
+changeAndTest("add", "purple", "has_scope", selector, false, grey);
+changeAndTest("add", "j", "parent_previous", selector, true, purple);
+changeAndTest("remove", "j", "parent_previous", selector, false, grey);
+changeAndTest("remove", "purple", "has_scope", selector, false, grey);
+
+selector = ".orange:has(#descendant:is(:is(.m, .n) .o))";
+changeAndTest("add", "orange", "has_scope", selector, false, grey);
+changeAndTest("add", "m", "parent", selector, true, orange);
+changeAndTest("remove", "m", "parent", selector, false, grey);
+changeAndTest("add", "n", "parent", selector, true, orange);
+changeAndTest("remove", "n", "parent", selector, false, grey);
+changeAndTest("add", "m", "has_scope", selector, true, orange);
+changeAndTest("remove", "m", "has_scope", selector, false, grey);
+changeAndTest("add", "n", "has_scope", selector, true, orange);
+changeAndTest("remove", "n", "has_scope", selector, false, grey);
+changeAndTest("add", "m", "child", selector, true, orange);
+changeAndTest("remove", "m", "child", selector, false, grey);
+changeAndTest("add", "n", "child", selector, true, orange);
+changeAndTest("remove", "n", "child", selector, false, grey);
+changeAndTest("remove", "orange", "has_scope", selector, false, grey);
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has.html
new file mode 100644
index 0000000..b99b309
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>CSS Selectors Invalidation: :not(&lt;complex-selector&gt;) in :has() argument (complex selector)</title>
+<link rel="author" title="Byungwoo Lee" href="blee@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+div { color: grey }
+.red:has(#descendant:not(.a .b)) { color: red }
+.green:has(#descendant:not(.c ~ .d .e)) { color: green }
+.blue:has(~ #indirect_next:not(.f ~ .g)) { color: blue }
+.yellow:has(~ #indirect_next:not(.h .i)) { color: yellow }
+.purple:has(~ #indirect_next:not(.j ~ .k .l)) { color: purple }
+.orange:has(#descendant:not(.m:not(.n) .o)) { color: orange }
+</style>
+<div>
+  <div id="parent_previous"></div>
+  <div id="parent" class="d k">
+    <div id="previous"></div>
+    <div id="has_scope" class="d">
+      <div id="child_previous"></div>
+      <div id="child" class="d">
+        <div id="descendant" class="b e o"></div>
+      </div>
+    </div>
+    <div id="direct_next"></div>
+    <div id="indirect_next" class="g i l"></div>
+  </div>
+</div>
+<script>
+const grey = "rgb(128, 128, 128)";
+const red = "rgb(255, 0, 0)";
+const green = "rgb(0, 128, 0)";
+const blue = "rgb(0, 0, 255)";
+const yellow = "rgb(255, 255, 0)";
+const purple = "rgb(128, 0, 128)";
+const orange = "rgb(255, 165, 0)";
+
+function changeAndTest(operation, class_name, element_id,
+                       selector, matches_result, scope_color) {
+  let element = document.getElementById(element_id);
+  assert_equals(element ? element.id : "", element_id);
+  let message_prefix = [
+      "[", selector, "]",
+      ["#", element.id, ".classList.", operation, "('", class_name, "')"].join(""),
+      ": "].join(" ");
+  if (operation == "add") {
+    element.classList.add(class_name);
+  } else {
+    assert_equals(operation, "remove");
+    element.classList.remove(class_name);
+  }
+  test(function() {
+      assert_equals(has_scope.matches(selector), matches_result);
+  }, message_prefix + "check matches (" + matches_result + ")");
+  test(function() {
+      assert_equals(getComputedStyle(has_scope).color, scope_color);
+  }, message_prefix + "check #has_scope color");
+}
+
+assert_equals(getComputedStyle(has_scope).color, grey);
+
+let selector = ".red:has(#descendant:not(.a .b))";
+changeAndTest("add", "red", "has_scope", selector, true, red);
+changeAndTest("add", "a", "parent", selector, false, grey);
+changeAndTest("remove", "a", "parent", selector, true, red);
+changeAndTest("add", "a", "has_scope", selector, false, grey);
+changeAndTest("remove", "a", "has_scope", selector, true, red);
+changeAndTest("add", "a", "child", selector, false, grey);
+changeAndTest("remove", "a", "child", selector, true, red);
+changeAndTest("remove", "red", "has_scope", selector, false, grey);
+
+selector = ".green:has(#descendant:not(.c ~ .d .e))";
+changeAndTest("add", "green", "has_scope", selector, true, green);
+changeAndTest("add", "c", "parent_previous", selector, false, grey);
+changeAndTest("remove", "c", "parent_previous", selector, true, green);
+changeAndTest("add", "c", "previous", selector, false, grey);
+changeAndTest("remove", "c", "previous", selector, true, green);
+changeAndTest("add", "c", "child_previous", selector, false, grey);
+changeAndTest("remove", "c", "child_previous", selector, true, green);
+changeAndTest("remove", "green", "has_scope", selector, false, grey);
+
+selector = ".blue:has(~ #indirect_next:not(.f ~ .g))";
+changeAndTest("add", "blue", "has_scope", selector, true, blue);
+changeAndTest("add", "f", "previous", selector, false, grey);
+changeAndTest("remove", "f", "previous", selector, true, blue);
+changeAndTest("add", "f", "has_scope", selector, false, grey);
+changeAndTest("remove", "f", "has_scope", selector, true, blue);
+changeAndTest("add", "f", "direct_next", selector, false, grey);
+changeAndTest("remove", "f", "direct_next", selector, true, blue);
+changeAndTest("remove", "blue", "has_scope", selector, false, grey);
+
+selector = ".yellow:has(~ #indirect_next:not(.h .i))"
+changeAndTest("add", "yellow", "has_scope", selector, true, yellow);
+changeAndTest("add", "h", "parent", selector, false, grey);
+changeAndTest("remove", "h", "parent", selector, true, yellow);
+changeAndTest("remove", "yellow", "has_scope", selector, false, grey);
+
+selector = ".purple:has(~ #indirect_next:not(.j ~ .k .l))"
+changeAndTest("add", "purple", "has_scope", selector, true, purple);
+changeAndTest("add", "j", "parent_previous", selector, false, grey);
+changeAndTest("remove", "j", "parent_previous", selector, true, purple);
+changeAndTest("remove", "purple", "has_scope", selector, false, grey);
+
+selector = ".orange:has(#descendant:not(.m:not(.n) .o))";
+changeAndTest("add", "orange", "has_scope", selector, true, orange);
+changeAndTest("add", "m", "parent", selector, false, grey);
+changeAndTest("add", "n", "parent", selector, true, orange);
+changeAndTest("remove", "n", "parent", selector, false, grey);
+changeAndTest("remove", "m", "parent", selector, true, orange);
+changeAndTest("add", "m", "has_scope", selector, false, grey);
+changeAndTest("add", "n", "has_scope", selector, true, orange);
+changeAndTest("remove", "n", "has_scope", selector, false, grey);
+changeAndTest("remove", "m", "has_scope", selector, true, orange);
+changeAndTest("add", "m", "child", selector, false, grey);
+changeAndTest("add", "n", "child", selector, true, orange);
+changeAndTest("remove", "n", "child", selector, false, grey);
+changeAndTest("remove", "m", "child", selector, true, orange);
+changeAndTest("remove", "orange", "has_scope", selector, false, grey);
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/inert/inert-with-modal-dialog-001.html b/third_party/blink/web_tests/external/wpt/inert/inert-with-modal-dialog-001.html
index a31fa15..aa0c29c 100644
--- a/third_party/blink/web_tests/external/wpt/inert/inert-with-modal-dialog-001.html
+++ b/third_party/blink/web_tests/external/wpt/inert/inert-with-modal-dialog-001.html
@@ -3,7 +3,7 @@
 <title>Interaction of 'inert' attribute with modal dialog</title>
 <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
-<meta name="assert" content="Checks that being part of a modal dialog does not protect a node from being marked inert by an 'inert' attribute.">
+<meta name="assert" content="Checks that a modal dialog escapes inertness from ancestors.">
 <div id="log"></div>
 <div id="wrapper">
   wrapper
@@ -51,6 +51,6 @@
 test(function() {
   wrapper.inert = true;
   this.add_cleanup(() => { wrapper.inert = false; });
-  checkSelection("");
-}, "If an ancestor of the dialog has the 'inert' attribute, everything becomes inert");
+  checkSelection("dialog child");
+}, "If an ancestor of the dialog has the 'inert' attribute, the dialog escapes inertness");
 </script>
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-argument-with-explicit-scope.tentative-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-argument-with-explicit-scope.tentative-expected.txt
deleted file mode 100644
index d18a2116..0000000
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-argument-with-explicit-scope.tentative-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-PASS :has(:scope) matches expected elements on scope1
-PASS :has(:scope .c) matches expected elements on scope1
-PASS :has(.a :scope) matches expected elements on scope1
-PASS .a:has(:scope) .c matches expected elements on scope1
-PASS .a:has(:scope) .c and :is(.a :scope .c) returns same elements on scope1
-PASS .a:has(:scope) .c matches expected elements on scope2
-PASS .a:has(:scope) .c and :is(.a :scope .c) returns same elements on scope2
-FAIL .c:has(:is(:scope .d)) matches expected elements on scope1 assert_equals: expected "d02,d03" but got ""
-FAIL .c:has(:is(:scope .d)) and :scope .c:has(.d) returns same elements on scope1 assert_equals: expected "d02,d03" but got ""
-FAIL .c:has(:is(:scope .d)) and .c:has(.d) returns same elements on scope1 assert_equals: expected "d02,d03" but got ""
-PASS .c:has(:is(:scope .d)) matches expected elements on scope2
-PASS .c:has(:is(:scope .d)) and :scope .c:has(.d) returns same elements on scope2
-PASS .c:has(:is(:scope .d)) and .c:has(.d) returns same elements on scope2
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-basic-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-basic-expected.txt
index 5c4cfdf..3af197a 100644
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-basic-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/has-basic-expected.txt
@@ -6,8 +6,8 @@
 PASS .parent:has(.target) matches expected elements
 PASS :has(.sibling ~ .target) matches expected elements
 PASS .parent:has(.sibling ~ .target) matches expected elements
-FAIL :has(:is(.target ~ .sibling .descendant)) matches expected elements assert_equals: expected "a,h,j" but got ""
-FAIL .parent:has(:is(.target ~ .sibling .descendant)) matches expected elements assert_equals: expected "h" but got ""
+PASS :has(:is(.target ~ .sibling .descendant)) matches expected elements
+PASS .parent:has(:is(.target ~ .sibling .descendant)) matches expected elements
 PASS .sibling:has(.descendant) ~ .target matches expected elements
 FAIL :has(.sibling:has(.descendant) ~ .target) matches expected elements assert_equals: expected "a,b" but got ""
 FAIL :has(.sibling:has(.descendant) ~ .target) ~ .parent > .descendant matches expected elements assert_equals: expected "g,i,j" but got ""
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has-expected.txt
new file mode 100644
index 0000000..c94f96e8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/is-pseudo-containing-complex-in-has-expected.txt
@@ -0,0 +1,96 @@
+This is a testharness.js-based test.
+Found 92 tests; 80 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.add('red') : check matches (false)
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.add('red') : check #has_scope color
+PASS [ .red:has(#descendant:is(.a .b)) ] #parent.classList.add('a') : check matches (true)
+FAIL [ .red:has(#descendant:is(.a .b)) ] #parent.classList.add('a') : check #has_scope color assert_equals: expected "rgb(255, 0, 0)" but got "rgb(128, 128, 128)"
+PASS [ .red:has(#descendant:is(.a .b)) ] #parent.classList.remove('a') : check matches (false)
+PASS [ .red:has(#descendant:is(.a .b)) ] #parent.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.add('a') : check matches (true)
+FAIL [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.add('a') : check #has_scope color assert_equals: expected "rgb(255, 0, 0)" but got "rgb(128, 128, 128)"
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.remove('a') : check matches (false)
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:is(.a .b)) ] #child.classList.add('a') : check matches (true)
+PASS [ .red:has(#descendant:is(.a .b)) ] #child.classList.add('a') : check #has_scope color
+PASS [ .red:has(#descendant:is(.a .b)) ] #child.classList.remove('a') : check matches (false)
+PASS [ .red:has(#descendant:is(.a .b)) ] #child.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.remove('red') : check matches (false)
+PASS [ .red:has(#descendant:is(.a .b)) ] #has_scope.classList.remove('red') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #has_scope.classList.add('green') : check matches (false)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #has_scope.classList.add('green') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #parent_previous.classList.add('c') : check matches (true)
+FAIL [ .green:has(#descendant:is(.c ~ .d .e)) ] #parent_previous.classList.add('c') : check #has_scope color assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #parent_previous.classList.remove('c') : check matches (false)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #parent_previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #previous.classList.add('c') : check matches (true)
+FAIL [ .green:has(#descendant:is(.c ~ .d .e)) ] #previous.classList.add('c') : check #has_scope color assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #previous.classList.remove('c') : check matches (false)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #child_previous.classList.add('c') : check matches (true)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #child_previous.classList.add('c') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #child_previous.classList.remove('c') : check matches (false)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #child_previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #has_scope.classList.remove('green') : check matches (false)
+PASS [ .green:has(#descendant:is(.c ~ .d .e)) ] #has_scope.classList.remove('green') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.add('blue') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.add('blue') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #previous.classList.add('f') : check matches (true)
+FAIL [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #previous.classList.add('f') : check #has_scope color assert_equals: expected "rgb(0, 0, 255)" but got "rgb(128, 128, 128)"
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #previous.classList.remove('f') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #previous.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.add('f') : check matches (true)
+FAIL [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.add('f') : check #has_scope color assert_equals: expected "rgb(0, 0, 255)" but got "rgb(128, 128, 128)"
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.remove('f') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #direct_next.classList.add('f') : check matches (true)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #direct_next.classList.add('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #direct_next.classList.remove('f') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #direct_next.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.remove('blue') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:is(.f ~ .g)) ] #has_scope.classList.remove('blue') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #has_scope.classList.add('yellow') : check matches (false)
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #has_scope.classList.add('yellow') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #parent.classList.add('h') : check matches (true)
+FAIL [ .yellow:has(~ #indirect_next:is(.h .i)) ] #parent.classList.add('h') : check #has_scope color assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)"
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #parent.classList.remove('h') : check matches (false)
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #parent.classList.remove('h') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #has_scope.classList.remove('yellow') : check matches (false)
+PASS [ .yellow:has(~ #indirect_next:is(.h .i)) ] #has_scope.classList.remove('yellow') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #has_scope.classList.add('purple') : check matches (false)
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #has_scope.classList.add('purple') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #parent_previous.classList.add('j') : check matches (true)
+FAIL [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #parent_previous.classList.add('j') : check #has_scope color assert_equals: expected "rgb(128, 0, 128)" but got "rgb(128, 128, 128)"
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #parent_previous.classList.remove('j') : check matches (false)
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #parent_previous.classList.remove('j') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #has_scope.classList.remove('purple') : check matches (false)
+PASS [ .purple:has(~ #indirect_next:is(.j ~ .k .l)) ] #has_scope.classList.remove('purple') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('orange') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('orange') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.add('m') : check matches (true)
+FAIL [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.add('m') : check #has_scope color assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.remove('m') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.add('n') : check matches (true)
+FAIL [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.add('n') : check #has_scope color assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.remove('n') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #parent.classList.remove('n') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('m') : check matches (true)
+FAIL [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('m') : check #has_scope color assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('m') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('n') : check matches (true)
+FAIL [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.add('n') : check #has_scope color assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('n') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('n') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.add('m') : check matches (true)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.add('m') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.remove('m') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.add('n') : check matches (true)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.add('n') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.remove('n') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #child.classList.remove('n') : check #has_scope color
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('orange') : check matches (false)
+PASS [ .orange:has(#descendant:is(:is(.m, .n) .o)) ] #has_scope.classList.remove('orange') : check #has_scope color
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/logical-combinations-in-has-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/logical-combinations-in-has-expected.txt
new file mode 100644
index 0000000..5695721
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/logical-combinations-in-has-expected.txt
@@ -0,0 +1,34 @@
+This is a testharness.js-based test.
+PASS Initial color
+FAIL Check :not() for has-scope parent change: Add .c assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
+PASS Check :not() for has-scope parent change: Remove .c
+FAIL Check :not() for has-scope change: Add .c assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
+PASS Check :not() for has-scope change: Remove .c
+PASS Check :not() for has-scope descendant change: Add .c
+PASS Check :not() for has-scope descendant change: Remove .c
+FAIL Check :is() for has-scope parent change: Add .g1 assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope parent change: Remove .g1
+FAIL Check :is() for has-scope change: Add .g1 assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope change: Remove .g1
+PASS Check :is() for has-scope descendant change: Add .g1
+PASS Check :is() for has-scope descendant change: Remove .g1
+FAIL Check :where() for has-scope parent change: Add .g2 assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)"
+PASS Check :where() for has-scope parent change: Remove .g2
+FAIL Check :where() for has-scope change: Add .g2 assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)"
+PASS Check :where() for has-scope change: Remove .g2
+PASS Check :where() for has-scope descendant change: Add .g2
+PASS Check :where() for has-scope descendant change: Remove .g2
+FAIL Check :is() for has-scope parent sibling change: Add .k assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope parent sibling change: Remove .k
+FAIL Check :is() for has-scope sibling change: Add .k assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope sibling change: Remove .k
+PASS Check :is() for has-scope descendant sibling change: Add .k
+PASS Check :is() for has-scope descendant sibling change: Remove .k
+FAIL Check :is() for has-scope previous sibling change: Add .p assert_equals: expected "rgb(0, 0, 255)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope previous sibling change: Remove .p
+FAIL Check :is() for has-scope change (for sibling relationship): Add .p assert_equals: expected "rgb(0, 0, 255)" but got "rgb(128, 128, 128)"
+PASS Check :is() for has-scope change (for sibling relationship): Remove .p
+PASS Check :is() for has-scope next sibling change: Add .p
+PASS Check :is() for has-scope next sibling change: Remove .p
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has-expected.txt
new file mode 100644
index 0000000..4a306fc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/invalidation/not-pseudo-containing-complex-in-has-expected.txt
@@ -0,0 +1,96 @@
+This is a testharness.js-based test.
+Found 92 tests; 80 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.add('red') : check matches (true)
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.add('red') : check #has_scope color
+PASS [ .red:has(#descendant:not(.a .b)) ] #parent.classList.add('a') : check matches (false)
+FAIL [ .red:has(#descendant:not(.a .b)) ] #parent.classList.add('a') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
+PASS [ .red:has(#descendant:not(.a .b)) ] #parent.classList.remove('a') : check matches (true)
+PASS [ .red:has(#descendant:not(.a .b)) ] #parent.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.add('a') : check matches (false)
+FAIL [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.add('a') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.remove('a') : check matches (true)
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:not(.a .b)) ] #child.classList.add('a') : check matches (false)
+PASS [ .red:has(#descendant:not(.a .b)) ] #child.classList.add('a') : check #has_scope color
+PASS [ .red:has(#descendant:not(.a .b)) ] #child.classList.remove('a') : check matches (true)
+PASS [ .red:has(#descendant:not(.a .b)) ] #child.classList.remove('a') : check #has_scope color
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.remove('red') : check matches (false)
+PASS [ .red:has(#descendant:not(.a .b)) ] #has_scope.classList.remove('red') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #has_scope.classList.add('green') : check matches (true)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #has_scope.classList.add('green') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #parent_previous.classList.add('c') : check matches (false)
+FAIL [ .green:has(#descendant:not(.c ~ .d .e)) ] #parent_previous.classList.add('c') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(0, 128, 0)"
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #parent_previous.classList.remove('c') : check matches (true)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #parent_previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #previous.classList.add('c') : check matches (false)
+FAIL [ .green:has(#descendant:not(.c ~ .d .e)) ] #previous.classList.add('c') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(0, 128, 0)"
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #previous.classList.remove('c') : check matches (true)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #child_previous.classList.add('c') : check matches (false)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #child_previous.classList.add('c') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #child_previous.classList.remove('c') : check matches (true)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #child_previous.classList.remove('c') : check #has_scope color
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #has_scope.classList.remove('green') : check matches (false)
+PASS [ .green:has(#descendant:not(.c ~ .d .e)) ] #has_scope.classList.remove('green') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.add('blue') : check matches (true)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.add('blue') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #previous.classList.add('f') : check matches (false)
+FAIL [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #previous.classList.add('f') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(0, 0, 255)"
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #previous.classList.remove('f') : check matches (true)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #previous.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.add('f') : check matches (false)
+FAIL [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.add('f') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(0, 0, 255)"
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.remove('f') : check matches (true)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #direct_next.classList.add('f') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #direct_next.classList.add('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #direct_next.classList.remove('f') : check matches (true)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #direct_next.classList.remove('f') : check #has_scope color
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.remove('blue') : check matches (false)
+PASS [ .blue:has(~ #indirect_next:not(.f ~ .g)) ] #has_scope.classList.remove('blue') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #has_scope.classList.add('yellow') : check matches (true)
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #has_scope.classList.add('yellow') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #parent.classList.add('h') : check matches (false)
+FAIL [ .yellow:has(~ #indirect_next:not(.h .i)) ] #parent.classList.add('h') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 255, 0)"
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #parent.classList.remove('h') : check matches (true)
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #parent.classList.remove('h') : check #has_scope color
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #has_scope.classList.remove('yellow') : check matches (false)
+PASS [ .yellow:has(~ #indirect_next:not(.h .i)) ] #has_scope.classList.remove('yellow') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #has_scope.classList.add('purple') : check matches (true)
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #has_scope.classList.add('purple') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #parent_previous.classList.add('j') : check matches (false)
+FAIL [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #parent_previous.classList.add('j') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(128, 0, 128)"
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #parent_previous.classList.remove('j') : check matches (true)
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #parent_previous.classList.remove('j') : check #has_scope color
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #has_scope.classList.remove('purple') : check matches (false)
+PASS [ .purple:has(~ #indirect_next:not(.j ~ .k .l)) ] #has_scope.classList.remove('purple') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('orange') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('orange') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.add('m') : check matches (false)
+FAIL [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.add('m') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.add('n') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.add('n') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.remove('n') : check matches (false)
+FAIL [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.remove('n') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.remove('m') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #parent.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('m') : check matches (false)
+FAIL [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('m') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('n') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.add('n') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('n') : check matches (false)
+FAIL [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('n') : check #has_scope color assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('m') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.add('m') : check matches (false)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.add('m') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.add('n') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.add('n') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.remove('n') : check matches (false)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.remove('n') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.remove('m') : check matches (true)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #child.classList.remove('m') : check #has_scope color
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('orange') : check matches (false)
+PASS [ .orange:has(#descendant:not(.m:not(.n) .o)) ] #has_scope.classList.remove('orange') : check #has_scope color
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/parsing/parse-has-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/parsing/parse-has-expected.txt
index de256753..bd13fe7 100644
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/parsing/parse-has-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/selectors/parsing/parse-has-expected.txt
@@ -16,8 +16,8 @@
 PASS ".a .b:has(.c .d)" should be a valid selector
 PASS ".a .b:has(.c .d) .e" should be a valid selector
 FAIL ".a:has(.b:has(.c))" should be a valid selector assert_equals: serialization should be canonical expected ".a:has(.b:has(.c))" but got ".a:has()"
-FAIL ".a:has(.b:is(.c .d))" should be a valid selector assert_equals: serialization should be canonical expected ".a:has(.b:is(.c .d))" but got ".a:has()"
-FAIL ".a:has(.b:is(.c:has(.d) .e))" should be a valid selector assert_equals: serialization should be canonical expected ".a:has(.b:is(.c:has(.d) .e))" but got ".a:has()"
+PASS ".a:has(.b:is(.c .d))" should be a valid selector
+FAIL ".a:has(.b:is(.c:has(.d) .e))" should be a valid selector assert_equals: serialization should be canonical expected ".a:has(.b:is(.c:has(.d) .e))" but got ".a:has(.b:is())"
 PASS ".a:is(.b:has(.c) .d)" should be a valid selector
 PASS ".a:not(:has(.b))" should be a valid selector
 PASS ".a:has(:not(.b))" should be a valid selector
diff --git a/third_party/blink/web_tests/platform/linux/fast/css/text-rendering-expected.png b/third_party/blink/web_tests/platform/linux/fast/css/text-rendering-expected.png
index 41f97b2..0b03e98 100644
--- a/third_party/blink/web_tests/platform/linux/fast/css/text-rendering-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/css/text-rendering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/W3C-SVG-1.1/filters-conv-01-f-expected.png b/third_party/blink/web_tests/platform/linux/svg/W3C-SVG-1.1/filters-conv-01-f-expected.png
index 813c1dd..231d04eb 100644
--- a/third_party/blink/web_tests/platform/linux/svg/W3C-SVG-1.1/filters-conv-01-f-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/W3C-SVG-1.1/filters-conv-01-f-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/text/current-text-position-initial-expected.png b/third_party/blink/web_tests/platform/linux/svg/text/current-text-position-initial-expected.png
index 006c20e0..dca26f1 100644
--- a/third_party/blink/web_tests/platform/linux/svg/text/current-text-position-initial-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/text/current-text-position-initial-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/text/scaling-font-with-geometric-precision-expected.png b/third_party/blink/web_tests/platform/linux/svg/text/scaling-font-with-geometric-precision-expected.png
index a61b034..5139bb8 100644
--- a/third_party/blink/web_tests/platform/linux/svg/text/scaling-font-with-geometric-precision-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/text/scaling-font-with-geometric-precision-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/text/text-with-geometric-precision-expected.png b/third_party/blink/web_tests/platform/linux/svg/text/text-with-geometric-precision-expected.png
index 7068a17..4dda7b1a 100644
--- a/third_party/blink/web_tests/platform/linux/svg/text/text-with-geometric-precision-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/text/text-with-geometric-precision-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emphasis-combined-text-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emphasis-combined-text-expected.png
index 760f34e..2ce67d0 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emphasis-combined-text-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emphasis-combined-text-expected.png
Binary files differ
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 558b26c..a59aba8c 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 446363255
-Date: 2022/05/04 UTC
+Version: 449569319
+Date: 2022/05/18 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/system_profile.proto b/third_party/metrics_proto/system_profile.proto
index d13379b..b5653a4 100644
--- a/third_party/metrics_proto/system_profile.proto
+++ b/third_party/metrics_proto/system_profile.proto
@@ -229,7 +229,7 @@
   optional OS os = 5;
 
   // Information on the user's hardware.
-  // Next tag: 24
+  // Next tag: 25
   message Hardware {
     // OS CPU architecture. Taken from uname -m and modified in Chromium logic.
     // Common options are: x86, x86_64, armv7l, armv8l, aarch64.
@@ -275,6 +275,10 @@
     // qualification reports, test device tags, etc.
     optional string full_hardware_class = 18;
 
+    // This field is only sent on Chrome OS devices with cellular support.
+    // This represents the variant of cellular modem present on the device.
+    optional string cellular_device_variant = 24;
+
     // The number of physical screens.
     optional int32 screen_count = 5;
 
diff --git a/tools/accessibility/inspect/ax_tree_server.cc b/tools/accessibility/inspect/ax_tree_server.cc
index 1d5dd08..797ca8bc 100644
--- a/tools/accessibility/inspect/ax_tree_server.cc
+++ b/tools/accessibility/inspect/ax_tree_server.cc
@@ -50,8 +50,14 @@
   }
 
   // Otherwise, dump the tree.
-  // Use optional filters with the default filter set.
-  formatter->SetPropertyFilters(scenario.property_filters,
+  // Use user provided filters with the default filter set.
+  std::vector<ui::AXPropertyFilter> property_filters_ext(
+      {{"AXRoleDescription", ui::AXPropertyFilter::ALLOW}});
+  property_filters_ext.insert(property_filters_ext.end(),
+                              scenario.property_filters.begin(),
+                              scenario.property_filters.end());
+
+  formatter->SetPropertyFilters(property_filters_ext,
                                 AXTreeFormatter::kFiltersDefaultSet);
 
   // Get accessibility tree as a nested dictionary.
diff --git a/tools/android/dependency_analysis/diff_graphs.py b/tools/android/dependency_analysis/diff_graphs.py
index 5f6bf97..3bf1cd6 100755
--- a/tools/android/dependency_analysis/diff_graphs.py
+++ b/tools/android/dependency_analysis/diff_graphs.py
@@ -7,7 +7,7 @@
 import argparse
 
 import itertools
-from typing import List, Set, Tuple
+from typing import List, Set, Tuple, Callable
 
 import chrome_names
 import count_cycles
@@ -41,6 +41,18 @@
     _print_set_diff(before_nodes, after_nodes, label)
 
 
+def _print_diff_node_list_filtered(graph1: graph.Graph, graph2: graph.Graph,
+                                   label: str,
+                                   filter_fn: Callable[[graph.Node], bool]):
+
+    before_nodes: Set[str] = set(node.name for node in graph1.nodes
+                                 if filter_fn(node))
+    after_nodes: Set[str] = set(node.name for node in graph2.nodes
+                                if filter_fn(node))
+    _print_diff_metric(len(before_nodes), len(after_nodes), label)
+    _print_set_diff(before_nodes, after_nodes, label)
+
+
 def _print_diff_edge_list(graph1: graph.Graph, graph2: graph.Graph,
                           label: str):
     before_edges: Set[str] = set(_edge_str(edge) for edge in graph1.edges)
@@ -108,6 +120,11 @@
         '--package-cycles',
         type=int,
         help='Also diff the set of package cycles up to the specified size.')
+    arg_parser.add_argument(
+        '--build-target',
+        type=str,
+        help='Also diff the set of class nodes in the given build target, e.g. '
+        '"//chrome/android:chrome_java"')
     arguments = arg_parser.parse_args()
 
     class_graph_before, package_graph_before, _ = \
@@ -156,6 +173,16 @@
             cycles_before, cycles_after,
             f'Java package cycles (up to size {arguments.package_cycles})')
 
+    if arguments.build_target:
+
+        def is_in_build_target(node):
+            return arguments.build_target in node.build_targets
+
+        print()
+        _print_diff_node_list_filtered(
+            class_graph_before, class_graph_after,
+            f'Java classes in {arguments.build_target}', is_in_build_target)
+
 
 if __name__ == '__main__':
     main()
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 7e622a8..9f9d576 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -3471,6 +3471,14 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="AppList_CreateFolder">
+  <owner>jamescook@chromium.org</owner>
+  <owner>tbarzic@chromium.org</owner>
+  <description>
+    User created a folder in the Chrome OS app launcher.
+  </description>
+</action>
+
 <action name="AppList_CurrentWindowToHomeLauncher">
   <owner>newcomer@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
@@ -3488,6 +3496,14 @@
   </description>
 </action>
 
+<action name="AppList_DeleteFolder">
+  <owner>jamescook@chromium.org</owner>
+  <owner>tbarzic@chromium.org</owner>
+  <description>
+    User deleted a folder in the Chrome OS app launcher.
+  </description>
+</action>
+
 <action name="AppList_EnterSearch">
   <owner>vadimt@chromium.org</owner>
   <description>CrOS Launcher entered search mode.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a258a81..90622293 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4030,6 +4030,14 @@
   <int value="7" label="Webview Downgrade Failure"/>
 </enum>
 
+<enum name="ArcCpuRestrictionVmResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Other failure reason"/>
+  <int value="2" label="VM concierge service is not available"/>
+  <int value="3" label="VM concierge client is not available"/>
+  <int value="4" label="VM Concierge did not respond"/>
+</enum>
+
 <enum name="ArcCustomTabsSessionEndReason">
   <obsolete>
     Removed as of 05/2022
@@ -54821,6 +54829,7 @@
   <int value="-1915854488" label="enable-offline-pages"/>
   <int value="-1913801713"
       label="UploadCrashReportsUsingJobScheduler:disabled"/>
+  <int value="-1913734393" label="CrosPrivacyHub:disabled"/>
   <int value="-1912999136" label="enable-automatic-password-saving:enabled"/>
   <int value="-1911918596" label="WebviewAccelerateSmallCanvases:disabled"/>
   <int value="-1911316813" label="BlockTabUnders:disabled"/>
@@ -56038,7 +56047,6 @@
   <int value="-1144754105" label="ScalableStatusArea:enabled"/>
   <int value="-1144501989" label="UserActivityPredictionMlService:disabled"/>
   <int value="-1144170722" label="DisableProcessReuse:enabled"/>
-  <int value="-1144128534" label="GoogleLensSdkIntent:enabled"/>
   <int value="-1143496217" label="enable-oop-rasterization"/>
   <int value="-1143007275" label="EnableNewStyleLauncher:disabled"/>
   <int value="-1142933895" label="ReduceDisplayNotifications:disabled"/>
@@ -59183,6 +59191,7 @@
   <int value="968715406"
       label="enable-experimental-accessibility-magnifier-new-focus-following"/>
   <int value="969340095" label="EnableDspHotword:disabled"/>
+  <int value="970344161" label="CrosPrivacyHub:enabled"/>
   <int value="972228058" label="SyncUSSSessions:disabled"/>
   <int value="973601997" label="SafeBrowsingUseLocalBlacklistsV2:disabled"/>
   <int value="975104092" label="show-taps"/>
@@ -59711,6 +59720,7 @@
   <int value="1308537004" label="force-pnacl-subzero"/>
   <int value="1310000273" label="ReleaseNotesNotificationAllChannels:disabled"/>
   <int value="1310316934" label="OptimizationGuideModelDownloading:enabled"/>
+  <int value="1311443340" label="DriveFsChromeNetworking:enabled"/>
   <int value="1311506187" label="ClipboardCustomFormats:disabled"/>
   <int value="1311579438" label="FencedFrames:enabled"/>
   <int value="1311860720" label="ChromeHomeNtpRedesign:disabled"/>
@@ -59792,7 +59802,6 @@
   <int value="1367487214" label="VaapiJpegImageDecodeAcceleration:enabled"/>
   <int value="1367529437" label="NTPAssetDownloadSuggestions:enabled"/>
   <int value="1367671275" label="enable-proximity-auth-proximity-detection"/>
-  <int value="1368907760" label="GoogleLensSdkIntent:disabled"/>
   <int value="1369022678" label="OrganicRepeatableQueries:disabled"/>
   <int value="1369449914" label="SysInternals:disabled"/>
   <int value="1369649674" label="ChromeShareScreenshot:enabled"/>
@@ -60416,6 +60425,7 @@
       label="ContextualSuggestionsIPHReverseScroll:disabled"/>
   <int value="1799521026" label="LegacyTLSEnforced:disabled"/>
   <int value="1799526742" label="PreemptiveLinkToTextGeneration:disabled"/>
+  <int value="1800509458" label="DriveFsChromeNetworking:disabled"/>
   <int value="1801585504" label="ContextMenuShopWithGoogleLens:enabled"/>
   <int value="1802874714" label="QueryTilesEnableQueryEditing:disabled"/>
   <int value="1803465156" label="enable-zero-suggest-most-visited"/>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 8cef64e..5d11c62 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -779,8 +779,8 @@
 
 <histogram name="Arc.CpuRestrictionDisabled{ArcThrottleObservers}" units="ms"
     expires_after="2022-09-01">
-  <owner>wvk@google.com</owner>
   <owner>khmel@google.com</owner>
+  <owner>arc-performance@google.com</owner>
   <summary>
     Records the time that throttling was disabled due to a particular throttle
     observer. The duration begins when a new throttle observer becomes active,
@@ -791,13 +791,27 @@
     <variant name=".ArcAppLaunchRequested"
         summary="ARC app launch in progress."/>
     <variant name=".ArcIsBooting" summary="ARC is booting or restarting."/>
+    <variant name=".ArcKioskMode" summary="ARC works in Kiosk mode."/>
     <variant name=".ArcPipWindowIsVisible"
         summary="An ARC PIP window is active."/>
+    <variant name=".ArcPower" summary="ARC handles pre-ANR conditions."/>
+    <variant name=".ArcSwitch"
+        summary="ARC throttling is disabled by switch. Not expected in
+                 production."/>
     <variant name=".ArcWindowIsActiveWindow"
         summary="An ARC window is active."/>
   </token>
 </histogram>
 
+<histogram name="Arc.CpuRestrictionVmResult" enum="ArcCpuRestrictionVmResult"
+    expires_after="2022-09-01">
+  <owner>khmel@google.com</owner>
+  <owner>arc-performance@google.com</owner>
+  <summary>
+    Records the result of setting ARC CPU restrictions in ARCVM.
+  </summary>
+</histogram>
+
 <histogram name="Arc.CumulativeUseTime" units="seconds"
     expires_after="2022-05-01">
   <owner>elijahtaylor@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index b90c11c..602e148 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1370,6 +1370,9 @@
 
 <histogram name="Ash.DarkTheme.Settings.IsDarkModeEnabled" enum="Boolean"
     expires_after="2023-03-01">
+  <obsolete>
+    Deprecated May 2022, now that it's no longer needed.
+  </obsolete>
   <owner>minch@chromium.org</owner>
   <owner>changmar@chromium.org</owner>
   <summary>
@@ -1381,6 +1384,9 @@
 
 <histogram name="Ash.DarkTheme.Settings.IsThemed" enum="Boolean"
     expires_after="2023-03-01">
+  <obsolete>
+    Deprecated May 2022, now that it's no longer needed.
+  </obsolete>
   <owner>minch@chromium.org</owner>
   <owner>changmar@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index e6d8255..b951ab7a 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -2088,6 +2088,38 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Blink.Responsiveness.UserInteraction.MaxEventDuration.{InteractionType}"
+    units="ms" expires_after="2023-05-17">
+  <owner>sullivan@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    The duration of a single user interaction:
+    https://web.dev/better-responsiveness-metric/#group-events-into-interactions
+
+    This metric is emitted once for every interaction in the web contents
+    {InteractionType}
+  </summary>
+  <token key="InteractionType">
+    <variant name="AllTypes"
+        summary="of all interaction types (keyboard, click, tap, drag). The
+                 maximum event duration for the keydown, keypress, keyup,
+                 pointerdown, pointerup, mousedown, mouseup, and click event
+                 is reported."/>
+    <variant name="ClickOrTap"
+        summary="of type click or tap. The maximum event duration for the
+                 pointerdown, pointerup, mousedown, mouseup, and click event
+                 is reported."/>
+    <variant name="Drag"
+        summary="of type drag. The maximum event duration for the
+                 pointerdown, pointerup, mousedown, mouseup, and click event
+                 is reported."/>
+    <variant name="Keyboard"
+        summary="of type keyboard. The maximum event duration for the
+                 keydown, keypress, and keyup event is reported."/>
+  </token>
+</histogram>
+
 <histogram name="Blink.ScanAndPreloadTime.{FrameType}.{IsInitial}" units="ms"
     expires_after="2022-11-29">
   <owner>cduvall@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 214e5b89..53088d8 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -260,17 +260,6 @@
   </summary>
 </histogram>
 
-<histogram name="Compositing.Display.DrmOverlayManager.CacheHit" enum="Boolean"
-    expires_after="2020-09-01">
-  <owner>samans@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    Indicates whether DrmOverlayManager was able to find the overlay
-    configuration in its cache. Recorded when DrmOverlayManager is asked to
-    verify an overlay configuration.
-  </summary>
-</histogram>
-
 <histogram name="Compositing.Display.DrmOverlayManager.FirstTestPageFlipPassed"
     enum="Boolean" expires_after="2022-11-30">
   <owner>khaslett@chromium.org</owner>
@@ -302,18 +291,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Compositing.Display.DrmOverlayManager.TotalTestBufferMemorySize"
-    units="KB" expires_after="2020-09-01">
-  <owner>samans@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    The amount of memory allocated for the purpose of performing a pageflip
-    test. Recorded every time DRM thread is asked to validate an overlay
-    configuration.
-  </summary>
-</histogram>
-
 <histogram name="Compositing.Display.DrmThread.CheckOverlayCapabilitiesSyncUs"
     units="microseconds" expires_after="2022-10-04">
   <owner>khaslett@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 038bd86e..24b600c 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3467,6 +3467,9 @@
 
 <histogram name="Conversions.ImpressionIgnoredByFeaturePolicy"
     enum="BooleanIgnored" expires_after="M105">
+  <obsolete>
+    Removed May 2022.
+  </obsolete>
   <owner>apaseltiner@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -3591,7 +3594,7 @@
 </histogram>
 
 <histogram name="Conversions.RegisterImpressionAllowed" enum="BooleanAllowed"
-    expires_after="M105">
+    expires_after="M109">
   <owner>apaseltiner@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index 7b4726b..c2651fa 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -259,7 +259,7 @@
 
 <histogram
     name="SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed2"
-    units="microseconds" expires_after="2022-06-01">
+    units="microseconds" expires_after="2023-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -274,7 +274,7 @@
 
 <histogram
     name="SubresourceFilter.DocumentLoad.SubframeFilteringDelay.WouldDisallow"
-    units="microseconds" expires_after="2022-06-01">
+    units="microseconds" expires_after="2023-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -367,7 +367,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.IndexRuleset.Verify2.WallDuration"
-    units="ms" expires_after="2022-06-01">
+    units="ms" expires_after="2023-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -379,7 +379,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.IndexRuleset.WallDuration" units="ms"
-    expires_after="2022-06-01">
+    expires_after="2023-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -468,7 +468,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.Evaluated"
-    units="resource loads" expires_after="2022-06-01">
+    units="resource loads" expires_after="2023-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -492,7 +492,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.Total"
-    units="resource loads" expires_after="2022-06-01">
+    units="resource loads" expires_after="2023-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -644,7 +644,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.WriteRuleset.Result"
-    enum="SubresourceFilterWriteRulesetResult" expires_after="2022-06-01">
+    enum="SubresourceFilterWriteRulesetResult" expires_after="2023-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/perf/chrome-health-presets.yaml b/tools/perf/chrome-health-presets.yaml
index 9c5fc17..d108885 100644
--- a/tools/perf/chrome-health-presets.yaml
+++ b/tools/perf/chrome-health-presets.yaml
@@ -18,7 +18,6 @@
           - AllRecipes_cold
       - benchmark: loading.mobile
         configs:
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - Amazon
@@ -29,7 +28,6 @@
           - mac-m1_mini_2020-perf
           - linux-perf
           - win-10-perf
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - Speedometer2
@@ -51,7 +49,6 @@
           - motionmark_ramp_suits
       - benchmark: rendering.mobile
         configs:
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - motionmark_ramp_canvas_arcs
@@ -71,7 +68,6 @@
           - mac-m1_mini_2020-perf
           - linux-perf
           - win-10-perf
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - Speedometer2
@@ -95,7 +91,6 @@
           - motionmark_ramp_suits
       - benchmark: rendering.mobile
         configs:
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - motionmark_ramp_canvas_arcs
@@ -120,7 +115,6 @@
           - AllRecipes_cold
       - benchmark: loading.mobile
         configs:
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - Amazon
@@ -235,7 +229,6 @@
           - yahoo.co.jp_warm
       - benchmark: loading.mobile
         configs:
-          - android-pixel2-perf
           - android-pixel4-perf
         stories:
           - 58Pic
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index db5b060..7948591 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "43d725104c2b26e9df92a90940395914e9c02c14",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3aded8458ec316b21ef66cc9afd274ca2e4a604b/trace_processor_shell.exe"
+            "hash": "0c4444f5c08b752ebdbebdc0bcbf49882abe25e8",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/03376e559abfc2e993526197d9bd2198ca356959/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "8ffcfae9f26e371127632271b5f4ddb19c553ae5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/3aded8458ec316b21ef66cc9afd274ca2e4a604b/trace_processor_shell"
+            "hash": "ec3bf68a4b46e95ab00042be24d8d6da15f4ccfb",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/b146e432cba8ac3c886a671169f1677519389ab2/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "45efb72815abd76eb15718005137798939be4e02",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/03376e559abfc2e993526197d9bd2198ca356959/trace_processor_shell"
+            "hash": "250caa942a98af360830b5bdb732736e5d2a0614",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d099a600a3410a602cee37a19e875f1a974d493a/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/models/dialog_model.cc b/ui/base/models/dialog_model.cc
index 9704592f..6ca513aa 100644
--- a/ui/base/models/dialog_model.cc
+++ b/ui/base/models/dialog_model.cc
@@ -97,8 +97,10 @@
 
 DialogModel::~DialogModel() = default;
 
-void DialogModel::AddBodyText(const DialogModelLabel& label) {
-  AddField(std::make_unique<DialogModelBodyText>(GetPassKey(), this, label));
+void DialogModel::AddBodyText(const DialogModelLabel& label,
+                              ElementIdentifier id) {
+  AddField(
+      std::make_unique<DialogModelBodyText>(GetPassKey(), this, label, id));
 }
 
 void DialogModel::AddCheckbox(ElementIdentifier id,
diff --git a/ui/base/models/dialog_model.h b/ui/base/models/dialog_model.h
index 0f935b6..e22f066 100644
--- a/ui/base/models/dialog_model.h
+++ b/ui/base/models/dialog_model.h
@@ -135,6 +135,13 @@
       return *this;
     }
 
+    Builder& SetBannerImage(ImageModel banner,
+                            ImageModel dark_mode_banner = ImageModel()) {
+      model_->banner_ = std::move(banner);
+      model_->dark_mode_banner_ = std::move(dark_mode_banner);
+      return *this;
+    }
+
     Builder& SetIcon(ImageModel icon,
                      ImageModel dark_mode_icon = ImageModel()) {
       model_->icon_ = std::move(icon);
@@ -204,8 +211,9 @@
     Builder& AddExtraLink(ui::DialogModelLabel::Link link);
 
     // Adds body text. See DialogModel::AddBodyText().
-    Builder& AddBodyText(const DialogModelLabel& label) {
-      model_->AddBodyText(label);
+    Builder& AddBodyText(const DialogModelLabel& label,
+                         ElementIdentifier id = ElementIdentifier()) {
+      model_->AddBodyText(label, id);
       return *this;
     }
 
@@ -280,7 +288,7 @@
   DialogModelHost* host() { return host_; }
 
   // Adds body text at the end of the dialog model.
-  void AddBodyText(const DialogModelLabel& label);
+  void AddBodyText(const DialogModelLabel& label, ElementIdentifier id);
 
   // Adds a checkbox ([checkbox] label) at the end of the dialog model.
   void AddCheckbox(ElementIdentifier id,
@@ -360,6 +368,14 @@
     return dark_mode_icon_;
   }
 
+  const ImageModel& banner(base::PassKey<DialogModelHost>) const {
+    return banner_;
+  }
+
+  const ImageModel& dark_mode_banner(base::PassKey<DialogModelHost>) const {
+    return dark_mode_banner_;
+  }
+
   ElementIdentifier initially_focused_field(
       base::PassKey<DialogModelHost>) const {
     return initially_focused_field_;
@@ -414,6 +430,9 @@
   ImageModel icon_;
   ImageModel dark_mode_icon_;
 
+  ImageModel banner_;
+  ImageModel dark_mode_banner_;
+
   std::vector<std::unique_ptr<DialogModelField>> fields_;
   ElementIdentifier initially_focused_field_;
   bool is_alert_dialog_ = false;
diff --git a/ui/base/models/dialog_model_field.cc b/ui/base/models/dialog_model_field.cc
index 871d43a..04c91e9 100644
--- a/ui/base/models/dialog_model_field.cc
+++ b/ui/base/models/dialog_model_field.cc
@@ -179,9 +179,9 @@
 
 DialogModelBodyText::DialogModelBodyText(base::PassKey<DialogModel> pass_key,
                                          DialogModel* model,
-                                         const DialogModelLabel& label)
-    : DialogModelField(pass_key, model, kBodyText, ElementIdentifier(), {}),
-      label_(label) {}
+                                         const DialogModelLabel& label,
+                                         ElementIdentifier id)
+    : DialogModelField(pass_key, model, kBodyText, id, {}), label_(label) {}
 
 DialogModelBodyText::~DialogModelBodyText() = default;
 
diff --git a/ui/base/models/dialog_model_field.h b/ui/base/models/dialog_model_field.h
index df351a4..2de0a8f 100644
--- a/ui/base/models/dialog_model_field.h
+++ b/ui/base/models/dialog_model_field.h
@@ -226,7 +226,8 @@
   // fields.
   DialogModelBodyText(base::PassKey<DialogModel> pass_key,
                       DialogModel* model,
-                      const DialogModelLabel& label);
+                      const DialogModelLabel& label,
+                      ElementIdentifier id);
   DialogModelBodyText(const DialogModelBodyText&) = delete;
   DialogModelBodyText& operator=(const DialogModelBodyText&) = delete;
   ~DialogModelBodyText() override;
diff --git a/ui/events/keycodes/DEPS b/ui/events/keycodes/DEPS
index 0186a15e..d974f6c 100644
--- a/ui/events/keycodes/DEPS
+++ b/ui/events/keycodes/DEPS
@@ -7,6 +7,7 @@
   "-third_party",
   "-ui",
 
+  "+third_party/abseil-cpp/absl",
   "+ui/base/buildflags.h",  # Doesn't bring in all of UI.
   "+ui/gfx/x",
   "+ui/events",
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js
index 03112b3..827cc81 100644
--- a/ui/file_manager/integration_tests/file_manager/background.js
+++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -64,9 +64,6 @@
 
 export {FILE_MANAGER_EXTENSIONS_ID};
 
-/**
- * @type {!RemoteCallFilesApp}
- */
 export let remoteCall;
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/recents.js b/ui/file_manager/integration_tests/file_manager/recents.js
index d8846aa..60b28ca 100644
--- a/ui/file_manager/integration_tests/file_manager/recents.js
+++ b/ui/file_manager/integration_tests/file_manager/recents.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addEntries, ENTRIES, getCaller, getDateWithinLastMonth, pending, repeatUntil, RootPath, sendTestMessage, TestEntryInfo, wait} from '../test_util.js';
+import {addEntries, ENTRIES, EntryType, getDateWithinLastMonth, RootPath, sendTestMessage, TestEntryInfo} from '../test_util.js';
 import {testcase} from '../testcase.js';
 
-import {mountCrostini, navigateWithDirectoryTree, openNewWindow, remoteCall, setupAndWaitUntilReady} from './background.js';
+import {mountCrostini, openNewWindow, remoteCall, setupAndWaitUntilReady} from './background.js';
 import {BASIC_CROSTINI_ENTRY_SET, BASIC_DRIVE_ENTRY_SET, BASIC_LOCAL_ENTRY_SET, NESTED_ENTRY_SET, RECENT_ENTRY_SET} from './test_data.js';
 
 // Test entry for a recently-modified video file.
@@ -217,106 +217,24 @@
 }
 
 /**
- * Select a file and right click to show the context menu, then click the
- * specified context menu item.
- *
- * @param {string} appId Files app windowId.
- * @param {string} fileName Name of the file to right click.
- * @param {string} commandId The command id for the context menu item.
- */
-async function rightClickContextMenu(appId, fileName, commandId) {
-  // Select the item.
-  chrome.test.assertTrue(
-      !!await remoteCall.callRemoteTestUtil('selectFile', appId, [fileName]));
-
-  // Right-click the selected file.
-  await remoteCall.waitAndRightClick(appId, '.table-row[selected]');
-
-  // Click the context menu item with the command id.
-  const contextMenuItem = '#file-context-menu:not([hidden]) ' +
-      `[command="#${commandId}"]:not([hidden]):not([disabled])`;
-  await remoteCall.waitAndClickElement(appId, contextMenuItem);
-}
-
-/**
  * Opens given file's containing folder by choosing "Go to file location"
  * context menu item.
  *
  * @param {string} appId Files app windowId.
- * @param {string} fileName Name of the file to open containing folder.
+ * @param {string} itemName Name of the file to open containing folder.
  */
-async function goToFileLocation(appId, fileName) {
-  await rightClickContextMenu(appId, fileName, 'go-to-file-location');
-}
-
-/**
- * Delete a given file by choosing "Delete" context menu item.
- *
- * @param {string} appId Files app windowId.
- * @param {string} fileName Name of the file to delete.
- */
-async function deleteFile(appId, fileName) {
-  await rightClickContextMenu(appId, fileName, 'delete');
-  // Click "Delete" on the Delete confirm dialog.
-  await remoteCall.waitAndClickElement(
-      appId, '.files-confirm-dialog button.cr-dialog-ok');
-}
-
-/**
- * Rename a given file by choosing "Rename" context menu item.
- *
- * @param {string} appId Files app windowId.
- * @param {string} fileName Name of the file to rename.
- * @param {string} newName The new file name.
- */
-async function renameFile(appId, fileName, newName) {
-  await rightClickContextMenu(appId, fileName, 'rename');
-  // Wait for the rename input field.
-  await remoteCall.waitForElement(appId, 'input.rename');
-  // Input the new name.
-  await remoteCall.inputText(appId, 'input.rename', newName);
-  // Press Enter to commit rename.
-  const keyDown = ['input.rename', 'Enter', false, false, false];
+async function goToFileLocation(appId, itemName) {
+  // Select the item.
   chrome.test.assertTrue(
-      await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, keyDown));
-}
+      !!await remoteCall.callRemoteTestUtil('selectFile', appId, [itemName]));
 
-/**
- * Cut a given file by choosing "Cut" context menu item and paste the file
- * to the new folder.
- *
- * @param {string} appId Files app windowId.
- * @param {string} fileName Name of the file to cut.
- * @param {string} newFolder Full breadcrumb path for the new folder to paste.
- */
-async function cutFileAndPasteTo(appId, fileName, newFolder) {
-  await rightClickContextMenu(appId, fileName, 'cut');
-  // Go to the new folder to paste.
-  await navigateWithDirectoryTree(appId, newFolder);
-  chrome.test.assertTrue(
-      await remoteCall.callRemoteTestUtil('execCommand', appId, ['paste']));
-  // Wait for the operation to be completed.
-  const caller = getCaller();
-  await repeatUntil(async () => {
-    const element = await remoteCall.waitForElement(
-        appId, ['#progress-panel', 'xf-panel-item']);
-    const expectedPrimaryText =
-        `Moving ${fileName} to ${newFolder.split('/').pop()}`;
-    const expectedSecondaryText = 'Complete';
-    const actualPrimaryText = element.attributes['primary-text'];
-    const actualSecondaryText = element.attributes['secondary-text'];
+  // Right-click the selected file.
+  await remoteCall.waitAndRightClick(appId, '.table-row[selected]');
 
-    if (expectedPrimaryText === actualPrimaryText &&
-        actualSecondaryText === actualSecondaryText) {
-      return;
-    }
-
-    return pending(
-        caller,
-        `Expected feedback panel msg: "${expectedPrimaryText} - ${
-            expectedSecondaryText}", got "${actualPrimaryText} - ${
-            actualSecondaryText}"`);
-  });
+  // Click 'Go to file location' menu command.
+  const goToLocationMenu = '#file-context-menu:not([hidden]) ' +
+      '[command="#go-to-file-location"]:not([hidden]):not([disabled])';
+  remoteCall.waitAndClickElement(appId, goToLocationMenu);
 }
 
 /**
@@ -701,233 +619,3 @@
       'Videos filter is off. Filter is reset.',
       a11yMessages[a11yMessages.length - 1]);
 };
-
-/**
- * Tests the read only flag on Recents view should be hidden.
- */
-testcase.recentsReadOnlyHidden = async () => {
-  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
-  await navigateToRecent(appId);
-  const readOnlyIndicator =
-      await remoteCall.waitForElement(appId, ['#read-only-indicator']);
-  chrome.test.assertTrue(
-      readOnlyIndicator.hidden, 'Read only indicator should be hidden');
-};
-
-/**
- * Tests delete operation can be performed in Recents view on files from
- * Downloads, Drive and Play Files.
- */
-testcase.recentsAllowDeletion = async () => {
-  await addPlayFileEntries();
-  const appId = await setupAndWaitUntilReady(
-      RootPath.DOWNLOADS, [ENTRIES.beautiful], [ENTRIES.desktop]);
-  await navigateToRecent(appId);
-  const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  await remoteCall.waitForFiles(appId, files);
-
-  // Delete a file originated from Downloads.
-  await deleteFile(appId, ENTRIES.beautiful.nameText);
-  const files1 = TestEntryInfo.getExpectedRows([
-    ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  await remoteCall.waitForFiles(appId, files1);
-
-  // Delete a file originated from Drive.
-  await deleteFile(appId, ENTRIES.desktop.nameText);
-  const files2 = TestEntryInfo.getExpectedRows([
-    RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_IMAGE,
-    RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  await remoteCall.waitForFiles(appId, files2);
-
-  // Delete a file originated from Play Files.
-  await deleteFile(appId, RECENT_MODIFIED_ANDROID_IMAGE.nameText);
-  const files3 = TestEntryInfo.getExpectedRows(
-      [RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_VIDEO]);
-  await remoteCall.waitForFiles(appId, files3);
-};
-
-/**
- * Tests delete operation can be performed in Recents view with multiple files
- * from different sources including Downloads, Drive and Play Files.
- */
-testcase.recentsAllowMultipleFilesDeletion = async () => {
-  await addPlayFileEntries();
-  const appId = await setupAndWaitUntilReady(
-      RootPath.DOWNLOADS, [ENTRIES.beautiful], [ENTRIES.desktop]);
-  await navigateToRecent(appId);
-  const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  await remoteCall.waitForFiles(appId, files);
-
-  // Select all files from the gear menu.
-  await remoteCall.waitAndClickElement(appId, '#gear-button');
-  const selectAllMenu = '#gear-menu:not([hidden]) ' +
-      `[command="#select-all"]:not([hidden]):not([disabled])`;
-  await remoteCall.waitAndClickElement(appId, selectAllMenu);
-  await remoteCall.waitForElement(appId, '.table-row[selected]');
-  // Wait for the files selection label.
-  const caller = getCaller();
-  await repeatUntil(async () => {
-    const element =
-        await remoteCall.waitForElement(appId, '#files-selected-label');
-    const expectedLabel = '5 files selected';
-
-    if (element.text === expectedLabel) {
-      return;
-    }
-
-    return pending(
-        caller,
-        `Expected files selection label: "${expectedLabel}", got "${
-            element.text}"`);
-  });
-  // Delete all selected files via action bar.
-  await remoteCall.waitAndClickElement(appId, '#delete-button');
-  // Click okay on the confirm dialog.
-  await remoteCall.waitAndClickElement(
-      appId, '.files-confirm-dialog button.cr-dialog-ok');
-
-  // Check all files should be deleted.
-  await remoteCall.waitForFiles(appId, []);
-};
-
-/**
- * Tests rename operation can be performed in Recents view on files from
- * Downloads, Drive.
- */
-testcase.recentsAllowRename = async () => {
-  const appId = await setupAndWaitUntilReady(
-      RootPath.DOWNLOADS, [ENTRIES.beautiful], [ENTRIES.desktop]);
-  await navigateToRecent(appId);
-  const files =
-      TestEntryInfo.getExpectedRows([ENTRIES.beautiful, ENTRIES.desktop]);
-  await remoteCall.waitForFiles(appId, files);
-
-  // Rename a file originated from Downloads.
-  const newBeautiful = ENTRIES.beautiful.cloneWithNewName('new-beautiful.ogg');
-  await renameFile(appId, ENTRIES.beautiful.nameText, newBeautiful.nameText);
-  const files1 = TestEntryInfo.getExpectedRows([
-    newBeautiful,
-    ENTRIES.desktop,
-  ]);
-  await remoteCall.waitForFiles(appId, files1);
-
-  // Rename a file originated from Drive.
-  const newDesktop = ENTRIES.desktop.cloneWithNewName('new-desktop.png');
-  await renameFile(appId, ENTRIES.desktop.nameText, newDesktop.nameText);
-  const files2 = TestEntryInfo.getExpectedRows([
-    newDesktop,
-    newBeautiful,
-  ]);
-  await remoteCall.waitForFiles(appId, files2);
-};
-
-/**
- * Tests rename operation is not allowed in Recents view for files from
- * Play files.
- */
-testcase.recentsNoRenameForPlayFiles = async () => {
-  await addPlayFileEntries();
-  const appId =
-      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
-  await navigateToRecent(appId);
-  const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  await remoteCall.waitForFiles(appId, files);
-
-  // Select the item.
-  chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil(
-      'selectFile', appId, [RECENT_MODIFIED_ANDROID_DOCUMENT.nameText]));
-
-  // Right-click the selected file.
-  await remoteCall.waitAndRightClick(appId, '.table-row[selected]');
-
-  // Checks the rename menu should be disabled.
-  const renameMenu = '#file-context-menu:not([hidden]) ' +
-      '[command="#rename"][disabled]:not([hidden])';
-  await remoteCall.waitForElement(appId, renameMenu);
-};
-
-/**
- * Tests cut operation can be performed in Recents view on files from
- * Downloads, Drive and Play Files.
- */
-testcase.recentsAllowCut = async () => {
-  await addPlayFileEntries();
-  const appId = await setupAndWaitUntilReady(
-      RootPath.DOWNLOADS, [ENTRIES.beautiful, ENTRIES.directoryA],
-      [ENTRIES.desktop]);
-  const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
-  ]);
-  const newFolderBreadcrumb =
-      `/My files/Downloads/${ENTRIES.directoryA.nameText}`;
-
-  // Cut/Paste a file originated from Downloads.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  await cutFileAndPasteTo(
-      appId, ENTRIES.beautiful.nameText, newFolderBreadcrumb);
-  // The file being cut should appear in the new directory.
-  const filesInNewDir1 = TestEntryInfo.getExpectedRows([ENTRIES.beautiful]);
-  await remoteCall.waitForFiles(appId, filesInNewDir1);
-  // Recents view still have the full file list because the file being cut just
-  // moves to a new directory, but it still belongs to Recent.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  // Use "go to location" to validate the file in Recents after cut is
-  // collected from the new folder.
-  await goToFileLocation(appId, ENTRIES.beautiful.nameText);
-  await remoteCall.waitForFiles(appId, filesInNewDir1);
-  await verifyBreadcrumbsPath(appId, newFolderBreadcrumb);
-
-  // Cut/Paste a file originated from Drive.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  await cutFileAndPasteTo(appId, ENTRIES.desktop.nameText, newFolderBreadcrumb);
-  // The file being cut should appear in the new directory.
-  const filesInNewDir2 = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful,
-    ENTRIES.desktop,
-  ]);
-  await remoteCall.waitForFiles(appId, filesInNewDir2);
-  // Recents view still have the full file list because the file being cut just
-  // moves to a new directory, but it still belongs to Recent.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  // Use "go to location" to validate the file in Recents after cut is
-  // collected from the new folder.
-  await goToFileLocation(appId, ENTRIES.desktop.nameText);
-  await remoteCall.waitForFiles(appId, filesInNewDir2);
-  await verifyBreadcrumbsPath(appId, newFolderBreadcrumb);
-
-  // Cut/Paste a file originated from Play Files.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  await cutFileAndPasteTo(
-      appId, RECENT_MODIFIED_ANDROID_IMAGE.nameText, newFolderBreadcrumb);
-  // The file being cut should appear in the new directory.
-  const filesInNewDir3 = TestEntryInfo.getExpectedRows(
-      [ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_IMAGE]);
-  await remoteCall.waitForFiles(appId, filesInNewDir3);
-  // Recents view still have the full file list because the file being cut just
-  // moves to a new directory, but it still belongs to Recent.
-  await navigateToRecent(appId);
-  await remoteCall.waitForFiles(appId, files);
-  // Use "go to location" to validate the file in Recents after cut is
-  // collected from the new folder.
-  await goToFileLocation(appId, RECENT_MODIFIED_ANDROID_IMAGE.nameText);
-  await remoteCall.waitForFiles(appId, filesInNewDir3);
-  await verifyBreadcrumbsPath(appId, newFolderBreadcrumb);
-};
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 37af31a2..53eaef6 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -471,23 +471,6 @@
         }));
     return new TestEntryInfo(updatedOptions);
   }
-
-  /**
-   * Clone the existing TestEntryInfo object to a new TestEntryInfo object but
-   * with modified targetPath field. This is especially useful for testing
-   * rename functionality.
-   *
-   * @param {string} newName the new modified name
-   * @return {!TestEntryInfo}
-   */
-  cloneWithNewName(newName) {
-    const updatedOptions =
-        /** @type {TestEntryInfoOptions} */ (Object.assign({}, this, {
-          targetPath: newName,
-          nameText: newName,
-        }));
-    return new TestEntryInfo(updatedOptions);
-  }
 }
 
 /**
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc b/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc
index 6f51baf3..f803e30 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc
@@ -142,7 +142,6 @@
     iter = cache.Put(cache_key, std::move(value));
   }
 
-  bool cache_hit = false;
   OverlayValidationCacheValue& value = iter->second;
   if (value.request_num < kThrottleRequestSize) {
     value.request_num++;
@@ -151,7 +150,6 @@
     if (value.status.back() == OVERLAY_STATUS_PENDING)
       SendOverlayValidationRequest(result_candidates, widget);
   } else if (value.status.back() != OVERLAY_STATUS_PENDING) {
-    cache_hit = true;
     size_t size = candidates->size();
     const std::vector<OverlayStatus>& status = value.status;
     DCHECK_EQ(size, status.size());
@@ -161,8 +159,6 @@
       candidates->at(i).overlay_handled = status[i] == OVERLAY_STATUS_ABLE;
     }
   }
-  UMA_HISTOGRAM_BOOLEAN("Compositing.Display.DrmOverlayManager.CacheHit",
-                        cache_hit);
 }
 
 void DrmOverlayManager::RegisterOverlayRequirement(
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
index a94d256..d93c0b4 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
@@ -30,8 +30,7 @@
 scoped_refptr<DrmFramebuffer> GetBufferForPageFlipTest(
     const DrmWindow* drm_window,
     const OverlaySurfaceCandidate& overlay_surface,
-    std::vector<scoped_refptr<DrmFramebuffer>>* reusable_buffers,
-    size_t* total_allocated_memory_size) {
+    std::vector<scoped_refptr<DrmFramebuffer>>* reusable_buffers) {
   if (overlay_surface.native_pixmap) {
     return static_cast<GbmPixmap*>(overlay_surface.native_pixmap.get())
         ->framebuffer();
@@ -70,9 +69,6 @@
   if (!buffer)
     return nullptr;
 
-  for (size_t i = 0; i < buffer->GetNumPlanes(); ++i)
-    *total_allocated_memory_size += buffer->GetPlaneSize(i);
-
   scoped_refptr<DrmFramebuffer> drm_framebuffer =
       DrmFramebuffer::AddFramebuffer(drm_device, buffer.get(),
                                      buffer->GetSize(), modifiers);
@@ -91,10 +87,9 @@
 
 DrmOverlayPlane DrmOverlayValidator::MakeOverlayPlane(
     const OverlaySurfaceCandidate& param,
-    std::vector<scoped_refptr<DrmFramebuffer>>& reusable_buffers,
-    size_t& total_allocated_memory_size) {
-  scoped_refptr<DrmFramebuffer> buffer = GetBufferForPageFlipTest(
-      window_, param, &reusable_buffers, &total_allocated_memory_size);
+    std::vector<scoped_refptr<DrmFramebuffer>>& reusable_buffers) {
+  scoped_refptr<DrmFramebuffer> buffer =
+      GetBufferForPageFlipTest(window_, param, &reusable_buffers);
 
   return DrmOverlayPlane(buffer, param.plane_z_order, param.transform,
                          gfx::ToNearestRect(param.display_rect),
@@ -124,8 +119,6 @@
     reusable_buffers.push_back(plane.buffer);
   }
 
-  size_t total_allocated_memory_size = 0;
-
   std::vector<size_t> plane_indices;
   for (size_t i = 0; i < params.size(); ++i) {
     auto& param = params[i];
@@ -135,8 +128,7 @@
       continue;
     }
 
-    DrmOverlayPlane plane =
-        MakeOverlayPlane(param, reusable_buffers, total_allocated_memory_size);
+    DrmOverlayPlane plane = MakeOverlayPlane(param, reusable_buffers);
     if (!plane.buffer) {
       returns[i] = OVERLAY_STATUS_NOT;
       continue;
@@ -193,9 +185,6 @@
     returns[index] = OVERLAY_STATUS_ABLE;
   }
 
-  UMA_HISTOGRAM_MEMORY_KB(
-      "Compositing.Display.DrmOverlayManager.TotalTestBufferMemorySize",
-      total_allocated_memory_size / 1024);
   UMA_HISTOGRAM_COUNTS_100(
       "Compositing.Display.DrmOverlayManager.TestPageFlipCount",
       test_page_flip_count);
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
index b7ccb7e..790e470 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
@@ -38,8 +38,7 @@
  private:
   DrmOverlayPlane MakeOverlayPlane(
       const OverlaySurfaceCandidate& param,
-      std::vector<scoped_refptr<DrmFramebuffer>>& reusable_buffers,
-      size_t& total_allocated_memory_size);
+      std::vector<scoped_refptr<DrmFramebuffer>>& reusable_buffers);
 
   DrmWindow* const window_;  // Not owned.
 };
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.cc b/ui/ozone/platform/wayland/host/wayland_event_source.cc
index 863ab4f..fd857aa 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.cc
@@ -226,7 +226,8 @@
   // MouseEvent's flags should contain the button that was released too.
   int flags = pointer_flags_ | keyboard_modifiers_ | changed_button;
   MouseEvent event(type, pointer_location_, pointer_location_,
-                   EventTimeForNow(), flags, changed_button);
+                   EventTimeForNow(), flags, changed_button,
+                   PointerDetailsForDispatching());
   DispatchEvent(&event);
 
   if (window)
@@ -244,7 +245,7 @@
 
   int flags = pointer_flags_ | keyboard_modifiers_;
   MouseEvent event(ET_MOUSE_MOVED, pointer_location_, pointer_location_,
-                   EventTimeForNow(), flags, 0);
+                   EventTimeForNow(), flags, 0, PointerDetailsForDispatching());
   DispatchEvent(&event);
 }
 
@@ -484,6 +485,11 @@
   return pointer_flags_ & button;
 }
 
+void WaylandEventSource::OnPointerStylusToolChanged(
+    EventPointerType pointer_type) {
+  last_pointer_stylus_tool_ = pointer_type;
+}
+
 void WaylandEventSource::ResetPointerFlags() {
   pointer_flags_ = 0;
 }
@@ -572,4 +578,12 @@
   return connection_->surface_submission_in_pixel_coordinates();
 }
 
+PointerDetails WaylandEventSource::PointerDetailsForDispatching() const {
+  if (!last_pointer_stylus_tool_)
+    return PointerDetails(EventPointerType::kMouse);
+
+  DCHECK_NE(*last_pointer_stylus_tool_, EventPointerType::kUnknown);
+  return PointerDetails(*last_pointer_stylus_tool_);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.h b/ui/ozone/platform/wayland/host/wayland_event_source.h
index 4610628b..3c1c40e 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.h
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.h
@@ -111,6 +111,7 @@
   void OnResetPointerFlags() override;
   const gfx::PointF& GetPointerLocation() const override;
   bool IsPointerButtonPressed(EventFlags button) const override;
+  void OnPointerStylusToolChanged(EventPointerType pointer_type) override;
 
   // WaylandTouch::Delegate
   void OnTouchPressEvent(WaylandWindow* window,
@@ -171,6 +172,9 @@
   gfx::Vector2dF ComputeFlingVelocity();
 
   bool SurfaceSubmissionInPixelCoordinates() const;
+
+  PointerDetails PointerDetailsForDispatching() const;
+
   WaylandWindowManager* const window_manager_;
 
   WaylandConnection* const connection_;
@@ -196,6 +200,9 @@
   // Time of the last pointer frame event.
   base::TimeTicks last_pointer_frame_time_;
 
+  // Last known pointer stylus type (eg mouse, pointer, eraser, touch).
+  absl::optional<EventPointerType> last_pointer_stylus_tool_;
+
   // Recent pointer frames to compute fling scroll.
   // Front is newer, and back is older.
   std::deque<PointerFrame> recent_pointer_frames_;
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.cc b/ui/ozone/platform/wayland/host/wayland_pointer.cc
index d0e6a50..aa95fd3 100644
--- a/ui/ozone/platform/wayland/host/wayland_pointer.cc
+++ b/ui/ozone/platform/wayland/host/wayland_pointer.cc
@@ -210,8 +210,25 @@
 // static
 void WaylandPointer::Tool(void* data,
                           struct zcr_pointer_stylus_v2* x,
-                          uint32_t y) {
-  NOTIMPLEMENTED_LOG_ONCE();
+                          uint32_t wl_pointer_type) {
+  auto* pointer = static_cast<WaylandPointer*>(data);
+
+  ui::EventPointerType pointer_type = ui::EventPointerType::kMouse;
+  switch (wl_pointer_type) {
+    case (ZCR_POINTER_STYLUS_V2_TOOL_TYPE_PEN):
+      pointer_type = EventPointerType::kPen;
+      break;
+    case (ZCR_POINTER_STYLUS_V2_TOOL_TYPE_ERASER):
+      pointer_type = ui::EventPointerType::kEraser;
+      break;
+    case (ZCR_POINTER_STYLUS_V2_TOOL_TYPE_TOUCH):
+      pointer_type = EventPointerType::kTouch;
+      break;
+    case (ZCR_POINTER_STYLUS_V2_TOOL_TYPE_NONE):
+      break;
+  }
+
+  pointer->delegate_->OnPointerStylusToolChanged(pointer_type);
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.h b/ui/ozone/platform/wayland/host/wayland_pointer.h
index 893ad2b..2df105e 100644
--- a/ui/ozone/platform/wayland/host/wayland_pointer.h
+++ b/ui/ozone/platform/wayland/host/wayland_pointer.h
@@ -120,6 +120,7 @@
   virtual void OnResetPointerFlags() = 0;
   virtual const gfx::PointF& GetPointerLocation() const = 0;
   virtual bool IsPointerButtonPressed(EventFlags button) const = 0;
+  virtual void OnPointerStylusToolChanged(EventPointerType pointer_type) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/views/bubble/bubble_dialog_model_host.cc b/ui/views/bubble/bubble_dialog_model_host.cc
index eae74f4..b0482387 100644
--- a/ui/views/bubble/bubble_dialog_model_host.cc
+++ b/ui/views/bubble/bubble_dialog_model_host.cc
@@ -16,6 +16,7 @@
 #include "ui/base/models/dialog_model.h"
 #include "ui/base/models/dialog_model_field.h"
 #include "ui/views/accessibility/accessibility_paint_checks.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/checkbox.h"
@@ -26,6 +27,7 @@
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/theme_tracking_image_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/fill_layout.h"
@@ -418,6 +420,21 @@
     AddDialogModelHostFieldForExistingView(
         {model_->extra_button(GetPassKey()), GetExtraView(), nullptr});
   }
+
+  if (const ui::ImageModel& banner = model_->banner(GetPassKey());
+      !banner.IsEmpty()) {
+    const ui::ImageModel& dark_mode_banner =
+        model_->dark_mode_banner(GetPassKey());
+    auto banner_view = std::make_unique<ThemeTrackingImageView>(
+        banner.Rasterize(contents_view_->GetColorProvider()),
+        (dark_mode_banner.IsEmpty() ? banner : dark_mode_banner)
+            .Rasterize(contents_view_->GetColorProvider()),
+        base::BindRepeating(&views::BubbleDialogDelegate::GetBackgroundColor,
+                            base::Unretained(this)));
+    // The banner is supposed to be purely decorative.
+    banner_view->GetViewAccessibility().OverrideIsIgnored(true);
+    GetBubbleFrameView()->SetHeaderView(std::move(banner_view));
+  }
 }
 
 void BubbleDialogModelHost::Close() {
@@ -549,6 +566,7 @@
   std::unique_ptr<View> view =
       CreateViewForLabel(model_field->label(GetPassKey()));
   DialogModelHostField info{model_field, view.get(), nullptr};
+  view->SetProperty(kElementIdentifierKey, model_field->id(GetPassKey()));
   AddDialogModelHostField(std::move(view), info);
 }
 
diff --git a/ui/views/controls/separator.cc b/ui/views/controls/separator.cc
index 4454b6a..4051d9e 100644
--- a/ui/views/controls/separator.cc
+++ b/ui/views/controls/separator.cc
@@ -32,23 +32,34 @@
   OnPropertyChanged(&overridden_color_, kPropertyEffectsPaint);
 }
 
-int Separator::GetPreferredHeight() const {
-  return preferred_height_;
+int Separator::GetPreferredLength() const {
+  return preferred_length_;
 }
 
-void Separator::SetPreferredHeight(int height) {
-  if (preferred_height_ == height)
+void Separator::SetPreferredLength(int length) {
+  if (preferred_length_ == length)
     return;
 
-  preferred_height_ = height;
-  OnPropertyChanged(&preferred_height_, kPropertyEffectsPreferredSizeChanged);
+  preferred_length_ = length;
+  OnPropertyChanged(&preferred_length_, kPropertyEffectsPreferredSizeChanged);
+}
+
+Separator::Orientation Separator::GetOrientation() const {
+  return orientation_;
+}
+
+void Separator::SetOrientation(Orientation orientation) {
+  orientation_ = orientation;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Separator, View overrides:
 
 gfx::Size Separator::CalculatePreferredSize() const {
-  gfx::Size size(kThickness, preferred_height_);
+  gfx::Size size(kThickness, preferred_length_);
+  if (orientation_ == Orientation::kHorizontal)
+    size.Transpose();
+
   gfx::Insets insets = GetInsets();
   size.Enlarge(insets.width(), insets.height());
   return size;
@@ -96,7 +107,8 @@
 
 BEGIN_METADATA(Separator, View)
 ADD_PROPERTY_METADATA(SkColor, Color, ui::metadata::SkColorConverter)
-ADD_PROPERTY_METADATA(int, PreferredHeight)
+ADD_PROPERTY_METADATA(int, PreferredLength)
+ADD_PROPERTY_METADATA(Separator::Orientation, Orientation)
 END_METADATA
 
 }  // namespace views
diff --git a/ui/views/controls/separator.h b/ui/views/controls/separator.h
index 0c95b21..60f415ae 100644
--- a/ui/views/controls/separator.h
+++ b/ui/views/controls/separator.h
@@ -20,6 +20,9 @@
   // The separator's thickness in dip.
   static constexpr int kThickness = 1;
 
+  // The separator's orientation, set to `kVertical` by default.
+  enum class Orientation { kVertical, kHorizontal };
+
   Separator();
 
   Separator(const Separator&) = delete;
@@ -30,21 +33,28 @@
   SkColor GetColor() const;
   void SetColor(SkColor color);
 
-  int GetPreferredHeight() const;
-  void SetPreferredHeight(int height);
+  // Vertical or horizontal extension depending on the orientation. Set to
+  // `kThickness` by default.
+  int GetPreferredLength() const;
+  void SetPreferredLength(int length);
+
+  Orientation GetOrientation() const;
+  void SetOrientation(Orientation orientation);
 
   // Overridden from View:
   gfx::Size CalculatePreferredSize() const override;
   void OnPaint(gfx::Canvas* canvas) override;
 
  private:
-  int preferred_height_ = kThickness;
+  int preferred_length_ = kThickness;
   absl::optional<SkColor> overridden_color_;
+  Orientation orientation_ = Orientation::kVertical;
 };
 
 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, Separator, View)
 VIEW_BUILDER_PROPERTY(SkColor, Color)
-VIEW_BUILDER_PROPERTY(int, PreferredHeight)
+VIEW_BUILDER_PROPERTY(int, PreferredLength)
+VIEW_BUILDER_PROPERTY(Separator::Orientation, Orientation)
 END_VIEW_BUILDER
 
 }  // namespace views
diff --git a/ui/views/controls/separator_unittest.cc b/ui/views/controls/separator_unittest.cc
index 60bc83c..a9f608c 100644
--- a/ui/views/controls/separator_unittest.cc
+++ b/ui/views/controls/separator_unittest.cc
@@ -71,9 +71,25 @@
   EXPECT_FALSE(gfx::test::AreBitmapsEqual(painted, unpainted.GetBitmap()));
 }
 
+TEST_F(SeparatorTest, GetPreferredSize_VerticalOrientation) {
+  // Orientation is vertical by default.
+  constexpr int kLength = 8;
+  separator_->SetPreferredLength(kLength);
+  EXPECT_EQ(separator_->GetPreferredSize(),
+            gfx::Size(Separator::kThickness, kLength));
+}
+
+TEST_F(SeparatorTest, GetPreferredSize_HorizontalOrientation) {
+  constexpr int kLength = 8;
+  separator_->SetOrientation(Separator::Orientation::kHorizontal);
+  separator_->SetPreferredLength(kLength);
+  EXPECT_EQ(separator_->GetPreferredSize(),
+            gfx::Size(kLength, Separator::kThickness));
+}
+
 TEST_F(SeparatorTest, ImageScaleBelowOne) {
   // Vertical line with 1[dp] thickness by default.
-  separator_->SetPreferredHeight(8);
+  separator_->SetPreferredLength(8);
   ExpectDrawAtLeastOnePixel(0.4);
 }
 
diff --git a/ui/views/interaction/element_tracker_views.h b/ui/views/interaction/element_tracker_views.h
index dbfc8cec..e69b0573 100644
--- a/ui/views/interaction/element_tracker_views.h
+++ b/ui/views/interaction/element_tracker_views.h
@@ -15,6 +15,7 @@
 #include "base/strings/string_piece_forward.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/element_tracker.h"
+#include "ui/views/view_utils.h"
 #include "ui/views/views_export.h"
 
 namespace views {
@@ -81,6 +82,12 @@
   // and that (if present) the element is a View; will DCHECK/crash otherwise.
   View* GetUniqueView(ui::ElementIdentifier id, ui::ElementContext context);
 
+  // Convenience method that calls GetUniqueView() and then safely converts the
+  // result to `T`, which must be a View subclass with metadata. Fails if a View
+  // is found but is not of the expected subtype.
+  template <class T>
+  T* GetUniqueViewAs(ui::ElementIdentifier id, ui::ElementContext context);
+
   // Returns the first View with the given `id` in the given `context`; null if
   // none is found. Ignores all other Views and any matching elements that are
   // not Views.
@@ -90,6 +97,13 @@
   View* GetFirstMatchingView(ui::ElementIdentifier id,
                              ui::ElementContext context);
 
+  // Convenience method that calls GetFirstMatchingView() and then safely
+  // converts the result to `T`, which must be a view subclass with metadata.
+  // Fails if a View is found but is not of the expected subtype.
+  template <class T>
+  T* GetFirstMatchingViewAs(ui::ElementIdentifier id,
+                            ui::ElementContext context);
+
   // Returns a list of all visible Views with identifier `id` in `context`.
   // The list may be empty. Ignores any non-Views elements which might match.
   ViewList GetAllMatchingViews(ui::ElementIdentifier id,
@@ -154,6 +168,30 @@
   std::map<const Widget*, WidgetTracker> widget_trackers_;
 };
 
+// Template implementations.
+
+template <class T>
+T* ElementTrackerViews::GetUniqueViewAs(ui::ElementIdentifier id,
+                                        ui::ElementContext context) {
+  views::View* const view = GetUniqueView(id, context);
+  if (!view)
+    return nullptr;
+  T* const result = views::AsViewClass<T>(view);
+  DCHECK(result);
+  return result;
+}
+
+template <class T>
+T* ElementTrackerViews::GetFirstMatchingViewAs(ui::ElementIdentifier id,
+                                               ui::ElementContext context) {
+  views::View* const view = GetFirstMatchingView(id, context);
+  if (!view)
+    return nullptr;
+  T* const result = views::AsViewClass<T>(view);
+  DCHECK(result);
+  return result;
+}
+
 }  // namespace views
 
 #endif  // UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_
diff --git a/ui/views/interaction/element_tracker_views_unittest.cc b/ui/views/interaction/element_tracker_views_unittest.cc
index 4f1a583..00ffc1c 100644
--- a/ui/views/interaction/element_tracker_views_unittest.cc
+++ b/ui/views/interaction/element_tracker_views_unittest.cc
@@ -17,6 +17,8 @@
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/element_test_util.h"
 #include "ui/base/interaction/element_tracker.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -45,6 +47,15 @@
   return view_element ? view_element->view() : nullptr;
 }
 
+// A subclass of View that has metadata.
+class TypedView : public View {
+ public:
+  METADATA_HEADER(TypedView);
+};
+
+BEGIN_METADATA(TypedView, View)
+END_METADATA
+
 // Watches events on the ElementTracker and converts the resulting values back
 // into Views from the original ui::TrackedElement objects. Monitoring
 // callbacks in this way could be done with gmock but the boilerplate would be
@@ -958,6 +969,28 @@
                          kTestElementID, context));
 }
 
+TEST_F(ElementTrackerViewsTest, GetUniqueViewAs) {
+  auto widget = CreateWidget();
+  TypedView* const contents =
+      widget->SetContentsView(std::make_unique<TypedView>());
+  widget->Show();
+  const ui::ElementContext context =
+      ElementTrackerViews::GetContextForView(contents);
+  EXPECT_EQ(nullptr,
+            ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>(
+                kTestElementID, context));
+
+  contents->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(contents,
+            ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>(
+                kTestElementID, context));
+
+  contents->ClearProperty(kElementIdentifierKey);
+  EXPECT_EQ(nullptr,
+            ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>(
+                kTestElementID, context));
+}
+
 TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewWithSingleView) {
   auto widget = CreateWidget();
   View* const contents = widget->SetContentsView(std::make_unique<View>());
@@ -976,6 +1009,31 @@
                          kTestElementID, context));
 }
 
+TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewAs) {
+  auto widget = CreateWidget();
+  TypedView* const contents =
+      widget->SetContentsView(std::make_unique<TypedView>());
+  widget->Show();
+  const ui::ElementContext context =
+      ElementTrackerViews::GetContextForView(contents);
+  EXPECT_EQ(
+      nullptr,
+      ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>(
+          kTestElementID, context));
+
+  contents->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(
+      contents,
+      ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>(
+          kTestElementID, context));
+
+  contents->ClearProperty(kElementIdentifierKey);
+  EXPECT_EQ(
+      nullptr,
+      ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>(
+          kTestElementID, context));
+}
+
 TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewWithMultipleViews) {
   auto widget = CreateWidget();
   View* const contents = widget->SetContentsView(std::make_unique<View>());
diff --git a/ui/views/metadata/type_conversion.cc b/ui/views/metadata/type_conversion.cc
index e67c754..5594adb 100644
--- a/ui/views/metadata/type_conversion.cc
+++ b/ui/views/metadata/type_conversion.cc
@@ -12,6 +12,7 @@
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/controls/separator.h"
 
 std::u16string ui::metadata::TypeConverter<GURL>::ToString(
     const GURL& source_value) {
@@ -84,3 +85,8 @@
     {ui::TextInputType::TEXT_INPUT_TYPE_DATE_TIME_FIELD,
      u"TEXT_INPUT_TYPE_DATE_TIME_FIELD"},
     {ui::TextInputType::TEXT_INPUT_TYPE_NULL, u"TEXT_INPUT_TYPE_NULL"})
+
+DEFINE_ENUM_CONVERTERS(views::Separator::Orientation,
+                       {views::Separator::Orientation::kHorizontal,
+                        u"kHorizontal"},
+                       {views::Separator::Orientation::kVertical, u"kVertical"})