diff --git a/AUTHORS b/AUTHORS
index a30acbb8..11c7be6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -92,6 +92,7 @@
 Ancil George <ancilgeorge@samsung.com>
 Andra Paraschiv <andra.paraschiv@intel.com>
 Andras Tokodi <a.tokodi@eyeo.com>
+Andreas Nazlidis <andreas221b@gmail.com>
 Andreas Papacharalampous <andreas@apap04.com>
 Andrei Borza <andrei.borza@gmail.com>
 Andrei Parvu <andrei.prv@gmail.com>
diff --git a/DEPS b/DEPS
index e1e037e..1de0a9f 100644
--- a/DEPS
+++ b/DEPS
@@ -253,15 +253,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '3e8de76db58e279eb032afafdb7d59c75031a369',
+  'skia_revision': '1341b906f67b83d820b417eeff3d87301a88bbb4',
   # 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': '0a850644ff2b11cf435b540516ac29669cb40697',
+  'v8_revision': '84e2692c27a330fa4fa35df67858c7b38f037cff',
   # 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': '0ffff9eda030ee02698df349427b54f74aad8fcf',
+  'angle_revision': '06c201f842e9e494f0abfaebd482f5816cdc7258',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -328,7 +328,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': '38edb6087b14c63a9c8bbb22744d3b891b595d79',
+  'devtools_frontend_revision': 'cbe0e6e59572fedc3a4f6de91413549f353ef568',
   # 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.
@@ -364,7 +364,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': '8e6c4bb59a40aed1b4fef1d0f82bdb7eb44a8130',
+  'dawn_revision': 'b2e4a961f6c9585f3f9c8a66538a2cfe15507404',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -431,7 +431,7 @@
   'libcxx_revision':       '79a2e924d96e2fc1e4b937c42efd08898fa472d7',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:5eb3845ec2d8296b4f41da4eca85302eb111fe69',
+  'gn_version': 'git_revision:ab9104586734cb45aa77c70ca5042edbcc9f6aa5',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -755,7 +755,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f92acd838005849f0f1e232a8b328e2819341b68',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '920a8ae9438c594e977226a50ae60f4f74134c1a',
       'condition': 'checkout_ios',
   },
 
@@ -847,7 +847,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'JALRUm29IzaOZMHSHyxzoO6llHEuiUR2OJUvNyP3VOAC',
+          'version': 'R0kp0Ixu1bGITiiZSH-9a8lACEEPQJywPwZXHnzfaBwC',
         },
       ],
       'dep_type': 'cipd',
@@ -858,7 +858,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'sf-e2QxF_iGYT8ocII-udWwR-LMB3k3D-7fjFAspkFkC',
+          'version': 't6w1ousMjHmAvExLFwjYchJqEY49Wm0goy3NjECJANwC',
         },
       ],
       'dep_type': 'cipd',
@@ -1112,7 +1112,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2f6c761324b5e374b6519b947fe248d97ecec5ee',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '5b1bca37540e6cb237a43fa25c5063295b4b3dc2',
       'condition': 'checkout_chromeos',
   },
 
@@ -1515,7 +1515,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '341b4249f0262485d55003dc533942d0bb5cc0ff',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f3337aeb229bb23d401c344644077be5949de4f0',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1730,10 +1730,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'cf04aebdf9b53bb2853f22a81465688daf879ec6',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3486401d1eb80ce4f2fe4b9ad8cc206ad6de4a1e',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '527da4e7df69b65f9d72de2d9abd5c61d1b606f8',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '33d31fbc4855190200c95c328ae65240e739992d',
+    Var('webrtc_git') + '/src.git' + '@' + '72424408ed03c7179c9940d4abd2893766f5b7bf',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1803,7 +1803,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7d2b7c40edd7166c36b8f83b631aba757abf8e62',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@02de6f675e5e431b31d600a4871636623952babc',
     'condition': 'checkout_src_internal',
   },
 
@@ -1833,7 +1833,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'kzwiGA_LQV7h5c1iwi3p1CS560QlKT0i-Rly4uDc_x8C',
+        'version': 'jgs1n4ivsHzwEWCKTBhAcdQBXdWdv6zStaWrJ2W7_FwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1844,7 +1844,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'VRBEfA1loAOaCSMB36rxtbSs9dECQCc3GmHS50SGzgUC',
+        'version': '25lGBBxhjDy5OrwSWFWCXeq2w6fVYjqcUiRuwrlgFD4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1855,7 +1855,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': '7xkLV6QkYMLTIdIUZ8jkUIBuvrWXjV-58jXLU53AHXQC',
+        'version': 'D_kw82LXFdKCWMHFvAyxUP7jsb-87yBbzuYXjuqoA2IC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8bd4392..4e030a55 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -3072,13 +3072,14 @@
         # Check that every set noparent line has a corresponding file:// line
         # listed in build/OWNERS.setnoparent. An exception is made for top level
         # directories since src/OWNERS shouldn't review them.
-        if (f.LocalPath().count('/') != 1
-                and (not f.LocalPath() in _EXCLUDED_SET_NO_PARENT_PATHS)):
+        linux_path = f.LocalPath().replace(input_api.os_path.sep, '/')
+        if (linux_path.count('/') != 1
+                and (not linux_path in _EXCLUDED_SET_NO_PARENT_PATHS)):
             for set_noparent_line in found_set_noparent_lines:
                 if set_noparent_line in found_owners_files:
                     continue
                 errors.append('  %s:%d' %
-                              (f.LocalPath(),
+                              (linux_path,
                                found_set_noparent_lines[set_noparent_line]))
 
     results = []
diff --git a/android_webview/browser/gfx/display_webview.cc b/android_webview/browser/gfx/display_webview.cc
index bcc2f34c..047d01c 100644
--- a/android_webview/browser/gfx/display_webview.cc
+++ b/android_webview/browser/gfx/display_webview.cc
@@ -24,6 +24,10 @@
   std::unique_ptr<viz::OverlayProcessorInterface> overlay_processor;
   OverlayProcessorWebView* overlay_processor_webview_raw = nullptr;
   if (features::IsAndroidSurfaceControlEnabled()) {
+    // TODO(crbug.com/1039876): This is to help triage bugs on pre-release
+    // android. Remove this log once feature is controlled only by feature flag
+    // or launched.
+    LOG(WARNING) << "WebView overlays are enabled!";
     auto overlay_processor_webview = std::make_unique<OverlayProcessorWebView>(
         gpu_dependency.get(), frame_sink_manager);
     overlay_processor_webview_raw = overlay_processor_webview.get();
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index b057d55..7efe1e0 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -268,6 +268,9 @@
     // Disabled until viz scheduling can be improved.
     features.DisableIfNotSet(::features::kUseSurfaceLayerForVideoDefault);
 
+    // Enabled by default for webview.
+    features.EnableIfNotSet(::features::kWebViewThreadSafeMediaDefault);
+
     // Disable dr-dc on webview.
     features.DisableIfNotSet(::features::kEnableDrDc);
 
diff --git a/ash/components/DEPS b/ash/components/DEPS
index 0f1e6d68..7e7d463 100644
--- a/ash/components/DEPS
+++ b/ash/components/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
   "+mojo/core",
   "+ui",
+
+  # Codes in //ash/components are used by ash-chrome, so allow it.
+  "+chromeos/ash",
 ]
diff --git a/ash/components/arc/BUILD.gn b/ash/components/arc/BUILD.gn
index 1ed67fd..31bab867 100644
--- a/ash/components/arc/BUILD.gn
+++ b/ash/components/arc/BUILD.gn
@@ -195,10 +195,10 @@
     "//ash/constants",
     "//ash/public/cpp",
     "//base",
+    "//chromeos/ash/components/dbus/upstart:upstart",
     "//chromeos/dbus:concierge_proto",
     "//chromeos/dbus:dbus",
     "//chromeos/dbus/session_manager",
-    "//chromeos/dbus/upstart:upstart",
     "//components/exo",
     "//ui/aura",
   ]
@@ -433,6 +433,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/dbus:test_support",
     "//chromeos/dbus/dlcservice",
     "//chromeos/dbus/patchpanel",
@@ -444,7 +445,6 @@
     "//chromeos/dbus/session_manager",
     "//chromeos/dbus/session_manager:login_manager_proto",
     "//chromeos/dbus/tpm_manager:tpm_manager",
-    "//chromeos/dbus/upstart",
     "//chromeos/network:test_support",
     "//chromeos/ui/frame",
     "//components/account_id",
diff --git a/ash/components/arc/arc_util.cc b/ash/components/arc/arc_util.cc
index 30cb09c..06a65b7 100644
--- a/ash/components/arc/arc_util.cc
+++ b/ash/components/arc/arc_util.cc
@@ -18,8 +18,8 @@
 #include "base/process/process_metrics.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/user_manager/user_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/arc/arc_util_unittest.cc b/ash/components/arc/arc_util_unittest.cc
index 869ba663..670579d0f8 100644
--- a/ash/components/arc/arc_util_unittest.cc
+++ b/ash/components/arc/arc_util_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "components/account_id/account_id.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/prefs/testing_pref_service.h"
diff --git a/ash/components/arc/enterprise/BUILD.gn b/ash/components/arc/enterprise/BUILD.gn
index 0e1e5a1..396ba6bb 100644
--- a/ash/components/arc/enterprise/BUILD.gn
+++ b/ash/components/arc/enterprise/BUILD.gn
@@ -30,10 +30,10 @@
     "//ash/components/policy",
     "//ash/constants",
     "//base",
+    "//chromeos/ash/components/dbus/upstart:upstart",
     "//chromeos/dbus:dbus",
     "//chromeos/dbus/arc:arc",
     "//chromeos/dbus/power",
-    "//chromeos/dbus/upstart:upstart",
     "//components/prefs",
     "//components/session_manager/core",
     "//components/user_manager",
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
index 373e2ba7..4fbccea 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -21,8 +21,8 @@
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
index bdd8e5a..ca8d599c 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
@@ -20,9 +20,9 @@
 #include "base/system/sys_info.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/fake_user_manager.h"
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index add6a9e..968ab2f 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -51,11 +51,11 @@
     "//ash/constants:constants",
     "//ash/public/cpp",
     "//ash/public/cpp/external_arc:external_arc",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/components/sensors:buildflags",
     "//chromeos/dbus/dlcservice:dlcservice",
     "//chromeos/dbus/dlcservice:dlcservice_proto",
     "//chromeos/dbus/session_manager",
-    "//chromeos/dbus/upstart",
     "//chromeos/memory:memory",
     "//chromeos/system:system",
     "//components/prefs:prefs",
@@ -124,10 +124,10 @@
     "//ash/components/cryptohome",
     "//ash/constants",
     "//base/test:test_support",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/dbus",
     "//chromeos/dbus/dlcservice",
     "//chromeos/dbus/session_manager",
-    "//chromeos/dbus/upstart",
     "//chromeos/system:system",
     "//components/account_id",
     "//components/prefs:test_support",
diff --git a/ash/components/arc/session/arc_client_adapter_unittest.cc b/ash/components/arc/session/arc_client_adapter_unittest.cc
index 590fedf..86101b2 100644
--- a/ash/components/arc/session/arc_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_client_adapter_unittest.cc
@@ -10,10 +10,10 @@
 #include "ash/components/arc/session/arc_service_manager.h"
 #include "base/command_line.h"
 #include "base/scoped_observation.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace arc {
diff --git a/ash/components/arc/session/arc_data_remover.cc b/ash/components/arc/session/arc_data_remover.cc
index e7e0e69..0612d6f 100644
--- a/ash/components/arc/session/arc_data_remover.cc
+++ b/ash/components/arc/session/arc_data_remover.cc
@@ -11,7 +11,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 
 namespace arc {
 
diff --git a/ash/components/arc/session/arc_data_remover_unittest.cc b/ash/components/arc/session/arc_data_remover_unittest.cc
index d918894..6238280 100644
--- a/ash/components/arc/session/arc_data_remover_unittest.cc
+++ b/ash/components/arc/session/arc_data_remover_unittest.cc
@@ -15,8 +15,8 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
index 50ec454..19101a16 100644
--- a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -48,11 +48,11 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_run_loop_timeout.h"
 #include "base/time/time.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/concierge/fake_concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "components/user_manager/user_names.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/login/auth/BUILD.gn b/ash/components/login/auth/BUILD.gn
index 15b630cd..fc84e2d 100644
--- a/ash/components/login/auth/BUILD.gn
+++ b/ash/components/login/auth/BUILD.gn
@@ -16,8 +16,8 @@
     "//ash/constants",
     "//base",
     "//base:i18n",
-    "//chromeos/dbus/authpolicy",
-    "//chromeos/dbus/authpolicy:authpolicy_proto",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
     "//chromeos/dbus/constants",
     "//chromeos/dbus/cryptohome",
     "//chromeos/dbus/cryptohome:cryptohome_proto",
@@ -128,8 +128,8 @@
     ":auth",
     "//base",
     "//base:i18n",
-    "//chromeos/dbus/authpolicy",
-    "//chromeos/dbus/authpolicy:authpolicy_proto",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
     "//chromeos/dbus/cryptohome",
     "//components/prefs",
     "//components/prefs:test_support",
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 15097b63..67fc795 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -298,7 +298,8 @@
 void LoginScreenController::ShowKioskAppError(const std::string& message) {
   ToastData toast_data("KioskAppError", ToastCatalogName::kKioskAppError,
                        base::UTF8ToUTF16(message), ToastData::kInfiniteDuration,
-                       /*visible_on_lock_screen=*/true);
+                       /*visible_on_lock_screen=*/true,
+                       /*dismiss_text=*/std::u16string());
   Shell::Get()->toast_manager()->Show(toast_data);
 }
 
diff --git a/ash/webui/media_app_ui/resources/js/launch.js b/ash/webui/media_app_ui/resources/js/launch.js
index b0e0d8e..b1f4438b 100644
--- a/ash/webui/media_app_ui/resources/js/launch.js
+++ b/ash/webui/media_app_ui/resources/js/launch.js
@@ -26,6 +26,7 @@
   '.3gp', '.avi', '.m4v', '.mkv', '.mov', '.mp4', '.mpeg', '.mpeg4', '.mpg',
   '.mpg4', '.ogv', '.ogx', '.ogm', '.webm'
 ];
+const PDF_EXTENSIONS = ['.pdf'];
 const OPEN_ACCEPT_ARGS = {
   'AUDIO': {
     description: loadTimeData.getString('fileFilterAudio'),
@@ -39,7 +40,19 @@
     description: loadTimeData.getString('fileFilterVideo'),
     accept: {'video/*': VIDEO_EXTENSIONS}
   },
-  'PDF': {description: 'PDF', accept: {'application/pdf': '.pdf'}},
+  'PDF': {description: 'PDF', accept: {'application/pdf': PDF_EXTENSIONS}},
+  // All supported file types, excluding text files (see b/183150750).
+  'ALL_EX_TEXT': {
+    description: 'All',
+    accept: {
+      '*/*': [
+        ...AUDIO_EXTENSIONS,
+        ...IMAGE_EXTENSIONS,
+        ...VIDEO_EXTENSIONS,
+        ...PDF_EXTENSIONS,
+      ]
+    }
+  },
 };
 
 /**
@@ -401,11 +414,12 @@
 });
 
 guestMessagePipe.registerHandler(Message.OPEN_FILES_WITH_PICKER, async (m) => {
-  const {startInToken, accept} = /** @type {!OpenFilesWithPickerMessage} */ (m);
+  const {startInToken, accept, isSingleFile} =
+      /** @type {!OpenFilesWithPickerMessage} */ (m);
   const acceptTypes = accept.map(k => OPEN_ACCEPT_ARGS[k]).filter(a => !!a);
 
   /** @type {!FilePickerOptions|DraftFilePickerOptions} */
-  const options = {multiple: true};
+  const options = {multiple: !isSingleFile};
 
   if (startInToken) {
     options.startIn = fileHandleForToken(startInToken);
diff --git a/ash/webui/media_app_ui/resources/js/media_app.externs.js b/ash/webui/media_app_ui/resources/js/media_app.externs.js
index 1598129..342b5e7 100644
--- a/ash/webui/media_app_ui/resources/js/media_app.externs.js
+++ b/ash/webui/media_app_ui/resources/js/media_app.externs.js
@@ -162,11 +162,11 @@
  * will be added to the last received file list.
  * TODO(b/203466987): Remove the undefined here once we can ensure all file
  * lists implement a openFilesWithFilePicker function.
- * @type {function(!Array<string>, ?mediaApp.AbstractFile):
+ * @type {function(!Array<string>, ?mediaApp.AbstractFile, ?boolean):
  *     !Promise<undefined>|undefined}
  */
 mediaApp.AbstractFileList.prototype.openFilesWithFilePicker = function(
-    acceptTypeKeys, startInFolder) {};
+    acceptTypeKeys, startInFolder, isSingleFile) {};
 
 /**
  * The delegate which exposes open source privileged WebUi functions to
diff --git a/ash/webui/media_app_ui/resources/js/message_types.js b/ash/webui/media_app_ui/resources/js/message_types.js
index 097a8cc..0fe02a5 100644
--- a/ash/webui/media_app_ui/resources/js/message_types.js
+++ b/ash/webui/media_app_ui/resources/js/message_types.js
@@ -149,9 +149,11 @@
  * The `accept` array contains keys of preconfigured file filters to include on
  * the file picker file type dropdown. These are keys such as "AUDIO", "IMAGE",
  * "PDF", etc. that are known on both sides of API boundary.
+ * `isSingleFile` prevents a user selecting more than one file.
  * @typedef {{
  *   startInToken: number,
- *   accept: !Array<string>
+ *   accept: !Array<string>,
+ *   isSingleFile: ?boolean,
  * }}
  */
 export let OpenFilesWithPickerMessage;
diff --git a/ash/webui/media_app_ui/resources/js/receiver.js b/ash/webui/media_app_ui/resources/js/receiver.js
index 362258447..3516ddcb 100644
--- a/ash/webui/media_app_ui/resources/js/receiver.js
+++ b/ash/webui/media_app_ui/resources/js/receiver.js
@@ -225,9 +225,10 @@
    * @override
    * @param {!Array<string>} acceptTypeKeys
    * @param {?mediaApp.AbstractFile} startInFolder
+   * @param {?boolean} isSingleFile
    * @return {!Promise<undefined>}
    */
-  async openFilesWithFilePicker(acceptTypeKeys, startInFolder) {
+  async openFilesWithFilePicker(acceptTypeKeys, startInFolder, isSingleFile) {
     // AbstractFile doesn't guarantee tokens. Use one from a ReceivedFile if
     // there is one, after ensuring it is valid.
     const fileRep = /** @type {{token: (number|undefined)}} */ (startInFolder);
@@ -236,6 +237,7 @@
     const msg = {
       startInToken: startInToken > 0 ? startInToken : 0,
       accept: acceptTypeKeys,
+      isSingleFile,
     };
     await parentMessagePipe.sendMessage(Message.OPEN_FILES_WITH_PICKER, msg);
   }
diff --git a/ash/webui/media_app_ui/test/guest_query_receiver.js b/ash/webui/media_app_ui/test/guest_query_receiver.js
index 0b5679d..2a19867 100644
--- a/ash/webui/media_app_ui/test/guest_query_receiver.js
+++ b/ash/webui/media_app_ui/test/guest_query_receiver.js
@@ -134,7 +134,8 @@
     /**
      * @typedef {{
      *   acceptTypeKeys: !Array<string>,
-     *   explicitToken: (number|undefined)
+     *   explicitToken: (number|undefined),
+     *   singleFile: ?boolean,
      * }}
      */
     let Args;
@@ -144,7 +145,7 @@
       existingFile = {token: args.explicitToken};
     }
     await assertLastReceivedFileList().openFilesWithFilePicker(
-        args.acceptTypeKeys, existingFile);
+        args.acceptTypeKeys, existingFile, args.singleFile);
     return 'openFilesWithFilePicker resolved';
   },
 };
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
index a20166ab..6ca7683 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
@@ -1273,10 +1273,14 @@
       [await createTestImageFile(10, 10, 'original_file.jpg')]);
 
   const simpleArgs = {acceptTypeKeys: ['VIDEO', 'IMAGE']};
-  let testResponse =
-      await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs});
-  assertEquals(
-      testResponse.testQueryResult, 'openFilesWithFilePicker resolved');
+  async function openFilesWithFilePickerWithSimpleArgs() {
+    const response =
+        await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs});
+    assertEquals(response.testQueryResult, 'openFilesWithFilePicker resolved');
+    return response;
+  }
+
+  let testResponse = await openFilesWithFilePickerWithSimpleArgs();
 
   // Spot-check the file picker options. It has lots of file extensions in it.
   const {multiple, startIn, excludeAcceptAllOption, types} = lastPickerOptions;
@@ -1298,11 +1302,29 @@
   // Test to handle invalid tokens (b/209342852). These should leave the
   // `startIn` option unspecified.
   simpleArgs.explicitToken = -1;
-  testResponse =
-      await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs});
-  assertEquals(
-      testResponse.testQueryResult, 'openFilesWithFilePicker resolved');
+  testResponse = await openFilesWithFilePickerWithSimpleArgs();
   assertEquals(lastPickerOptions.startIn, undefined);
+
+  // Ensure the `singleFile` argument is handled when set.
+  simpleArgs.singleFile = true;
+  await openFilesWithFilePickerWithSimpleArgs();
+  assertEquals(lastPickerOptions.multiple, false);
+
+  simpleArgs.singleFile = false;
+  await openFilesWithFilePickerWithSimpleArgs();
+  assertEquals(lastPickerOptions.multiple, true);
+
+  // Spot-check the ALL_EX_TEXT filter key, which groups all extensions.
+  simpleArgs.acceptTypeKeys = ['ALL_EX_TEXT'];
+  await openFilesWithFilePickerWithSimpleArgs();
+  const extensions = lastPickerOptions.types[0].accept['*/*'];
+  assertEquals(lastPickerOptions.types.length, 1);
+  assertEquals(lastPickerOptions.types[0].description, 'All');
+  assertEquals(extensions.includes('.pdf'), true);
+  assertEquals(extensions.includes('.jpeg'), true);
+  assertEquals(extensions.includes('.avi'), true);
+  assertEquals(extensions.includes('.mp3'), true);
+  assertEquals(extensions.includes('.zip'), false);
 };
 
 MediaAppUIBrowserTest.RelatedFiles = async () => {
diff --git a/ash/webui/personalization_app/resources/common/utils.ts b/ash/webui/personalization_app/resources/common/utils.ts
index 8f6e022..51bdbd99 100644
--- a/ash/webui/personalization_app/resources/common/utils.ts
+++ b/ash/webui/personalization_app/resources/common/utils.ts
@@ -99,11 +99,21 @@
 }
 
 /**
+ * Returns loading placeholders to render given the current inner width of the
+ * |window|. Placeholders are constructed using the specified |factory|.
+ */
+export function getLoadingPlaceholders<T>(factory: () => T): T[] {
+  const x = getNumberOfGridItemsPerRow();
+  const y = Math.floor(window.innerHeight / /*tileHeightPx=*/ 136);
+  return Array.from({length: x * y}, factory);
+}
+
+/**
  * Returns the number of grid items to render per row given the current inner
  * width of the |window|.
  */
 export function getNumberOfGridItemsPerRow(): number {
-  return window.innerWidth > 688 ? 4 : 3;
+  return window.innerWidth > 720 ? 4 : 3;
 }
 
 /**
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
index 76bd82d..fd64a628 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
@@ -17,8 +17,10 @@
       <wallpaper-grid-item
         class="photo"
         image-src="[[photo.url.url]]"
+        index="[[index]]"
         on-click="onPhotoSelected_"
         on-keypress="onPhotoSelected_"
+        placeholder$="[[isPhotoPlaceholder_(photo)]]"
         selected="[[isPhotoSelected_(
           photo, currentSelected_, pendingSelected_)]]"
         tabindex$="[[tabIndex]]">
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
index 31062237..8aeada7 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
@@ -18,7 +18,7 @@
 import {IronScrollThresholdElement} from 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
 import {afterNextRender} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isSelectionEvent} from '../../common/utils.js';
+import {getLoadingPlaceholders, isSelectionEvent} from '../../common/utils.js';
 import {CurrentWallpaper, GooglePhotosAlbum, GooglePhotosPhoto, WallpaperImage, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 import {isGooglePhotosPhoto} from '../utils.js';
@@ -27,6 +27,17 @@
 import {fetchGooglePhotosAlbum, selectWallpaper} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
+const PLACEHOLDER_ID = 'placeholder';
+
+/** Returns placeholders to show while Google Photos are loading. */
+function getPlaceholders(): GooglePhotosPhoto[] {
+  return getLoadingPlaceholders(() => {
+    const photo = new GooglePhotosPhoto();
+    photo.id = PLACEHOLDER_ID;
+    return photo;
+  });
+}
+
 export interface GooglePhotosPhotosByAlbumId {
   $: {grid: IronListElement, gridScrollThreshold: IronScrollThresholdElement};
 }
@@ -53,7 +64,7 @@
 
       album_: {
         type: Array,
-        value: [],
+        value: getPlaceholders,
       },
 
       albums: Array,
@@ -177,19 +188,20 @@
     }
 
     // If the album associated with |albumId| or |photosByAlbumId| have not yet
-    // been set, there is nothing to display. This occurs if the user refreshes
-    // the wallpaper app while its navigated to a Google Photos album.
+    // been set, there is nothing to display except placeholders. This occurs
+    // if the user refreshes the wallpaper app while its navigated to a Google
+    // Photos album.
     if (!Array.isArray(albums) || !albums.some(album => album.id === albumId) ||
         !photosByAlbumId) {
-      this.album_ = [];
+      this.album_ = getPlaceholders();
       return;
     }
 
     // If the currently selected album has not already been fetched, do so
-    // though there is still nothing to display.
+    // though there is still nothing to display except placeholders.
     if (!photosByAlbumId.hasOwnProperty(albumId)) {
       fetchGooglePhotosAlbum(this.wallpaperProvider_, this.getStore(), albumId);
-      this.album_ = [];
+      this.album_ = getPlaceholders();
       return;
     }
 
@@ -216,11 +228,16 @@
   /** Invoked on selection of a photo. */
   private onPhotoSelected_(e: Event&{model: {photo: GooglePhotosPhoto}}) {
     assert(e.model.photo);
-    if (isSelectionEvent(e)) {
+    if (!this.isPhotoPlaceholder_(e.model.photo) && isSelectionEvent(e)) {
       selectWallpaper(e.model.photo, this.wallpaperProvider_, this.getStore());
     }
   }
 
+  /** Returns whether the specified |photo| is a placeholder. */
+  private isPhotoPlaceholder_(photo: GooglePhotosPhoto|null): boolean {
+    return !!photo && photo.id === PLACEHOLDER_ID;
+  }
+
   // Returns whether the specified |photo| is currently selected.
   private isPhotoSelected_(
       photo: GooglePhotosPhoto|null,
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
index 866989a3..5ec5185 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
@@ -44,6 +44,10 @@
     outline: 2px solid var(--cros-focus-ring-color);
   }
 
+  :host([placeholder]) .item {
+    animation: 2210ms linear var(--animation-delay, 1s) infinite ripple;
+  }
+
   .item[aria-selected] {
     background-color: rgba(
       var(--cros-color-prominent-rgb),
@@ -134,7 +138,8 @@
     }
   }
 </style>
-<div class="item" aria-selected$="[[selected]]">
+<div class="item" aria-selected$="[[selected]]"
+     style$="[[getItemPlaceholderAnimationDelay_(index)]]">
   <template is="dom-if" if="[[isImageVisible_(imageSrc)]]">
     <img is="cr-auto-img" auto-src="[[imageSrc]]" clear-src with-cookies></img>
   </template>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
index d3d5e96..8626165 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
@@ -10,6 +10,7 @@
 import '../../common/styles.js';
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {getLoadingPlaceholderAnimationDelay} from '../../common/utils.js';
 import {getTemplate} from './wallpaper_grid_item_element.html.js';
 
 export class WallpaperGridItem extends PolymerElement {
@@ -24,6 +25,7 @@
   static get properties() {
     return {
       imageSrc: String,
+      index: Number,
       primaryText: String,
       secondaryText: String,
 
@@ -37,6 +39,9 @@
   /** The source for the image to render for the grid item. */
   imageSrc: string|undefined;
 
+  /** The index of the grid item within its parent grid. */
+  index: number;
+
   /** The primary text to render for the grid item. */
   primaryText: string|undefined;
 
@@ -46,6 +51,12 @@
   /** Whether the grid item is currently selected. */
   selected: boolean;
 
+  /** Returns the delay to use for the grid item's placeholder animation. */
+  private getItemPlaceholderAnimationDelay_(index: WallpaperGridItem['index']):
+      string {
+    return getLoadingPlaceholderAnimationDelay(index);
+  }
+
   /** Whether the image is currently visible. */
   private isImageVisible_() {
     return !!this.imageSrc && !!this.imageSrc.length;
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index d86ce0fd..642369f 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -12,7 +12,7 @@
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {Events, EventType, kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../common/constants.js';
-import {getCountText, getLoadingPlaceholderAnimationDelay, getNumberOfGridItemsPerRow, isNonEmptyArray, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
+import {getCountText, getLoadingPlaceholderAnimationDelay, getLoadingPlaceholders, isNonEmptyArray, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
 import {GooglePhotosEnablementState, WallpaperCollection} from '../trusted/personalization_app.mojom-webui.js';
 import {selectCollection, selectGooglePhotosCollection, selectLocalCollection, validateReceivedData} from '../untrusted/iframe_api.js';
 
@@ -26,9 +26,6 @@
 const kGooglePhotosCollectionId = 'google_photos_';
 const kLocalCollectionId = 'local_';
 
-/** Height in pixels of a tile. */
-const kTileHeightPx = 136;
-
 enum TileType {
   LOADING = 'loading',
   IMAGE_GOOGLE_PHOTOS = 'image_google_photos',
@@ -188,9 +185,7 @@
         value() {
           // Fill the view with loading tiles. Will be adjusted to the correct
           // number of tiles when collections are received.
-          const x = getNumberOfGridItemsPerRow();
-          const y = Math.floor(window.innerHeight / kTileHeightPx);
-          return Array.from({length: x * y}, () => ({type: TileType.LOADING}));
+          return getLoadingPlaceholders(() => ({type: TileType.LOADING}));
         }
       },
     };
diff --git a/base/android/build_info.cc b/base/android/build_info.cc
index 6bc3695..319db1e 100644
--- a/base/android/build_info.cc
+++ b/base/android/build_info.cc
@@ -80,7 +80,8 @@
       is_debug_android_(GetIntParam(params, 22)),
       is_tv_(GetIntParam(params, 23)),
       version_incremental_(StrDupParam(params, 24)),
-      hardware_(StrDupParam(params, 25)) {}
+      hardware_(StrDupParam(params, 25)),
+      is_at_least_t_(GetIntParam(params, 26)) {}
 
 // static
 BuildInfo* BuildInfo::GetInstance() {
diff --git a/base/android/build_info.h b/base/android/build_info.h
index 6717e80..294f9740 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -140,6 +140,8 @@
 
   const char* hardware() const { return hardware_; }
 
+  bool is_at_least_t() const { return is_at_least_t_; }
+
  private:
   friend struct BuildInfoSingletonTraits;
 
@@ -176,6 +178,7 @@
   const bool is_tv_;
   const char* const version_incremental_;
   const char* const hardware_;
+  const bool is_at_least_t_;
 };
 
 }  // namespace android
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index 47137eb6..a5f7e59 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -95,6 +95,7 @@
                 buildInfo.isTV ? "1" : "0",
                 Build.VERSION.INCREMENTAL,
                 Build.HARDWARE,
+                isAtLeastT() ? "1" : "0",
         };
     }
 
diff --git a/base/android/jni_generator/golden/testProxyMultiplexGenJni.2.golden b/base/android/jni_generator/golden/testProxyMultiplexGenJni.2.golden
new file mode 100644
index 0000000..43cb137
--- /dev/null
+++ b/base/android/jni_generator/golden/testProxyMultiplexGenJni.2.golden
@@ -0,0 +1,87 @@
+// 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.
+
+package org.chromium.base.natives;
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_registration_generator.py
+// Please do not change its content.
+
+public class GEN_JNI {
+    public static final boolean TESTING_ENABLED = false;
+    public static final boolean REQUIRE_MOCK = false;
+
+
+    public static void org_chromium_example_jni_1generator_SampleForAnnotationProcessor_foo() {
+        J.N.resolve_for_void(-3890945313637314700L);
+    }
+
+    public static Object org_chromium_example_jni_1generator_SampleForAnnotationProcessor_bar(Object sample) {
+        return J.N.resolve_for_object_O(2227777243221232668L, sample);
+    }
+
+    public static String org_chromium_example_jni_1generator_SampleForAnnotationProcessor_revString(String stringToReverse) {
+        return J.N.resolve_for_string_R(3717128594383367634L, stringToReverse);
+    }
+
+    public static String[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_sendToNative(String[] strs) {
+        return J.N.resolve_for_string_array_RA(12825275381484104L, strs);
+    }
+
+    public static Object[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_sendSamplesToNative(Object[] strs) {
+        return J.N.resolve_for_object_array_OA(1879234562834588228L, strs);
+    }
+
+    public static boolean org_chromium_example_jni_1generator_SampleForAnnotationProcessor_hasPhalange() {
+        return J.N.resolve_for_boolean(6577090212445038314L);
+    }
+
+    public static int[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_testAllPrimitives(int zint, int[] ints, long zlong, long[] longs, short zshort, short[] shorts, char zchar, char[] chars, byte zbyte, byte[] bytes, double zdouble, double[] doubles, float zfloat, float[] floats, boolean zbool, boolean[] bools) {
+        return J.N.resolve_for_int_array_IIAJJASSACCABBADDAFFAZZA(-7687282957371512872L, zint, ints, zlong, longs, zshort, shorts, zchar, chars, zbyte, bytes, zdouble, doubles, zfloat, floats, zbool, bools);
+    }
+
+    public static void org_chromium_example_jni_1generator_SampleForAnnotationProcessor_testSpecialTypes(Class clazz, Class[] classes, Throwable throwable, Throwable[] throwables, String string, String[] strings, Object tStruct, Object[] structs, Object obj, Object[] objects) {
+        J.N.resolve_for_void_LLATTARRAOOAOOA(-6275232019293411879L, clazz, classes, throwable, throwables, string, strings, tStruct, structs, obj, objects);
+    }
+
+    public static Throwable org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnThrowable() {
+        return J.N.resolve_for_throwable(-5073520581764159162L);
+    }
+
+    public static Throwable[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnThrowables() {
+        return J.N.resolve_for_throwable_array(17582460986153510L);
+    }
+
+    public static Class org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnClass() {
+        return J.N.resolve_for_class(-461602969360398827L);
+    }
+
+    public static Class[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnClasses() {
+        return J.N.resolve_for_class_array(-427008638958312484L);
+    }
+
+    public static String org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnString() {
+        return J.N.resolve_for_string(-3292889389021976364L);
+    }
+
+    public static String[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnStrings() {
+        return J.N.resolve_for_string_array(-5564218335546380707L);
+    }
+
+    public static Object org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnStruct() {
+        return J.N.resolve_for_object(5419834314509580268L);
+    }
+
+    public static Object[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnStructs() {
+        return J.N.resolve_for_object_array(2313390248928530514L);
+    }
+
+    public static Object org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnObject() {
+        return J.N.resolve_for_object(-426730623629742951L);
+    }
+
+    public static Object[] org_chromium_example_jni_1generator_SampleForAnnotationProcessor_returnObjects() {
+        return J.N.resolve_for_object_array(4508266875426191279L);
+    }
+}
diff --git a/base/android/jni_generator/golden/testProxyMultiplexGenJni.golden b/base/android/jni_generator/golden/testProxyMultiplexGenJni.golden
new file mode 100644
index 0000000..04c86a6
--- /dev/null
+++ b/base/android/jni_generator/golden/testProxyMultiplexGenJni.golden
@@ -0,0 +1,30 @@
+// 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.
+
+package J;
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_registration_generator.py
+// Please do not change its content.
+
+public class N {
+
+
+      public static native Class resolve_for_class(long switch_num);
+      public static native Class[] resolve_for_class_array(long switch_num);
+      public static native Object resolve_for_object(long switch_num);
+      public static native Object resolve_for_object_O(long switch_num, Object object_param1);
+      public static native Object[] resolve_for_object_array(long switch_num);
+      public static native Object[] resolve_for_object_array_OA(long switch_num, Object[] object_array_param1);
+      public static native String resolve_for_string(long switch_num);
+      public static native String resolve_for_string_R(long switch_num, String string_param1);
+      public static native String[] resolve_for_string_array(long switch_num);
+      public static native String[] resolve_for_string_array_RA(long switch_num, String[] string_array_param1);
+      public static native Throwable resolve_for_throwable(long switch_num);
+      public static native Throwable[] resolve_for_throwable_array(long switch_num);
+      public static native boolean resolve_for_boolean(long switch_num);
+      public static native int[] resolve_for_int_array_IIAJJASSACCABBADDAFFAZZA(long switch_num, int int_param1, int[] int_array_param1, long long_param1, long[] long_array_param1, short short_param1, short[] short_array_param1, char char_param1, char[] char_array_param1, byte byte_param1, byte[] byte_array_param1, double double_param1, double[] double_array_param1, float float_param1, float[] float_array_param1, boolean boolean_param1, boolean[] boolean_array_param1);
+      public static native void resolve_for_void(long switch_num);
+      public static native void resolve_for_void_LLATTARRAOOAOOA(long switch_num, Class class_param1, Class[] class_array_param1, Throwable throwable_param1, Throwable[] throwable_array_param1, String string_param1, String[] string_array_param1, Object object_param1, Object[] object_array_param1, Object object_param2, Object[] object_array_param2);
+}
diff --git a/base/android/jni_generator/golden/testProxyMultiplexNatives.golden b/base/android/jni_generator/golden/testProxyMultiplexNatives.golden
new file mode 100644
index 0000000..0671fba
--- /dev/null
+++ b/base/android/jni_generator/golden/testProxyMultiplexNatives.golden
@@ -0,0 +1,361 @@
+// Copyright 2017 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.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_registration_generator.py
+// Please do not change its content.
+
+#ifndef HEADER_GUARD
+#define HEADER_GUARD
+
+#include <jni.h>
+
+#include <iterator>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+#include "base/android/jni_int_wrapper.h"
+
+
+// Step 1: Forward declarations (classes).
+
+
+// Step 2: Forward declarations (methods).
+
+JNI_GENERATOR_EXPORT void
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1foo(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1bar(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject sample);
+JNI_GENERATOR_EXPORT jstring
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1revString(
+    JNIEnv* env,
+    jclass jcaller,
+    jstring stringToReverse);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendToNative(
+    JNIEnv* env,
+    jclass jcaller,
+    jobjectArray strs);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendSamplesToNative(
+    JNIEnv* env,
+    jclass jcaller,
+    jobjectArray strs);
+JNI_GENERATOR_EXPORT jboolean
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1hasPhalange(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jintArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testAllPrimitives(
+    JNIEnv* env,
+    jclass jcaller,
+    jint zint,
+    jintArray ints,
+    jlong zlong,
+    jlongArray longs,
+    jshort zshort,
+    jshortArray shorts,
+    jchar zchar,
+    jcharArray chars,
+    jbyte zbyte,
+    jbyteArray bytes,
+    jdouble zdouble,
+    jdoubleArray doubles,
+    jfloat zfloat,
+    jfloatArray floats,
+    jboolean zbool,
+    jbooleanArray bools);
+JNI_GENERATOR_EXPORT void
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testSpecialTypes(
+    JNIEnv* env,
+    jclass jcaller,
+    jclass clazz,
+    jobjectArray classes,
+    jthrowable throwable,
+    jobjectArray throwables,
+    jstring string,
+    jobjectArray strings,
+    jobject tStruct,
+    jobjectArray structs,
+    jobject obj,
+    jobjectArray objects);
+JNI_GENERATOR_EXPORT jthrowable
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowable(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowables(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jclass
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClass(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClasses(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jstring
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnString(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStrings(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStruct(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStructs(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObject(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObjects(
+    JNIEnv* env,
+    jclass jcaller);
+
+
+JNI_GENERATOR_EXPORT jclass Java_J_Ntest_resolve_1for_1class(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -461602969360398827:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClass(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1class was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1class_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -427008638958312484:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClasses(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1class_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobject Java_J_Ntest_resolve_1for_1object(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 5419834314509580268:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStruct(env, jcaller);
+
+          case -426730623629742951:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObject(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobject Java_J_Ntest_resolve_1for_1object_1O(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobject object_param1) {
+        switch (switch_num) {
+
+          case 2227777243221232668:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1bar(env, jcaller, object_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1O was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1object_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 2313390248928530514:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStructs(env, jcaller);
+
+          case 4508266875426191279:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObjects(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1object_1array_1OA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobjectArray object_array_param1) {
+        switch (switch_num) {
+
+          case 1879234562834588228:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendSamplesToNative(env, jcaller, object_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1array_1OA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jstring Java_J_Ntest_resolve_1for_1string(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -3292889389021976364:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnString(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jstring Java_J_Ntest_resolve_1for_1string_1R(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jstring string_param1) {
+        switch (switch_num) {
+
+          case 3717128594383367634:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1revString(env, jcaller, string_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1R was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1string_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -5564218335546380707:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStrings(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1string_1array_1RA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobjectArray string_array_param1) {
+        switch (switch_num) {
+
+          case 12825275381484104:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendToNative(env, jcaller, string_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1array_1RA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jthrowable Java_J_Ntest_resolve_1for_1throwable(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -5073520581764159162:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowable(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1throwable was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1throwable_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 17582460986153510:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowables(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1throwable_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jboolean Java_J_Ntest_resolve_1for_1boolean(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 6577090212445038314:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1hasPhalange(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1boolean was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jintArray Java_J_Ntest_resolve_1for_1int_1array_1IIAJJASSACCABBADDAFFAZZA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jint int_param1, jintArray int_array_param1, jlong long_param1, jlongArray long_array_param1, jshort short_param1, jshortArray short_array_param1, jchar char_param1, jcharArray char_array_param1, jbyte byte_param1, jbyteArray byte_array_param1, jdouble double_param1, jdoubleArray double_array_param1, jfloat float_param1, jfloatArray float_array_param1, jboolean boolean_param1, jbooleanArray boolean_array_param1) {
+        switch (switch_num) {
+
+          case -7687282957371512872:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testAllPrimitives(env, jcaller, int_param1, int_array_param1, long_param1, long_array_param1, short_param1, short_array_param1, char_param1, char_array_param1, byte_param1, byte_array_param1, double_param1, double_array_param1, float_param1, float_array_param1, boolean_param1, boolean_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1int_1array_1IIAJJASSACCABBADDAFFAZZA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT void Java_J_Ntest_resolve_1for_1void(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -3890945313637314700:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1foo(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1void was called with an invalid switch number: " << switch_num;
+            return;
+        }
+}
+JNI_GENERATOR_EXPORT void Java_J_Ntest_resolve_1for_1void_1LLATTARRAOOAOOA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jclass class_param1, jobjectArray class_array_param1, jthrowable throwable_param1, jobjectArray throwable_array_param1, jstring string_param1, jobjectArray string_array_param1, jobject object_param1, jobjectArray object_array_param1, jobject object_param2, jobjectArray object_array_param2) {
+        switch (switch_num) {
+
+          case -6275232019293411879:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testSpecialTypes(env, jcaller, class_param1, class_array_param1, throwable_param1, throwable_array_param1, string_param1, string_array_param1, object_param1, object_array_param1, object_param2, object_array_param2);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1void_1LLATTARRAOOAOOA was called with an invalid switch number: " << switch_num;
+            return;
+        }
+}
+
+#endif  // HEADER_GUARD
diff --git a/base/android/jni_generator/golden/testProxyMultiplexNativesRegistration.golden b/base/android/jni_generator/golden/testProxyMultiplexNativesRegistration.golden
new file mode 100644
index 0000000..707f98f
--- /dev/null
+++ b/base/android/jni_generator/golden/testProxyMultiplexNativesRegistration.golden
@@ -0,0 +1,440 @@
+// Copyright 2017 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.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_registration_generator.py
+// Please do not change its content.
+
+#ifndef HEADER_GUARD
+#define HEADER_GUARD
+
+#include <jni.h>
+
+#include <iterator>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+#include "base/android/jni_int_wrapper.h"
+
+
+// Step 1: Forward declarations (classes).
+
+
+// Step 2: Forward declarations (methods).
+
+JNI_GENERATOR_EXPORT void
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1foo(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1bar(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject sample);
+JNI_GENERATOR_EXPORT jstring
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1revString(
+    JNIEnv* env,
+    jclass jcaller,
+    jstring stringToReverse);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendToNative(
+    JNIEnv* env,
+    jclass jcaller,
+    jobjectArray strs);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendSamplesToNative(
+    JNIEnv* env,
+    jclass jcaller,
+    jobjectArray strs);
+JNI_GENERATOR_EXPORT jboolean
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1hasPhalange(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jintArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testAllPrimitives(
+    JNIEnv* env,
+    jclass jcaller,
+    jint zint,
+    jintArray ints,
+    jlong zlong,
+    jlongArray longs,
+    jshort zshort,
+    jshortArray shorts,
+    jchar zchar,
+    jcharArray chars,
+    jbyte zbyte,
+    jbyteArray bytes,
+    jdouble zdouble,
+    jdoubleArray doubles,
+    jfloat zfloat,
+    jfloatArray floats,
+    jboolean zbool,
+    jbooleanArray bools);
+JNI_GENERATOR_EXPORT void
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testSpecialTypes(
+    JNIEnv* env,
+    jclass jcaller,
+    jclass clazz,
+    jobjectArray classes,
+    jthrowable throwable,
+    jobjectArray throwables,
+    jstring string,
+    jobjectArray strings,
+    jobject tStruct,
+    jobjectArray structs,
+    jobject obj,
+    jobjectArray objects);
+JNI_GENERATOR_EXPORT jthrowable
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowable(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowables(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jclass
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClass(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClasses(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jstring
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnString(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStrings(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStruct(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStructs(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobject
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObject(
+    JNIEnv* env,
+    jclass jcaller);
+JNI_GENERATOR_EXPORT jobjectArray
+    Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObjects(
+    JNIEnv* env,
+    jclass jcaller);
+
+
+JNI_GENERATOR_EXPORT jclass Java_J_Ntest_resolve_1for_1class(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -461602969360398827:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClass(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1class was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1class_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -427008638958312484:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnClasses(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1class_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobject Java_J_Ntest_resolve_1for_1object(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 5419834314509580268:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStruct(env, jcaller);
+
+          case -426730623629742951:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObject(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobject Java_J_Ntest_resolve_1for_1object_1O(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobject object_param1) {
+        switch (switch_num) {
+
+          case 2227777243221232668:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1bar(env, jcaller, object_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1O was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1object_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 2313390248928530514:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStructs(env, jcaller);
+
+          case 4508266875426191279:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnObjects(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1object_1array_1OA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobjectArray object_array_param1) {
+        switch (switch_num) {
+
+          case 1879234562834588228:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendSamplesToNative(env, jcaller, object_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1object_1array_1OA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jstring Java_J_Ntest_resolve_1for_1string(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -3292889389021976364:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnString(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jstring Java_J_Ntest_resolve_1for_1string_1R(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jstring string_param1) {
+        switch (switch_num) {
+
+          case 3717128594383367634:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1revString(env, jcaller, string_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1R was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1string_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -5564218335546380707:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnStrings(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1string_1array_1RA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jobjectArray string_array_param1) {
+        switch (switch_num) {
+
+          case 12825275381484104:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1sendToNative(env, jcaller, string_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1string_1array_1RA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jthrowable Java_J_Ntest_resolve_1for_1throwable(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -5073520581764159162:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowable(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1throwable was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jobjectArray Java_J_Ntest_resolve_1for_1throwable_1array(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 17582460986153510:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1returnThrowables(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1throwable_1array was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jboolean Java_J_Ntest_resolve_1for_1boolean(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case 6577090212445038314:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1hasPhalange(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1boolean was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT jintArray Java_J_Ntest_resolve_1for_1int_1array_1IIAJJASSACCABBADDAFFAZZA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jint int_param1, jintArray int_array_param1, jlong long_param1, jlongArray long_array_param1, jshort short_param1, jshortArray short_array_param1, jchar char_param1, jcharArray char_array_param1, jbyte byte_param1, jbyteArray byte_array_param1, jdouble double_param1, jdoubleArray double_array_param1, jfloat float_param1, jfloatArray float_array_param1, jboolean boolean_param1, jbooleanArray boolean_array_param1) {
+        switch (switch_num) {
+
+          case -7687282957371512872:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testAllPrimitives(env, jcaller, int_param1, int_array_param1, long_param1, long_array_param1, short_param1, short_array_param1, char_param1, char_array_param1, byte_param1, byte_array_param1, double_param1, double_array_param1, float_param1, float_array_param1, boolean_param1, boolean_array_param1);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1int_1array_1IIAJJASSACCABBADDAFFAZZA was called with an invalid switch number: " << switch_num;
+            return {};
+        }
+}
+JNI_GENERATOR_EXPORT void Java_J_Ntest_resolve_1for_1void(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num) {
+        switch (switch_num) {
+
+          case -3890945313637314700:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1foo(env, jcaller);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1void was called with an invalid switch number: " << switch_num;
+            return;
+        }
+}
+JNI_GENERATOR_EXPORT void Java_J_Ntest_resolve_1for_1void_1LLATTARRAOOAOOA(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong switch_num, jclass class_param1, jobjectArray class_array_param1, jthrowable throwable_param1, jobjectArray throwable_array_param1, jstring string_param1, jobjectArray string_array_param1, jobject object_param1, jobjectArray object_array_param1, jobject object_param2, jobjectArray object_array_param2) {
+        switch (switch_num) {
+
+          case -6275232019293411879:
+            return Java_J_N_org_1chromium_1example_1jni_11generator_1SampleForAnnotationProcessor_1testSpecialTypes(env, jcaller, class_param1, class_array_param1, throwable_param1, throwable_array_param1, string_param1, string_array_param1, object_param1, object_array_param1, object_param2, object_array_param2);
+
+          default:
+            CHECK(false) << "JNI multiplexing function Java_J_Ntest_resolve_1for_1void_1LLATTARRAOOAOOA was called with an invalid switch number: " << switch_num;
+            return;
+        }
+}
+// Step 3: Method declarations.
+
+
+static const JNINativeMethod kMethods_J_NMAIN_DEX[] = {
+    { "resolve_for_boolean", "(J)Z", reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1boolean) },
+    { "resolve_for_class", "(J)Ljava/lang/Class;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1class) },
+    { "resolve_for_class_array", "(J)[Ljava/lang/Class;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1class_1array) },
+    { "resolve_for_int_array_IIAJJASSACCABBADDAFFAZZA", "(JI[IJ[JS[SC[CB[BD[DF[FZ[Z)[I",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1int_1array_1IIAJJASSACCABBADDAFFAZZA) },
+    { "resolve_for_object", "(J)Ljava/lang/Object;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1object) },
+    { "resolve_for_object_O", "(JLjava/lang/Object;)Ljava/lang/Object;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1object_1O) },
+    { "resolve_for_object_array", "(J)[Ljava/lang/Object;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1object_1array) },
+    { "resolve_for_object_array_OA", "(J[Ljava/lang/Object;)[Ljava/lang/Object;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1object_1array_1OA) },
+    { "resolve_for_string", "(J)Ljava/lang/String;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1string) },
+    { "resolve_for_string_R", "(JLjava/lang/String;)Ljava/lang/String;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1string_1R) },
+    { "resolve_for_string_array", "(J)[Ljava/lang/String;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1string_1array) },
+    { "resolve_for_string_array_RA", "(J[Ljava/lang/String;)[Ljava/lang/String;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1string_1array_1RA) },
+    { "resolve_for_throwable", "(J)Ljava/lang/Throwable;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1throwable) },
+    { "resolve_for_throwable_array", "(J)[Ljava/lang/Throwable;",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1throwable_1array) },
+    { "resolve_for_void", "(J)V", reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1void) },
+    { "resolve_for_void_LLATTARRAOOAOOA",
+        "(JLjava/lang/Class;[Ljava/lang/Class;Ljava/lang/Throwable;[Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)V",
+        reinterpret_cast<void*>(Java_J_Ntest_resolve_1for_1void_1LLATTARRAOOAOOA) }
+};
+
+namespace {
+
+JNI_REGISTRATION_EXPORT bool RegisterNative_J_NMAIN_DEX(JNIEnv* env) {
+  const int number_of_methods = std::size(kMethods_J_NMAIN_DEX);
+
+  base::android::ScopedJavaLocalRef<jclass> native_clazz =
+      base::android::GetClass(env, "J/N");
+  if (env->RegisterNatives(
+      native_clazz.obj(),
+      kMethods_J_NMAIN_DEX,
+      number_of_methods) < 0) {
+
+    jni_generator::HandleRegistrationError(env, native_clazz.obj(), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+
+// Step 4: Main dex and non-main dex registration functions.
+
+namespace test {
+
+bool RegisterMainDexNatives(JNIEnv* env) {
+  // Register natives in a proxy.
+  if (!RegisterNative_J_NMAIN_DEX(env)) {
+    return false;
+  }
+
+
+  return true;
+}
+
+bool RegisterNonMainDexNatives(JNIEnv* env) {
+
+  return true;
+}
+
+}  // namespace test
+
+#endif  // HEADER_GUARD
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 00d158e..9032036 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -979,12 +979,17 @@
 class HeaderFileGeneratorHelper(object):
   """Include helper methods for header generators."""
 
-  def __init__(self, class_name, fully_qualified_class, use_proxy_hash,
-               split_name):
+  def __init__(self,
+               class_name,
+               fully_qualified_class,
+               use_proxy_hash,
+               split_name=None,
+               enable_jni_multiplexing=False):
     self.class_name = class_name
     self.fully_qualified_class = fully_qualified_class
     self.use_proxy_hash = use_proxy_hash
     self.split_name = split_name
+    self.enable_jni_multiplexing = enable_jni_multiplexing
 
   def GetStubName(self, native):
     """Return the name of the stub function for this native method.
@@ -1001,7 +1006,9 @@
       else:
         method_name = EscapeClassName(native.proxy_name)
       return 'Java_%s_%s' % (EscapeClassName(
-          ProxyHelpers.GetQualifiedClass(self.use_proxy_hash)), method_name)
+          ProxyHelpers.GetQualifiedClass(
+              self.use_proxy_hash
+              or self.enable_jni_multiplexing)), method_name)
 
     template = Template('Java_${JAVA_NAME}_native${NAME}')
 
@@ -1016,8 +1023,9 @@
     ret = collections.OrderedDict()
     for entry in origin:
       if isinstance(entry, NativeMethod) and entry.is_proxy:
-        ret[ProxyHelpers.GetClass(self.use_proxy_hash)] \
-          = ProxyHelpers.GetQualifiedClass(self.use_proxy_hash)
+        use_hash = self.use_proxy_hash or self.enable_jni_multiplexing
+        ret[ProxyHelpers.GetClass(use_hash)] \
+          = ProxyHelpers.GetQualifiedClass(use_hash)
         continue
       ret[self.class_name] = self.fully_qualified_class
 
@@ -1050,7 +1058,8 @@
       }
       # Since all proxy methods use the same class, defining this in every
       # header file would result in duplicated extern initializations.
-      if full_clazz != ProxyHelpers.GetQualifiedClass(self.use_proxy_hash):
+      if full_clazz != ProxyHelpers.GetQualifiedClass(
+          self.use_proxy_hash or self.enable_jni_multiplexing):
         ret += [template.substitute(values)]
 
     class_getter = """\
@@ -1081,7 +1090,8 @@
       }
       # Since all proxy methods use the same class, defining this in every
       # header file would result in duplicated extern initializations.
-      if full_clazz != ProxyHelpers.GetQualifiedClass(self.use_proxy_hash):
+      if full_clazz != ProxyHelpers.GetQualifiedClass(
+          self.use_proxy_hash or self.enable_jni_multiplexing):
         ret += [template.substitute(values)]
 
     return ''.join(ret)
@@ -1101,10 +1111,12 @@
     self.constant_fields = constant_fields
     self.jni_params = jni_params
     self.options = options
-    self.helper = HeaderFileGeneratorHelper(self.class_name,
-                                            fully_qualified_class,
-                                            self.options.use_proxy_hash,
-                                            self.options.split_name)
+    self.helper = HeaderFileGeneratorHelper(
+        self.class_name,
+        fully_qualified_class,
+        self.options.use_proxy_hash,
+        split_name=self.options.split_name,
+        enable_jni_multiplexing=self.options.enable_jni_multiplexing)
 
   def GetContent(self):
     """Returns the content of the JNI binding file."""
@@ -1607,6 +1619,9 @@
       action='store_true',
       help='Hashes the native declaration of methods used '
       'in @JniNatives interface.')
+  parser.add_argument('--enable_jni_multiplexing',
+                      action='store_true',
+                      help='Enables JNI multiplexing for Java native methods')
   parser.add_argument(
       '--split_name',
       help='Split name that the Java classes should be loaded from.')
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 5ffb206..1997061 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -13,6 +13,7 @@
 
 from __future__ import print_function
 
+import collections
 import difflib
 import inspect
 import optparse
@@ -59,6 +60,7 @@
     self.enable_profiling = False
     self.enable_tracing = False
     self.use_proxy_hash = False
+    self.enable_jni_multiplexing = False
     self.always_mangle = False
     self.unchecked_exceptions = False
     self.split_name = None
@@ -69,7 +71,8 @@
   @staticmethod
   def _MergeRegistrationForTests(results,
                                  header_guard='HEADER_GUARD',
-                                 namespace='test'):
+                                 namespace='test',
+                                 enable_jni_multiplexing=False):
 
     results.sort(key=lambda d: d['FULL_CLASS_NAME'])
 
@@ -79,6 +82,27 @@
 
     combined_dict['HEADER_GUARD'] = header_guard
     combined_dict['NAMESPACE'] = namespace
+
+    if enable_jni_multiplexing:
+      proxy_signatures_list = sorted(
+          set(combined_dict['PROXY_NATIVE_SIGNATURES'].split('\n')))
+      combined_dict['PROXY_NATIVE_SIGNATURES'] = '\n'.join(
+          signature for signature in proxy_signatures_list)
+
+      proxy_native_array_list = sorted(
+          set(combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'].split(
+              '},\n')))
+      combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'] = '},\n'.join(
+          p for p in proxy_native_array_list if p != '') + '}'
+
+      signature_to_cases = collections.defaultdict(list)
+      for d in results:
+        for signature, cases in d['SIGNATURE_TO_CASES'].items():
+          signature_to_cases[signature].extend(cases)
+      combined_dict[
+          'FORWARDING_CALLS'] = jni_registration_generator._AddForwardingCalls(
+              signature_to_cases, namespace)
+
     return combined_dict
 
   def _JoinScriptDir(self, path):
@@ -356,18 +380,18 @@
                                               natives, [], [], jni_params,
                                               TestOptions())
     self.AssertGoldenTextEquals(h1.GetContent())
-    h2 = jni_registration_generator.HeaderGenerator(
-        '',
-        'org/chromium/TestJni',
-        natives,
-        jni_params,
-        True,
-        use_proxy_hash=False)
+    h2 = jni_registration_generator.HeaderGenerator('',
+                                                    '',
+                                                    'org/chromium/TestJni',
+                                                    natives,
+                                                    jni_params,
+                                                    True,
+                                                    use_proxy_hash=False)
     content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
 
-    self.AssertGoldenTextEquals(
-        jni_registration_generator.CreateFromDict(content, use_hash=False),
-        suffix='Registrations')
+    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
+        content, use_hash=False, manual_jni_registration=True),
+                                suffix='Registrations')
 
   def testInnerClassNatives(self):
     test_data = """
@@ -457,18 +481,18 @@
                                              TestOptions())
     self.AssertGoldenTextEquals(h.GetContent())
 
-    h2 = jni_registration_generator.HeaderGenerator(
-        '',
-        'org/chromium/TestJni',
-        natives,
-        jni_params,
-        True,
-        use_proxy_hash=False)
+    h2 = jni_registration_generator.HeaderGenerator('',
+                                                    '',
+                                                    'org/chromium/TestJni',
+                                                    natives,
+                                                    jni_params,
+                                                    True,
+                                                    use_proxy_hash=False)
     content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
 
-    self.AssertGoldenTextEquals(
-        jni_registration_generator.CreateFromDict(content, use_hash=False),
-        suffix='Registrations')
+    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
+        content, use_hash=False, manual_jni_registration=True),
+                                suffix='Registrations')
 
   def testCalledByNatives(self):
     test_data = """"
@@ -1409,6 +1433,7 @@
     jni_params = jni_generator.JniParams(qualified_clazz)
     main_dex_header = jni_registration_generator.HeaderGenerator(
         '',
+        '',
         qualified_clazz,
         natives,
         jni_params,
@@ -1417,7 +1442,9 @@
     content = TestGenerator._MergeRegistrationForTests([main_dex_header])
 
     self.AssertGoldenTextEquals(
-        jni_registration_generator.CreateFromDict(content, use_hash=False))
+        jni_registration_generator.CreateFromDict(content,
+                                                  use_hash=False,
+                                                  manual_jni_registration=True))
 
     other_qualified_clazz = 'test/foo/Bar'
     other_natives = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
@@ -1426,6 +1453,7 @@
     jni_params = jni_generator.JniParams(other_qualified_clazz)
     non_main_dex_header = jni_registration_generator.HeaderGenerator(
         '',
+        '',
         other_qualified_clazz,
         other_natives,
         jni_params,
@@ -1436,7 +1464,9 @@
                                                        [non_main_dex_header])
 
     self.AssertGoldenTextEquals(
-        jni_registration_generator.CreateFromDict(content, use_hash=False),
+        jni_registration_generator.CreateFromDict(content,
+                                                  use_hash=False,
+                                                  manual_jni_registration=True),
         'AndNonMainDex')
 
   def testProxyNatives(self):
@@ -1522,18 +1552,26 @@
     h1 = jni_generator.InlHeaderFileGenerator('', qualified_clazz, natives, [],
                                               [], jni_params, TestOptions())
     self.AssertGoldenTextEquals(h1.GetContent())
-    h2 = jni_registration_generator.HeaderGenerator(
-        '', qualified_clazz, natives, jni_params, False, use_proxy_hash=False)
+    h2 = jni_registration_generator.HeaderGenerator('',
+                                                    '',
+                                                    qualified_clazz,
+                                                    natives,
+                                                    jni_params,
+                                                    False,
+                                                    use_proxy_hash=False)
     content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
 
-    proxy_opts = jni_registration_generator.ProxyOptions()
+    proxy_opts = jni_registration_generator.ProxyOptions(
+        manual_jni_registration=True)
     self.AssertGoldenTextEquals(
         jni_registration_generator.CreateProxyJavaFromDict(content, proxy_opts),
         suffix='Java')
 
-    self.AssertGoldenTextEquals(
-        jni_registration_generator.CreateFromDict(content, proxy_opts.use_hash),
-        suffix='Registrations')
+    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
+        content,
+        proxy_opts.use_hash,
+        manual_jni_registration=proxy_opts.manual_jni_registration),
+                                suffix='Registrations')
 
   def testProxyHashedExample(self):
     opts = TestOptions()
@@ -1651,6 +1689,66 @@
     self.AssertListEquals(golden_natives, _RemoveHashedNames(natives))
 
 
+class MultiplexTestGenerator(BaseTest):
+  def testProxyMultiplexGenJni(self):
+    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
+    reg_dict = jni_registration_generator._DictForPath(
+        self._JoinScriptDir(path),
+        enable_jni_multiplexing=True,
+        namespace='test')
+    reg_dict = self._MergeRegistrationForTests([reg_dict],
+                                               enable_jni_multiplexing=True)
+
+    proxy_opts = jni_registration_generator.ProxyOptions(
+        enable_jni_multiplexing=True)
+    self.AssertGoldenTextEquals(
+        jni_registration_generator.CreateProxyJavaFromDict(
+            reg_dict, proxy_opts),
+        golden_file='testProxyMultiplexGenJni.golden')
+
+    self.AssertGoldenTextEquals(
+        jni_registration_generator.CreateProxyJavaFromDict(reg_dict,
+                                                           proxy_opts,
+                                                           forwarding=True),
+        golden_file='testProxyMultiplexGenJni.2.golden')
+
+  def testProxyMultiplexNatives(self):
+    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
+    reg_dict = jni_registration_generator._DictForPath(
+        self._JoinScriptDir(path),
+        enable_jni_multiplexing=True,
+        namespace='test')
+    reg_dict = self._MergeRegistrationForTests([reg_dict],
+                                               enable_jni_multiplexing=True)
+
+    proxy_opts = jni_registration_generator.ProxyOptions(
+        enable_jni_multiplexing=True)
+    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
+        reg_dict,
+        proxy_opts.use_hash,
+        enable_jni_multiplexing=proxy_opts.enable_jni_multiplexing),
+                                golden_file='testProxyMultiplexNatives.golden')
+
+  def testProxyMultiplexNativesRegistration(self):
+    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
+    reg_dict_for_registration = jni_registration_generator._DictForPath(
+        self._JoinScriptDir(path),
+        enable_jni_multiplexing=True,
+        namespace='test')
+    reg_dict_for_registration = self._MergeRegistrationForTests(
+        [reg_dict_for_registration], enable_jni_multiplexing=True)
+
+    proxy_opts = jni_registration_generator.ProxyOptions(
+        enable_jni_multiplexing=True)
+    self.AssertGoldenTextEquals(
+        jni_registration_generator.CreateFromDict(
+            reg_dict_for_registration,
+            proxy_opts.use_hash,
+            enable_jni_multiplexing=proxy_opts.enable_jni_multiplexing,
+            manual_jni_registration=True),
+        golden_file='testProxyMultiplexNativesRegistration.golden')
+
+
 def TouchStamp(stamp_path):
   dir_name = os.path.dirname(stamp_path)
   if not os.path.isdir(dir_name):
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index cb69f9f..3226da2 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -12,6 +12,7 @@
 import argparse
 import collections
 import functools
+import hashlib
 import multiprocessing
 import os
 import string
@@ -56,23 +57,16 @@
     header_path: If specified, generates a header file in this location.
     namespace: If specified, sets the namespace for the generated header file.
   """
-  # For JNI multiplexing, a 16-bit prefix is used to identify each individual
-  # java file path. This allows fewer multiplexed functions to resolve multiple
-  # different native functions with the same signature across the JNI boundary
-  # using switch statements. Should not exceed 65536 (2**16) number of paths.
-  assert len(java_file_paths) < 65536
-  java_path_prefix_tuples = [(path, index)
-                             for index, path in enumerate(java_file_paths)]
   # Without multiprocessing, script takes ~13 seconds for chrome_public_apk
   # on a z620. With multiprocessing, takes ~2 seconds.
   results = []
   with multiprocessing.Pool() as pool:
     for d in pool.imap_unordered(
         functools.partial(
-            _DictForPathAndPrefix,
+            _DictForPath,
             use_proxy_hash=proxy_opts.use_hash,
-            enable_jni_multiplexing=proxy_opts.enable_jni_multiplexing),
-        java_path_prefix_tuples):
+            enable_jni_multiplexing=proxy_opts.enable_jni_multiplexing,
+            namespace=namespace), java_file_paths):
       if d:
         results.append(d)
 
@@ -82,25 +76,43 @@
   combined_dict = {}
   for key in MERGEABLE_KEYS:
     combined_dict[key] = ''.join(d.get(key, '') for d in results)
-  # PROXY_NATIVE_SIGNATURES will have duplicates for JNI multiplexing since
-  # all native methods with similar signatures map to the same proxy.
+  # PROXY_NATIVE_SIGNATURES and PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX will have
+  # duplicates for JNI multiplexing since all native methods with similar
+  # signatures map to the same proxy. Similarly, there may be multiple switch
+  # case entries for the same proxy signatures.
   if proxy_opts.enable_jni_multiplexing:
     proxy_signatures_list = sorted(
         set(combined_dict['PROXY_NATIVE_SIGNATURES'].split('\n')))
     combined_dict['PROXY_NATIVE_SIGNATURES'] = '\n'.join(
         signature for signature in proxy_signatures_list)
 
+    proxy_native_array_list = sorted(
+        set(combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'].split('},\n')))
+    combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'] = '},\n'.join(
+        p for p in proxy_native_array_list if p != '') + '}'
+
+    signature_to_cases = collections.defaultdict(list)
+    for d in results:
+      for signature, cases in d['SIGNATURE_TO_CASES'].items():
+        signature_to_cases[signature].extend(cases)
+    combined_dict['FORWARDING_CALLS'] = _AddForwardingCalls(
+        signature_to_cases, namespace)
+
   if header_path:
     combined_dict['HEADER_GUARD'] = \
         os.path.splitext(header_path)[0].replace('/', '_').upper() + '_'
     combined_dict['NAMESPACE'] = namespace
-    header_content = CreateFromDict(combined_dict, proxy_opts.use_hash)
+    header_content = CreateFromDict(
+        combined_dict,
+        proxy_opts.use_hash,
+        enable_jni_multiplexing=proxy_opts.enable_jni_multiplexing,
+        manual_jni_registration=proxy_opts.manual_jni_registration)
     with build_utils.AtomicOutput(header_path, mode='w') as f:
       f.write(header_content)
 
   with build_utils.AtomicOutput(srcjar_path) as f:
     with zipfile.ZipFile(f, 'w') as srcjar:
-      if proxy_opts.use_hash:
+      if proxy_opts.use_hash or proxy_opts.enable_jni_multiplexing:
         # J/N.java
         build_utils.AddToZipHermetic(
             srcjar,
@@ -120,20 +132,10 @@
             data=CreateProxyJavaFromDict(combined_dict, proxy_opts))
 
 
-# A wrapper for imap_ordered to call with a tuple.
-def _DictForPathAndPrefix(path_prefix_tuple, use_proxy_hash,
-                          enable_jni_multiplexing):
-  path, switch_prefix = path_prefix_tuple
-  return _DictForPath(path,
-                      use_proxy_hash=use_proxy_hash,
-                      enable_jni_multiplexing=enable_jni_multiplexing,
-                      switch_prefix=switch_prefix)
-
-
 def _DictForPath(path,
                  use_proxy_hash=False,
                  enable_jni_multiplexing=False,
-                 switch_prefix=None):
+                 namespace=''):
   with open(path) as f:
     contents = jni_generator.RemoveComments(f.read())
     if '@JniIgnoreNatives' in contents:
@@ -149,23 +151,68 @@
       ptr_type='long')
   if len(natives) == 0:
     return None
-  namespace = jni_generator.ExtractJNINamespace(contents)
+  # The namespace for the content is separate from the namespace for the
+  # generated header file.
+  content_namespace = jni_generator.ExtractJNINamespace(contents)
   jni_params = jni_generator.JniParams(fully_qualified_class)
   jni_params.ExtractImportsAndInnerClasses(contents)
   is_main_dex = jni_generator.IsMainDexJavaClass(contents)
   header_generator = HeaderGenerator(
       namespace,
+      content_namespace,
       fully_qualified_class,
       natives,
       jni_params,
       is_main_dex,
       use_proxy_hash,
-      enable_jni_multiplexing=enable_jni_multiplexing,
-      switch_prefix=switch_prefix)
+      enable_jni_multiplexing=enable_jni_multiplexing)
   return header_generator.Generate()
 
 
-def _SetProxyRegistrationFields(registration_dict, use_hash):
+def _AddForwardingCalls(signature_to_cases, namespace):
+  template = string.Template("""
+JNI_GENERATOR_EXPORT ${RETURN} Java_${CLASS_NAME}_${PROXY_SIGNATURE}(
+    JNIEnv* env,
+    jclass jcaller,
+    ${PARAMS_IN_STUB}) {
+        switch (switch_num) {
+          ${CASES}
+          default:
+            CHECK(false) << "JNI multiplexing function Java_\
+${CLASS_NAME}_${PROXY_SIGNATURE} was called with an invalid switch number: "\
+ << switch_num;
+            return${DEFAULT_RETURN};
+        }
+}""")
+
+  switch_statements = []
+  for signature, cases in sorted(signature_to_cases.items()):
+    return_type, params_list = signature
+    params_in_stub = _GetJavaToNativeParamsList(params_list)
+    switch_statements.append(
+        template.substitute({
+            'RETURN':
+            jni_generator.JavaDataTypeToC(return_type),
+            'CLASS_NAME':
+            jni_generator.EscapeClassName(
+                jni_generator.ProxyHelpers.GetQualifiedClass(True) + namespace),
+            'PROXY_SIGNATURE':
+            jni_generator.EscapeClassName(
+                _GetMultiplexProxyName(return_type, params_list)),
+            'PARAMS_IN_STUB':
+            params_in_stub,
+            'CASES':
+            ''.join(cases),
+            'DEFAULT_RETURN':
+            '' if return_type == 'void' else ' {}',
+        }))
+
+  return ''.join(s for s in switch_statements)
+
+
+def _SetProxyRegistrationFields(registration_dict, use_hash,
+                                enable_jni_multiplexing,
+                                manual_jni_registration):
   registration_template = string.Template("""\
 
 static const JNINativeMethod kMethods_${ESCAPED_PROXY_CLASS}[] = {
@@ -202,17 +249,46 @@
   }
 """)
 
+  manual_registration = string.Template("""\
+// Step 3: Method declarations.
+
+${JNI_NATIVE_METHOD_ARRAY}\
+${PROXY_NATIVE_METHOD_ARRAY}\
+
+${JNI_NATIVE_METHOD}
+// Step 4: Main dex and non-main dex registration functions.
+
+namespace ${NAMESPACE} {
+
+bool RegisterMainDexNatives(JNIEnv* env) {\
+${REGISTER_MAIN_DEX_PROXY_NATIVES}
+${REGISTER_MAIN_DEX_NATIVES}
+  return true;
+}
+
+bool RegisterNonMainDexNatives(JNIEnv* env) {\
+${REGISTER_PROXY_NATIVES}
+${REGISTER_NON_MAIN_DEX_NATIVES}
+  return true;
+}
+
+}  // namespace ${NAMESPACE}
+""")
+
   sub_dict = {
       'ESCAPED_PROXY_CLASS':
       jni_generator.EscapeClassName(
-          jni_generator.ProxyHelpers.GetQualifiedClass(use_hash)),
+          jni_generator.ProxyHelpers.GetQualifiedClass(
+              use_hash or enable_jni_multiplexing)),
       'PROXY_CLASS':
-      jni_generator.ProxyHelpers.GetQualifiedClass(use_hash),
+      jni_generator.ProxyHelpers.GetQualifiedClass(use_hash
+                                                   or enable_jni_multiplexing),
       'KMETHODS':
       registration_dict['PROXY_NATIVE_METHOD_ARRAY'],
       'REGISTRATION_NAME':
       jni_generator.GetRegistrationFunctionName(
-          jni_generator.ProxyHelpers.GetQualifiedClass(use_hash)),
+          jni_generator.ProxyHelpers.GetQualifiedClass(
+              use_hash or enable_jni_multiplexing)),
   }
 
   if registration_dict['PROXY_NATIVE_METHOD_ARRAY']:
@@ -236,6 +312,12 @@
   registration_dict['REGISTER_PROXY_NATIVES'] = proxy_natives_registration
   registration_dict['REGISTER_MAIN_DEX_PROXY_NATIVES'] = main_dex_call
 
+  if manual_jni_registration:
+    registration_dict['MANUAL_REGISTRATION'] = manual_registration.substitute(
+        registration_dict)
+  else:
+    registration_dict['MANUAL_REGISTRATION'] = ''
+
 
 def CreateProxyJavaFromDict(registration_dict, proxy_opts, forwarding=False):
   template = string.Template("""\
@@ -255,11 +337,13 @@
 }
 """)
 
-  is_natives_class = not forwarding and proxy_opts.use_hash
+  is_natives_class = not forwarding and (proxy_opts.use_hash
+                                         or proxy_opts.enable_jni_multiplexing)
   class_name = jni_generator.ProxyHelpers.GetClass(is_natives_class)
   package = jni_generator.ProxyHelpers.GetPackage(is_natives_class)
 
-  if forwarding or not proxy_opts.use_hash:
+  if forwarding or not (proxy_opts.use_hash
+                        or proxy_opts.enable_jni_multiplexing):
     fields = string.Template("""\
     public static final boolean TESTING_ENABLED = ${TESTING_ENABLED};
     public static final boolean REQUIRE_MOCK = ${REQUIRE_MOCK};
@@ -283,7 +367,10 @@
   })
 
 
-def CreateFromDict(registration_dict, use_hash):
+def CreateFromDict(registration_dict,
+                   use_hash,
+                   enable_jni_multiplexing=False,
+                   manual_jni_registration=False):
   """Returns the content of the header file."""
 
   template = string.Template("""\
@@ -313,54 +400,52 @@
 // Step 2: Forward declarations (methods).
 
 ${FORWARD_DECLARATIONS}
-
-// Step 3: Method declarations.
-
-${JNI_NATIVE_METHOD_ARRAY}\
-${PROXY_NATIVE_METHOD_ARRAY}\
-
-${JNI_NATIVE_METHOD}
-// Step 4: Main dex and non-main dex registration functions.
-
-namespace ${NAMESPACE} {
-
-bool RegisterMainDexNatives(JNIEnv* env) {\
-${REGISTER_MAIN_DEX_PROXY_NATIVES}
-${REGISTER_MAIN_DEX_NATIVES}
-  return true;
-}
-
-bool RegisterNonMainDexNatives(JNIEnv* env) {\
-${REGISTER_PROXY_NATIVES}
-${REGISTER_NON_MAIN_DEX_NATIVES}
-  return true;
-}
-
-}  // namespace ${NAMESPACE}
-
+${FORWARDING_CALLS}
+${MANUAL_REGISTRATION}
 #endif  // ${HEADER_GUARD}
 """)
-  _SetProxyRegistrationFields(registration_dict, use_hash)
-
+  _SetProxyRegistrationFields(registration_dict, use_hash,
+                              enable_jni_multiplexing, manual_jni_registration)
+  if not enable_jni_multiplexing:
+    registration_dict['FORWARDING_CALLS'] = ''
   if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
     return ''
 
   return template.substitute(registration_dict)
 
 
+def _GetJavaToNativeParamsList(params_list):
+  if not params_list:
+    return 'jlong switch_num'
+
+  # Parameters are named after their type, with a unique number per parameter
+  # type to make sure the names are unique, even within the same types.
+  params_type_count = collections.defaultdict(int)
+  params_in_stub = []
+  for p in params_list:
+    params_type_count[p] += 1
+    params_in_stub.append(
+        '%s %s_param%d' %
+        (jni_generator.JavaDataTypeToC(p), p.replace(
+            '[]', '_array').lower(), params_type_count[p]))
+
+  return 'jlong switch_num, ' + ', '.join(params_in_stub)
+
+
 class HeaderGenerator(object):
   """Generates an inline header file for JNI registration."""
 
   def __init__(self,
                namespace,
+               content_namespace,
                fully_qualified_class,
                natives,
                jni_params,
                main_dex,
                use_proxy_hash,
-               enable_jni_multiplexing=False,
-               switch_prefix=None):
+               enable_jni_multiplexing=False):
     self.namespace = namespace
+    self.content_namespace = content_namespace
     self.natives = natives
     self.proxy_natives = [n for n in natives if n.is_proxy]
     self.non_proxy_natives = [n for n in natives if not n.is_proxy]
@@ -369,12 +454,12 @@
     self.class_name = self.fully_qualified_class.split('/')[-1]
     self.main_dex = main_dex
     self.helper = jni_generator.HeaderFileGeneratorHelper(
-        self.class_name, fully_qualified_class, use_proxy_hash, None)
+        self.class_name,
+        fully_qualified_class,
+        use_proxy_hash,
+        enable_jni_multiplexing=enable_jni_multiplexing)
     self.use_proxy_hash = use_proxy_hash
     self.enable_jni_multiplexing = enable_jni_multiplexing
-    # Each java file path is assigned a 16-bit integer as a prefix to the
-    # switch number to ensure uniqueness across all native methods.
-    self.switch_prefix = switch_prefix
     self.registration_dict = None
 
   def Generate(self):
@@ -394,8 +479,9 @@
         for native in self.proxy_natives))
     if self.enable_jni_multiplexing:
       self._AssignSwitchNumberToNatives()
+      self._AddCases()
 
-    if self.use_proxy_hash:
+    if self.use_proxy_hash or self.enable_jni_multiplexing:
       self.registration_dict['FORWARDING_PROXY_METHODS'] = ('\n'.join(
           _MakeForwardingProxy(
               native, enable_jni_multiplexing=self.enable_jni_multiplexing)
@@ -460,8 +546,8 @@
 """)
     open_namespace = ''
     close_namespace = ''
-    if self.namespace:
-      parts = self.namespace.split('::')
+    if self.content_namespace:
+      parts = self.content_namespace.split('::')
       all_namespaces = ['namespace %s {' % ns for ns in parts]
       open_namespace = '\n'.join(all_namespaces) + '\n'
       all_namespaces = ['}  // namespace %s' % ns for ns in parts]
@@ -469,8 +555,9 @@
       close_namespace = '\n'.join(all_namespaces) + '\n\n'
 
     body = self._SubstituteNativeMethods(template)
-    self._SetDictValue('JNI_NATIVE_METHOD_ARRAY', ''.join((open_namespace, body,
-                                                           close_namespace)))
+    if body:
+      self._SetDictValue('JNI_NATIVE_METHOD_ARRAY', ''.join(
+          (open_namespace, body, close_namespace)))
 
   def _GetKMethodsString(self, clazz):
     ret = []
@@ -485,27 +572,39 @@
                                'reinterpret_cast<void*>(${STUB_NAME}) },')
 
     name = 'native' + native.name
+    jni_signature = self.jni_params.Signature(native.params, native.return_type)
+    stub_name = self.helper.GetStubName(native)
+
     if native.is_proxy:
       # Literal name of the native method in the class that contains the actual
       # native declaration.
-      if self.use_proxy_hash:
+      if self.enable_jni_multiplexing:
+        return_type, params_list = native.return_and_signature
+        class_name = jni_generator.EscapeClassName(
+            jni_generator.ProxyHelpers.GetQualifiedClass(True) + self.namespace)
+        proxy_signature = jni_generator.EscapeClassName(
+            _GetMultiplexProxyName(return_type, params_list))
+
+        name = _GetMultiplexProxyName(return_type, params_list)
+        jni_signature = self.jni_params.Signature(
+            [jni_generator.Param(datatype='long', name='switch_num')] +
+            native.params, native.return_type)
+        stub_name = 'Java_' + class_name + '_' + proxy_signature
+      elif self.use_proxy_hash:
         name = native.hashed_proxy_name
       else:
         name = native.proxy_name
     values = {
-        'NAME':
-        name,
-        'JNI_SIGNATURE':
-        self.jni_params.Signature(native.params, native.return_type),
-        'STUB_NAME':
-        self.helper.GetStubName(native)
+        'NAME': name,
+        'JNI_SIGNATURE': jni_signature,
+        'STUB_NAME': stub_name
     }
     return template.substitute(values)
 
   def _AddProxyNativeMethodKStrings(self):
     """Returns KMethodString for wrapped native methods in all_classes """
 
-    if self.main_dex:
+    if self.main_dex or self.enable_jni_multiplexing:
       key = 'PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'
     else:
       key = 'PROXY_NATIVE_METHOD_ARRAY'
@@ -524,13 +623,14 @@
 
     for clazz, full_clazz in all_classes.items():
       if not sub_proxy:
-        if clazz == jni_generator.ProxyHelpers.GetClass(self.use_proxy_hash):
+        if clazz == jni_generator.ProxyHelpers.GetClass(
+            self.use_proxy_hash or self.enable_jni_multiplexing):
           continue
 
       kmethods = self._GetKMethodsString(clazz)
       namespace_str = ''
-      if self.namespace:
-        namespace_str = self.namespace + '::'
+      if self.content_namespace:
+        namespace_str = self.content_namespace + '::'
       if kmethods:
         values = {
             'NAMESPACE': namespace_str,
@@ -594,39 +694,84 @@
     return ''
 
   def _AssignSwitchNumberToNatives(self):
-    # The switch number for a native method is a 32-bit integer and indicates
-    # which native implementation the method should be dispatched to across
-    # the JNI multiplexing boundary.
-    signature_to_methods = collections.defaultdict(list)
+    # The switch number for a native method is a 64-bit long with the first
+    # bit being a sign digit. The signed two's complement is taken when
+    # appropriate to make use of negative numbers.
     for native in self.proxy_natives:
-      same_signature_methods = signature_to_methods[native.return_and_signature]
-      # Should not exceed 65536 (2**16) methods with same proxy signature.
-      assert len(same_signature_methods) < 65536
+      hashed_long = hashlib.md5(
+          native.proxy_name.encode('utf-8')).hexdigest()[:16]
+      switch_num = int(hashed_long, 16)
+      if (switch_num & 1 << 63):
+        switch_num -= (1 << 64)
 
-      native.switch_num = self.switch_prefix * (2**16) + len(
-          same_signature_methods)
-      same_signature_methods.append(native.proxy_name)
+      native.switch_num = str(switch_num)
+
+  def _AddCases(self):
+    # Switch cases are grouped together by the same proxy signatures.
+    template = string.Template("""
+          case ${SWITCH_NUM}:
+            return ${STUB_NAME}(env, jcaller${PARAMS});
+          """)
+
+    signature_to_cases = collections.defaultdict(list)
+    for native in self.proxy_natives:
+      signature = native.return_and_signature
+      params = _GetParamsListForMultiplex(signature[1], with_types=False)
+      values = {
+          'SWITCH_NUM': native.switch_num,
+          'STUB_NAME': self.helper.GetStubName(native),
+          'PARAMS': params,
+      }
+      signature_to_cases[signature].append(template.substitute(values))
+
+    self.registration_dict['SIGNATURE_TO_CASES'] = signature_to_cases
 
 
-def _GetParamsListForMultiplex(params_list):
+def _GetParamsListForMultiplex(params_list, with_types):
   if not params_list:
-    return 'int switch_num'
+    return ''
 
   # Parameters are named after their type, with a unique number per parameter
   # type to make sure the names are unique, even within the same types.
   params_type_count = collections.defaultdict(int)
-  params_with_types = []
+  params = []
   for p in params_list:
     params_type_count[p] += 1
-    params_with_types.append(
-        '%s %s_param%d' %
-        (p, p.replace('[]', '_array').lower(), params_type_count[p]))
+    param_type = p + ' ' if with_types else ''
+    params.append(
+        '%s%s_param%d' %
+        (param_type, p.replace('[]', '_array').lower(), params_type_count[p]))
 
-  return ', '.join(params_with_types) + ', int switch_num'
+  return ', ' + ', '.join(params)
 
 
-def _GetMultiplexProxyName(return_type):
-  return 'resolve_for_' + return_type.replace('[]', '_array').lower()
+def _GetMultiplexProxyName(return_type, params_list):
+  # Proxy signatures for methods are named after their return type and
+  # parameters to ensure uniqueness, even for the same return types.
+  params = ''
+  if params_list:
+    type_convert_dictionary = {
+        '[]': 'A',
+        'byte': 'B',
+        'char': 'C',
+        'double': 'D',
+        'float': 'F',
+        'int': 'I',
+        'long': 'J',
+        'Class': 'L',
+        'Object': 'O',
+        'String': 'R',
+        'short': 'S',
+        'Throwable': 'T',
+        'boolean': 'Z',
+    }
+    # Parameter types could contain multi-dimensional arrays and every
+    # instance of [] has to be replaced in the proxy signature name.
+    for k, v in type_convert_dictionary.items():
+      params_list = [p.replace(k, v) for p in params_list]
+    params = '_' + ''.join(p for p in params_list)
+
+  return 'resolve_for_' + return_type.replace('[]', '_array').lower() + params
 
 
 def _MakeForwardingProxy(proxy_native, enable_jni_multiplexing=False):
@@ -642,10 +787,11 @@
 
   if enable_jni_multiplexing:
     if not param_names:
-      param_names = proxy_native.switch_num
+      param_names = proxy_native.switch_num + 'L'
     else:
-      param_names += ', %s' % proxy_native.switch_num
-    proxy_method_name = _GetMultiplexProxyName(proxy_native.return_type)
+      param_names = proxy_native.switch_num + 'L, ' + param_names
+    return_type, params_list = proxy_native.return_and_signature
+    proxy_method_name = _GetMultiplexProxyName(return_type, params_list)
   else:
     proxy_method_name = proxy_native.hashed_proxy_name
 
@@ -683,8 +829,9 @@
 
     alt_name = None
     return_type, params_list = proxy_native.return_and_signature
-    proxy_name = _GetMultiplexProxyName(return_type)
-    params_with_types = _GetParamsListForMultiplex(params_list)
+    proxy_name = _GetMultiplexProxyName(return_type, params_list)
+    params_with_types = 'long switch_num' + _GetParamsListForMultiplex(
+        params_list, with_types=True)
   elif use_proxy_hash:
     signature_template = string.Template("""
       // Original name: ${ALT_NAME}""" + native_method_line)
@@ -711,6 +858,7 @@
   def __init__(self, **kwargs):
     self.use_hash = kwargs.get('use_hash', False)
     self.enable_jni_multiplexing = kwargs.get('enable_jni_multiplexing', False)
+    self.manual_jni_registration = kwargs.get('manual_jni_registration', False)
     self.enable_mocks = kwargs.get('enable_mocks', False)
     self.require_mocks = kwargs.get('require_mocks', False)
     # Can never require and disable.
@@ -766,6 +914,10 @@
       '--enable_jni_multiplexing',
       action='store_true',
       help='Enables JNI multiplexing for Java native methods')
+  arg_parser.add_argument(
+      '--manual_jni_registration',
+      action='store_true',
+      help='Manually do JNI registration - required for crazy linker')
   args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
 
   if not args.enable_proxy_mocks and args.require_mocks:
@@ -773,10 +925,16 @@
         'Invalid arguments: --require_mocks without --enable_proxy_mocks. '
         'Cannot require mocks if they are not enabled.')
 
+  if not args.header_path and args.manual_jni_registration:
+    arg_parser.error(
+        'Invalid arguments: --manual_jni_registration without --header-path. '
+        'Cannot manually register JNI if there is no output header file.')
+
   sources_files = sorted(set(build_utils.ParseGnList(args.sources_files)))
   proxy_opts = ProxyOptions(
       use_hash=args.use_proxy_hash,
       enable_jni_multiplexing=args.enable_jni_multiplexing,
+      manual_jni_registration=args.manual_jni_registration,
       require_mocks=args.require_mocks,
       enable_mocks=args.enable_proxy_mocks)
 
@@ -786,12 +944,11 @@
     java_file_paths.extend(
         p for p in build_utils.ReadSourcesList(f)
         if p.startswith('..') and p not in args.sources_exclusions)
-  _Generate(
-      java_file_paths,
-      args.srcjar_path,
-      proxy_opts=proxy_opts,
-      header_path=args.header_path,
-      namespace=args.namespace)
+  _Generate(java_file_paths,
+            args.srcjar_path,
+            proxy_opts=proxy_opts,
+            header_path=args.header_path,
+            namespace=args.namespace)
 
   if args.depfile:
     build_utils.WriteDepfile(args.depfile, args.srcjar_path,
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc
index c965c97..8542c0d 100644
--- a/base/threading/platform_thread_win.cc
+++ b/base/threading/platform_thread_win.cc
@@ -11,6 +11,7 @@
 #include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/profiler.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
@@ -35,8 +36,15 @@
 
 namespace base {
 
+const Feature kUseThreadPriorityLowest = {"UseThreadPriorityLowest",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 namespace {
 
+// Flag used to set thread priority to |THREAD_PRIORITY_LOWEST| for
+// |kUseThreadPriorityLowest| Feature.
+std::atomic<bool> g_use_thread_priority_lowest;
+
 // The most common value returned by ::GetThreadPriority() after background
 // thread mode is enabled on Windows 7.
 constexpr int kWin7BackgroundThreadModePriority = 4;
@@ -363,7 +371,7 @@
   PlatformThreadHandle::Handle thread_handle =
       PlatformThread::CurrentHandle().platform_handle();
 
-  if (priority != ThreadPriority::BACKGROUND) {
+  if (!g_use_thread_priority_lowest && priority != ThreadPriority::BACKGROUND) {
     // Exit background mode if the new priority is not BACKGROUND. This is a
     // no-op if not in background mode.
     ::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END);
@@ -380,7 +388,10 @@
       // MSDN recommends THREAD_MODE_BACKGROUND_BEGIN for threads that perform
       // background work, as it reduces disk and memory priority in addition to
       // CPU priority.
-      desired_priority = THREAD_MODE_BACKGROUND_BEGIN;
+      desired_priority =
+          g_use_thread_priority_lowest.load(std::memory_order_relaxed)
+              ? THREAD_PRIORITY_LOWEST
+              : THREAD_MODE_BACKGROUND_BEGIN;
       break;
     case ThreadPriority::NORMAL:
       desired_priority = THREAD_PRIORITY_NORMAL;
@@ -404,7 +415,7 @@
   DPLOG_IF(ERROR, !success) << "Failed to set thread priority to "
                             << desired_priority;
 
-  if (priority == ThreadPriority::BACKGROUND) {
+  if (!g_use_thread_priority_lowest && priority == ThreadPriority::BACKGROUND) {
     // In a background process, THREAD_MODE_BACKGROUND_BEGIN lowers the memory
     // and I/O priorities but not the CPU priority (kernel bug?). Use
     // THREAD_PRIORITY_LOWEST to also lower the CPU priority.
@@ -483,6 +494,12 @@
   return ThreadPriority::NORMAL;
 }
 
+void InitializePlatformThreadFeatures() {
+  g_use_thread_priority_lowest.store(
+      FeatureList::IsEnabled(kUseThreadPriorityLowest),
+      std::memory_order_relaxed);
+}
+
 // static
 size_t PlatformThread::GetDefaultThreadStackSize() {
   return 0;
diff --git a/base/threading/platform_thread_win.h b/base/threading/platform_thread_win.h
index 3e83317..c5cc3653 100644
--- a/base/threading/platform_thread_win.h
+++ b/base/threading/platform_thread_win.h
@@ -5,6 +5,8 @@
 #ifndef BASE_THREADING_PLATFORM_THREAD_WIN_H_
 #define BASE_THREADING_PLATFORM_THREAD_WIN_H_
 
+#include "base/win/windows_types.h"
+
 #include "base/threading/platform_thread.h"
 
 #include "base/base_export.h"
@@ -18,6 +20,9 @@
 BASE_EXPORT void AssertMemoryPriority(HANDLE thread, int memory_priority);
 
 }  // namespace internal
+
+BASE_EXPORT void InitializePlatformThreadFeatures();
+
 }  // namespace base
 
 #endif  // BASE_THREADING_PLATFORM_THREAD_WIN_H_
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 47321456..56e2c642 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -286,6 +286,9 @@
     # Use hashed symbol names to reduce JNI symbol overhead.
     use_hashed_jni_names = !is_java_debug
 
+    # Enables JNI multiplexing to reduce JNI native methods overhead.
+    enable_jni_multiplexing = false
+
     # Enables Java library desugaring.
     # This will cause an extra classes.dex file to appear in every apk.
     enable_jdk_library_desugaring = true
@@ -297,6 +300,10 @@
         !is_java_debug && android_channel != "stable"
   }
 
+  if (enable_jni_multiplexing) {
+    use_hashed_jni_names = false
+  }
+
   # Host stuff -----------------------------------------------------------------
 
   # Defines the name the Android build gives to the current host CPU
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 122ba6a..9dd2140 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -195,6 +195,9 @@
         if (use_hashed_jni_names) {
           args += [ "--use_proxy_hash" ]
         }
+        if (enable_jni_multiplexing) {
+          args += [ "--enable_jni_multiplexing" ]
+        }
         if (defined(invoker.namespace)) {
           args += [ "-n ${invoker.namespace}" ]
         }
@@ -308,6 +311,8 @@
   # Variables
   #   targets: List of .build_config.json supported targets to provide java sources.
   #   header_output: Path to the generated .h file (optional).
+  #   manual_jni_registration: Manually do JNI registration - required for
+  #     crazy linker. (optional)
   #   sources_exclusions: List of .java files that should be skipped. (optional)
   #   namespace: Registration functions will be wrapped into this. (optional)
   #   require_native_mocks: Enforce that any native calls using
@@ -324,6 +329,7 @@
   #   generate_jni_registration("chrome_jni_registration") {
   #     targets = [ ":chrome_public_apk" ]
   #     header_output = "$target_gen_dir/$target_name.h"
+  #     manual_jni_registration = false
   #     sources_exclusions = [
   #       "//path/to/Exception.java",
   #     ]
@@ -368,6 +374,10 @@
         args += [ "--use_proxy_hash" ]
       }
 
+      if (enable_jni_multiplexing) {
+        args += [ "--enable_jni_multiplexing" ]
+      }
+
       if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) {
         args += [ "--enable_proxy_mocks" ]
 
@@ -383,6 +393,11 @@
           "--header-path",
           rebase_path(invoker.header_output, root_build_dir),
         ]
+
+        if (!defined(invoker.manual_jni_registration) ||
+            invoker.manual_jni_registration) {
+          args += [ "--manual_jni_registration" ]
+        }
       }
 
       if (defined(invoker.sources_exclusions)) {
@@ -2758,6 +2773,7 @@
         }
         if (defined(invoker.jni_registration_header)) {
           header_output = invoker.jni_registration_header
+          manual_jni_registration = true
         }
         if (defined(invoker.jni_sources_exclusions)) {
           sources_exclusions = invoker.jni_sources_exclusions
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index fe4a9fd..f2a4e0a 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -203,25 +203,16 @@
     testonly = true
 
     _test_runner_py = "//build/fuchsia/test_runner.py"
+    executable = rebase_path(_test_runner_py)
 
     if (defined(invoker.is_test_exe) && invoker.is_test_exe) {
-      executable = "//testing/test_env.py"
-      executable_args =
-          [ "@WrappedPath(" + rebase_path(_test_runner_py, root_out_dir) + ")" ]
-      data += [
-        _test_runner_py,
-        "//.vpython3",
-      ]
-      data_deps += [ "//testing:test_scripts_shared" ]
-    } else {
-      executable = rebase_path(_test_runner_py)
-      executable_args = []
+      data += [ "//.vpython3" ]
     }
     output_name_format = "run_%package%"
     executable_wrapper = invoker.target_name
 
     # Populate the arguments used by the test runner, defined at build-time.
-    executable_args += [
+    executable_args = [
       "--out-dir",
       "@WrappedPath(.)",
       "--target-cpu",
diff --git a/build/fuchsia/aemu_target.py b/build/fuchsia/aemu_target.py
deleted file mode 100644
index 9f86c77..0000000
--- a/build/fuchsia/aemu_target.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# 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.
-
-"""Implements commands for running and interacting with Fuchsia on AEMU."""
-
-import emu_target
-import os
-import platform
-import qemu_target
-import logging
-
-from common import GetEmuRootForPlatform
-
-
-def GetTargetType():
-  return AemuTarget
-
-
-class AemuTarget(qemu_target.QemuTarget):
-  EMULATOR_NAME = 'aemu'
-
-  def __init__(self, out_dir, target_cpu, cpu_cores, require_kvm, ram_size_mb,
-               enable_graphics, hardware_gpu, logs_dir):
-    super(AemuTarget, self).__init__(out_dir, target_cpu, cpu_cores,
-                                     require_kvm, ram_size_mb, logs_dir)
-
-    self._enable_graphics = enable_graphics
-    self._hardware_gpu = hardware_gpu
-
-  @staticmethod
-  def CreateFromArgs(args):
-    return AemuTarget(args.out_dir, args.target_cpu, args.cpu_cores,
-                      args.require_kvm, args.ram_size_mb, args.enable_graphics,
-                      args.hardware_gpu, args.logs_dir)
-
-  @staticmethod
-  def RegisterArgs(arg_parser):
-    aemu_args = arg_parser.add_argument_group('aemu', 'AEMU arguments')
-    aemu_args.add_argument('--enable-graphics',
-                           action='store_true',
-                           default=False,
-                           help='Start AEMU with graphics instead of '\
-                                'headless.')
-    aemu_args.add_argument('--hardware-gpu',
-                           action='store_true',
-                           default=False,
-                           help='Use local GPU hardware instead of '\
-                                'Swiftshader.')
-
-  def _EnsureEmulatorExists(self, path):
-    assert os.path.exists(path), \
-          'This checkout is missing %s.' % (self.EMULATOR_NAME)
-
-  def _BuildCommand(self):
-    aemu_folder = GetEmuRootForPlatform(self.EMULATOR_NAME)
-
-    self._EnsureEmulatorExists(aemu_folder)
-    aemu_path = os.path.join(aemu_folder, 'emulator')
-
-    # `VirtioInput` is needed for touch input device support on Fuchsia.
-    # `RefCountPipe` is needed for proper cleanup of resources when a process
-    # that uses Vulkan dies inside the guest
-    aemu_features = 'VirtioInput,RefCountPipe'
-
-    # Configure the CPU to emulate.
-    # On Linux, we can enable lightweight virtualization (KVM) if the host and
-    # guest architectures are the same.
-    if self._IsKvmEnabled():
-      aemu_features += ',KVM,GLDirectMem,Vulkan'
-    else:
-      if self._target_cpu != 'arm64':
-        aemu_features += ',-GLDirectMem'
-
-    # Use Swiftshader for Vulkan if requested
-    gpu_target = 'swiftshader_indirect'
-    if self._hardware_gpu:
-      gpu_target = 'host'
-
-    aemu_command = [aemu_path]
-    if not self._enable_graphics:
-      aemu_command.append('-no-window')
-    # All args after -fuchsia flag gets passed to QEMU
-    aemu_command.extend([
-        '-feature', aemu_features, '-window-size', '1024x600', '-gpu',
-        gpu_target, '-verbose', '-fuchsia'
-    ])
-
-    aemu_command.extend(self._BuildQemuConfig())
-
-    aemu_command.extend([
-      '-vga', 'none',
-      '-device', 'virtio-keyboard-pci',
-      '-device', 'virtio_input_multi_touch_pci_1',
-      '-device', 'ich9-ahci,id=ahci'])
-    if platform.machine() == 'x86_64':
-      aemu_command.extend(['-device', 'isa-debug-exit,iobase=0xf4,iosize=0x04'])
-
-    logging.info(' '.join(aemu_command))
-    return aemu_command
-
-  def _GetVulkanIcdFile(self):
-    return os.path.join(GetEmuRootForPlatform(self.EMULATOR_NAME), 'lib64',
-                        'vulkan', 'vk_swiftshader_icd.json')
-
-  def _SetEnv(self):
-    env = os.environ.copy()
-    aemu_logging_env = {
-        "ANDROID_EMU_VK_NO_CLEANUP": "1",
-        "ANDROID_EMUGL_LOG_PRINT": "1",
-        "ANDROID_EMUGL_VERBOSE": "1",
-        "VK_ICD_FILENAMES": self._GetVulkanIcdFile(),
-        "VK_LOADER_DEBUG": "info,error",
-    }
-    env.update(aemu_logging_env)
-    return env
diff --git a/build/fuchsia/common_args.py b/build/fuchsia/common_args.py
index b55f447..50faf4a 100644
--- a/build/fuchsia/common_args.py
+++ b/build/fuchsia/common_args.py
@@ -11,7 +11,7 @@
 
 from common import GetHostArchFromPlatform
 
-BUILTIN_TARGET_NAMES = ['aemu', 'qemu', 'device', 'fvdl']
+BUILTIN_TARGET_NAMES = ['qemu', 'device', 'fvdl']
 
 
 def _AddTargetSpecificationArgs(arg_parser):
@@ -29,8 +29,8 @@
   device_args.add_argument('--device',
                            default=None,
                            choices=BUILTIN_TARGET_NAMES + ['custom'],
-                           help='Choose to run on fvdl|aemu|qemu|device. '
-                           'By default, Fuchsia will run on AEMU on x64 '
+                           help='Choose to run on fvdl|qemu|device. '
+                           'By default, Fuchsia will run on Fvdl on x64 '
                            'hosts and QEMU on arm64 hosts. Alternatively, '
                            'setting to custom will require specifying the '
                            'subclass of Target class used via the '
@@ -44,8 +44,8 @@
                            default=None,
                            help='Specify path to file that contains the '
                            'subclass of Target that will be used. Only '
-                           'needed if device specific operations such as '
-                           'paving is required.')
+                           'needed if device specific operations is '
+                           'required.')
 
 
 def _GetPathToBuiltinTarget(target_name):
diff --git a/build/fuchsia/fvdl_target.py b/build/fuchsia/fvdl_target.py
index 431f7cfe..fcadceda 100644
--- a/build/fuchsia/fvdl_target.py
+++ b/build/fuchsia/fvdl_target.py
@@ -86,8 +86,17 @@
                            default=False,
                            help='Run emulator with emulated nic via tun/tap.')
     fvdl_args.add_argument('--custom-image',
-                           help='Specify an image used for booting up the '
-                           'emulator.')
+                           help='Specify an image used for booting up the '\
+                                'emulator.')
+    fvdl_args.add_argument('--enable-graphics',
+                           action='store_true',
+                           default=False,
+                           help='Start emulator with graphics instead of '\
+                                'headless.')
+    fvdl_args.add_argument('--hardware-gpu',
+                           action='store_true',
+                           default=False,
+                           help='Use local GPU hardware instead Swiftshader.')
 
   def _BuildCommand(self):
     boot_data.ProvisionSSH()
diff --git a/build/fuchsia/generic_x64_target.py b/build/fuchsia/generic_x64_target.py
deleted file mode 100644
index a058971..0000000
--- a/build/fuchsia/generic_x64_target.py
+++ /dev/null
@@ -1,99 +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.
-"""Implements commands for running and interacting with Fuchsia generic
-build on devices."""
-
-import boot_data
-import device_target
-import logging
-import os
-
-from common import SDK_ROOT, EnsurePathExists, \
-                   GetHostToolPathFromPlatform, SubprocessCallWithTimeout
-
-
-def GetTargetType():
-  return GenericX64PavedDeviceTarget
-
-
-class GenericX64PavedDeviceTarget(device_target.DeviceTarget):
-  """In addition to the functionality provided by DeviceTarget, this class
-  automatically handles paving of x64 devices that use generic Fuchsia build.
-
-  If there are no running devices, then search for a device running Zedboot
-  and pave it.
-
-  If there's only one running device, or |_node_name| is set, then the
-  device's SDK version is checked unless --os-check=ignore is set.
-  If --os-check=update is set, then the target device is repaved if the SDK
-  version doesn't match."""
-
-  TARGET_HASH_FILE_PATH = '/data/.hash'
-
-  def _SDKHashMatches(self):
-    """Checks if /data/.hash on the device matches SDK_ROOT/.hash.
-
-    Returns True if the files are identical, or False otherwise.
-    """
-
-    with tempfile.NamedTemporaryFile() as tmp:
-      # TODO: Avoid using an exception for when file is unretrievable.
-      try:
-        self.GetFile(TARGET_HASH_FILE_PATH, tmp.name)
-      except subprocess.CalledProcessError:
-        # If the file is unretrievable for whatever reason, assume mismatch.
-        return False
-
-      return filecmp.cmp(tmp.name, os.path.join(SDK_ROOT, '.hash'), False)
-
-  def _ProvisionDeviceIfNecessary(self):
-    should_provision = False
-
-    if self._Discover():
-      self._ConnectToTarget()
-
-      if self._os_check != 'ignore':
-        if self._SDKHashMatches():
-          if self._os_check == 'update':
-            logging.info('SDK hash does not match; rebooting and repaving.')
-            self.RunCommand(['dm', 'reboot'])
-            should_provision = True
-          elif self._os_check == 'check':
-            raise Exception('Target device SDK version does not match.')
-    else:
-      should_provision = True
-
-    if should_provision:
-      self._ProvisionDevice()
-
-  def _ProvisionDevice(self):
-    """Pave a device with a generic image of Fuchsia."""
-
-    bootserver_path = GetHostToolPathFromPlatform('bootserver')
-    bootserver_command = [
-        bootserver_path, '-1', '--fvm',
-        EnsurePathExists(
-            boot_data.GetTargetFile('storage-sparse.blk',
-                                    self._GetTargetSdkArch(),
-                                    boot_data.TARGET_TYPE_GENERIC)),
-        EnsurePathExists(
-            boot_data.GetBootImage(self._out_dir, self._GetTargetSdkArch(),
-                                   boot_data.TARGET_TYPE_GENERIC))
-    ]
-
-    if self._node_name:
-      bootserver_command += ['-n', self._node_name]
-
-    bootserver_command += ['--']
-    bootserver_command += boot_data.GetKernelArgs()
-
-    logging.debug(' '.join(bootserver_command))
-    _, stdout = SubprocessCallWithTimeout(bootserver_command,
-                                          silent=False,
-                                          timeout_secs=300)
-
-    self._ParseNodename(stdout)
-
-    # Update the target's hash to match the current tree's.
-    self.PutFile(os.path.join(SDK_ROOT, '.hash'), TARGET_HASH_FILE_PATH)
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 4b3bf20f..05e93df 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220405.3.1
+7.20220406.1.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 571ba88f..05e93df 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220406.0.1
+7.20220406.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 4b3bf20f..05e93df 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220405.3.1
+7.20220406.1.1
diff --git a/build/fuchsia/start_emulator.py b/build/fuchsia/start_emulator.py
index 5cc93e53..b699d2f7 100755
--- a/build/fuchsia/start_emulator.py
+++ b/build/fuchsia/start_emulator.py
@@ -12,7 +12,6 @@
 import time
 import subprocess
 
-from aemu_target import AemuTarget
 from exit_on_sig_term import ExitOnSigTerm
 from fvdl_target import FvdlTarget
 
@@ -23,7 +22,6 @@
       'be re-used for multiple test runs.')
   AddLongRunningArgs(parser)
   FvdlTarget.RegisterArgs(parser)
-  AemuTarget.RegisterArgs(parser)
   common_args.AddCommonArgs(parser)
   args = parser.parse_args()
   args.out_dir = None
diff --git a/build_overrides/swiftshader.gni b/build_overrides/swiftshader.gni
index 2864da9..0e9511b 100644
--- a/build_overrides/swiftshader.gni
+++ b/build_overrides/swiftshader.gni
@@ -7,6 +7,3 @@
 
 # Path to SwiftShader
 swiftshader_dir = "//third_party/swiftshader"
-
-# Paths to SwiftShader dependencies
-swiftshader_spirv_tools_dir = "//third_party/vulkan-deps/spirv-tools/src"
diff --git a/build_overrides/vulkan_headers.gni b/build_overrides/vulkan_headers.gni
index e92d4ee7..37e67a8 100644
--- a/build_overrides/vulkan_headers.gni
+++ b/build_overrides/vulkan_headers.gni
@@ -5,3 +5,17 @@
 import("//build/config/ozone.gni")
 
 vulkan_use_x11 = ozone_platform_x11
+
+if (ozone_platform_wayland) {
+  vulkan_use_wayland = true
+
+  import("//third_party/wayland/features.gni")
+  if (!use_system_libwayland) {
+    wayland_dir = "//third_party/wayland"
+    vulkan_wayland_include_dirs = [
+      "$wayland_dir/src/src",
+      "$wayland_dir/include/src",
+      "$wayland_dir/include/protocol",
+    ]
+  }
+}
diff --git a/chrome/VERSION b/chrome/VERSION
index aa150ef..4a3fd8f 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=102
 MINOR=0
-BUILD=4988
+BUILD=4989
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 6a072c35..fee0d7f 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -3641,16 +3641,6 @@
       file_path = "$root_build_dir/apks/${_minimal_apks_filename}"
       data_deps = [ ":monochrome_public_minimal_apks" ]
     }
-
-    android_size_bot_config(
-        "resource_size_config_monochrome_public_minimal_apks") {
-      name = "MonochromePublic"
-      mapping_files = [ "apks/MonochromePublic.aab.mapping" ]
-      to_resource_sizes_py = {
-        apk_name = "apks/$_minimal_apks_filename"
-      }
-      supersize_input_file = "apks/$_minimal_apks_filename"
-    }
   }
 
   monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_bundle") {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
index 2fb69153..aab3ee0 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
@@ -11,6 +11,7 @@
 import static androidx.test.espresso.action.ViewActions.typeText;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.hasBackground;
 import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
 import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
@@ -18,6 +19,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
 import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withParent;
 import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
@@ -1005,6 +1007,46 @@
     }
 
     /**
+     * When using backend data privacy notice should have no background.
+     */
+    @Test
+    @MediumTest
+    public void testPrivacyNoticeStyleWithBackendData() throws Exception {
+        ArrayList<ActionProto> list = new ArrayList<>();
+        list.add(ActionProto.newBuilder()
+                         .setCollectUserData(
+                                 CollectUserDataProto.newBuilder()
+                                         .setDataSource(DataSource.newBuilder())
+                                         .setPrivacyNoticeText("3rd party privacy text")
+                                         .setShowTermsAsCheckbox(true)
+                                         .setRequestTermsAndConditions(true)
+                                         .setAcceptTermsAndConditionsText("accept terms"))
+                         .build());
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                SupportedScriptProto.newBuilder()
+                        .setPath("form_target_website.html")
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true))
+                        .build(),
+                list);
+
+        AutofillAssistantTestService testService =
+                new AutofillAssistantTestService(Collections.singletonList(script));
+        testService.setUserData(GetUserDataResponseProto.getDefaultInstance());
+        startAutofillAssistant(mTestRule.getActivity(), testService);
+
+        waitUntilViewMatchesCondition(
+                allOf(withText("3rd party privacy text"),
+                        withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)),
+                isDisplayed());
+        onView(allOf(isDescendantOfA(withTagValue(
+                             is(AssistantTagsForTesting
+                                             .COLLECT_USER_DATA_CHECKBOX_TERMS_SECTION_TAG))),
+                       withId(R.id.collect_data_privacy_notice)))
+                .check(matches(
+                        not(hasBackground(R.drawable.autofill_assistant_lightblue_rect_bg))));
+    }
+
+    /**
      * Load and show a card from backend.
      * TODO(b/214022384): Fill it into a form (requires unmasking).
      */
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index 8c9dd7b..dc011847 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -565,6 +565,7 @@
     @Test
     @MediumTest
     @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
+    @DisabledTest(message = "crbug.com/1313972")
     public void testGridToTabToOtherLive() throws InterruptedException {
         assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled());
         prepareTabs(2, 0, mUrl);
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
index d10999e..adec1a4 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
@@ -440,7 +440,7 @@
         Visit site
       </message>
       <message name="IDS_PRICE_DROP_NOTIFICATION_ACTION_TURN_OFF_ALERT" desc="This text shows when users swipe down the price drop notification. When users click this button, we won't send price drop notifications on this product to them any more.">
-        Turn off alert
+        Stop tracking product
       </message>
 
       <!-- Merchant Viewer strings -->
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_PRICE_DROP_NOTIFICATION_ACTION_TURN_OFF_ALERT.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_PRICE_DROP_NOTIFICATION_ACTION_TURN_OFF_ALERT.png.sha1
index cbdbf948..83c5c34 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_PRICE_DROP_NOTIFICATION_ACTION_TURN_OFF_ALERT.png.sha1
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_PRICE_DROP_NOTIFICATION_ACTION_TURN_OFF_ALERT.png.sha1
@@ -1 +1 @@
-5c89f6d072c31abb335bce7e63c026ca8e660086
\ No newline at end of file
+b93068dfcb51cb526128be786a78484aa01103c4
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
index 13a1683c..1b7efc5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
@@ -19,6 +19,7 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
@@ -139,6 +140,7 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @DisabledTest(message = "crbug.com/1313949")
     public void testPlayChromeDinoGamePedal() throws IOException, InterruptedException {
         List<AutocompleteMatch> suggestionsList = new ArrayList<>();
         suggestionsList.add(
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 36dc3a0..81550e6d 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -87,6 +87,7 @@
 
 #include "base/debug/close_handle_hook_win.h"
 #include "base/files/important_file_writer_cleaner.h"
+#include "base/threading/platform_thread_win.h"
 #include "base/win/atl.h"
 #include "chrome/child/v8_crashpad_support_win.h"
 #include "chrome/chrome_elf/chrome_elf_main.h"
@@ -704,6 +705,7 @@
   SetUpExtendedCrashReporting(is_browser_process);
   base::sequence_manager::internal::ThreadControllerPowerMonitor::
       InitializeOnMainThread();
+  base::InitializePlatformThreadFeatures();
 #endif
 
   // Initialize the HangWatcher.
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 0ddda05..6489227 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -633,6 +633,10 @@
                desc="Subpage summary of warning for a file blocked for being too large.">
         Chromium blocked this file because it's too big for a security check. Try again with files up to 50 MB
       </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT"
+               desc="Subpage summary of warning for Deep Scanning.">
+        Chromium recommends scanning this file because it may be dangerous
+      </message>
 
       <!-- Download Shelf Items -->
       <message name="IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING"
diff --git a/chrome/app/chromium_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1 b/chrome/app/chromium_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1
new file mode 100644
index 0000000..f6509e9a
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1
@@ -0,0 +1 @@
+a1014828ef495bc9f080b0a91ceb7dc637919ab9
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3011641..14af708 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2578,6 +2578,14 @@
                desc="Label for the Delete button on the blocked download bubble subpage.">
         Delete
       </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_SCAN"
+               desc="Label for the button prompting for Deep Scanning">
+        Scan
+      </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_OPEN"
+               desc="Label for the button prompting to bypass Deep Scanning">
+        Open
+      </message>
       <message name="IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_DISK_FULL"
                desc="Status text for a download item that was interrupted because there is not enough room on the disk to download this file.">
         Out of storage space
@@ -2702,6 +2710,10 @@
                desc="Retry button when a download is failed">
         Try again
       </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT"
+               desc="Prompt for Deep Scanning of a download">
+        Scan before opening
+      </message>
 
       <!-- Desktop omnibox PWA install icon -->
       <if expr="not is_android">
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_OPEN.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_OPEN.png.sha1
new file mode 100644
index 0000000..b42072d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_OPEN.png.sha1
@@ -0,0 +1 @@
+d6022e110b8cd372844715e36de245c0833fce19
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_SCAN.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_SCAN.png.sha1
new file mode 100644
index 0000000..b42072d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_SCAN.png.sha1
@@ -0,0 +1 @@
+d6022e110b8cd372844715e36de245c0833fce19
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT.png.sha1
new file mode 100644
index 0000000..812ac00
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT.png.sha1
@@ -0,0 +1 @@
+c34f9be456aad5fc47f3b5d180297721866f8b70
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 08fbe0e..986c1c1 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -668,6 +668,10 @@
                desc="Subpage summary of warning for a file blocked for being too large.">
         Chrome blocked this file because it's too big for a security check. Try again with files up to 50 MB
       </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT"
+               desc="Subpage summary of warning for Deep Scanning.">
+        Chrome recommends scanning this file because it may be dangerous
+      </message>
 
       <!-- Download Shelf Items -->
       <message name="IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING"
diff --git a/chrome/app/google_chrome_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1
new file mode 100644
index 0000000..f6509e9a
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT.png.sha1
@@ -0,0 +1 @@
+a1014828ef495bc9f080b0a91ceb7dc637919ab9
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a43f176..65e67ef 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -73,7 +73,6 @@
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/autofill_assistant/browser/features.h"
-#include "components/bookmarks/browser/features.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/browsing_data/core/features.h"
 #include "components/certificate_transparency/ct_features.h"
@@ -5168,11 +5167,6 @@
      FEATURE_VALUE_TYPE(chrome::android::kBookmarkBottomSheet)},
 #endif
 
-    {"apps-shortcut-default-off",
-     flag_descriptions::kAppsShortcutDefaultOffName,
-     flag_descriptions::kAppsShortcutDefaultOffDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(bookmarks::features::kAppsShortcutDefaultOff)},
-
     {"tab-groups-new-badge-promo",
      flag_descriptions::kTabGroupsNewBadgePromoName,
      flag_descriptions::kTabGroupsNewBadgePromoDescription, kOsDesktop,
@@ -5369,7 +5363,8 @@
 #if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
     {"chrome-wide-echo-cancellation",
      flag_descriptions::kChromeWideEchoCancellationName,
-     flag_descriptions::kChromeWideEchoCancellationDescription, kOsWin | kOsMac,
+     flag_descriptions::kChromeWideEchoCancellationDescription,
+     kOsMac | kOsWin | kOsLinux,
      FEATURE_VALUE_TYPE(media::kChromeWideEchoCancellation)},
 #endif  // BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
 
@@ -8322,6 +8317,14 @@
     {"intent-chip-app-icon", flag_descriptions::kIntentChipAppIconName,
      flag_descriptions::kIntentChipAppIconDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(apps::features::kIntentChipAppIcon)},
+
+    // TODO(crbug.com/1313512): Replace kOsLinux with Lacros for sync custom
+    // passphrase flag.
+    {"sync-chromeos-explicit-passphrase-sharing",
+     flag_descriptions::kSyncChromeOSExplicitPassphraseSharingName,
+     flag_descriptions::kSyncChromeOSExplicitPassphraseSharingDescription,
+     kOsCrOS | kOsLinux,
+     FEATURE_VALUE_TYPE(syncer::kSyncChromeOSExplicitPassphraseSharing)},
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.cc b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
index 3666a7fa..cc8d0275e0 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -146,8 +146,10 @@
 
 void AppServiceProxyAsh::RegisterCrosApiSubScriber(
     SubscriberCrosapi* subscriber) {
-  crosapi_subscriber_ = subscriber;
-  // TODO(crbug.com/1253250): Init apps and preferred apps.
+  if (base::FeatureList::IsEnabled(AppServiceCrosApiOnAppsWithoutMojom)) {
+    crosapi_subscriber_ = subscriber;
+    crosapi_subscriber_->OnApps(app_registry_cache_.GetAllApps());
+  }
 }
 
 void AppServiceProxyAsh::Uninstall(
@@ -157,6 +159,24 @@
   UninstallImpl(app_id, uninstall_source, parent_window, base::DoNothing());
 }
 
+void AppServiceProxyAsh::OnApps(std::vector<AppPtr> deltas,
+                                AppType app_type,
+                                bool should_notify_initialized) {
+  if (crosapi_subscriber_) {
+    crosapi_subscriber_->OnApps(deltas);
+  }
+
+  AppServiceProxyBase::OnApps(std::move(deltas), app_type,
+                              should_notify_initialized);
+}
+
+void AppServiceProxyAsh::OnApps(std::vector<apps::mojom::AppPtr> deltas,
+                                apps::mojom::AppType app_type,
+                                bool should_notify_initialized) {
+  AppServiceProxyBase::OnApps(std::move(deltas), app_type,
+                              should_notify_initialized);
+}
+
 void AppServiceProxyAsh::PauseApps(
     const std::map<std::string, PauseData>& pause_data) {
   if (!app_service_.is_connected()) {
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.h b/chrome/browser/apps/app_service/app_service_proxy_ash.h
index 6a5ced8..acce5d07 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.h
@@ -82,6 +82,12 @@
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  gfx::NativeWindow parent_window) override;
+  void OnApps(std::vector<AppPtr> deltas,
+              AppType app_type,
+              bool should_notify_initialized) override;
+  void OnApps(std::vector<apps::mojom::AppPtr> deltas,
+              apps::mojom::AppType app_type,
+              bool should_notify_initialized) override;
 
   // Pauses apps. |pause_data|'s key is the app_id. |pause_data|'s PauseData
   // is the time limit setting for the app, which is shown in the pause app
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.h b/chrome/browser/apps/app_service/app_service_proxy_base.h
index 3580c955..f1d881f 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.h
@@ -255,9 +255,9 @@
                      apps::mojom::WindowMode window_mode);
 
   // Called by an app publisher to inform the proxy of a change in app state.
-  void OnApps(std::vector<AppPtr> deltas,
-              AppType app_type,
-              bool should_notify_initialized);
+  virtual void OnApps(std::vector<AppPtr> deltas,
+                      AppType app_type,
+                      bool should_notify_initialized);
 
  protected:
   // An adapter, presenting an IconLoader interface based on the underlying
diff --git a/chrome/browser/apps/app_service/subscriber_crosapi.cc b/chrome/browser/apps/app_service/subscriber_crosapi.cc
index 419c1056..0037aa073 100644
--- a/chrome/browser/apps/app_service/subscriber_crosapi.cc
+++ b/chrome/browser/apps/app_service/subscriber_crosapi.cc
@@ -23,17 +23,16 @@
 
 namespace {
 
-bool Accepts(apps::mojom::AppType app_type) {
-  return app_type == apps::mojom::AppType::kUnknown ||
-         app_type == apps::mojom::AppType::kArc ||
-         app_type == apps::mojom::AppType::kWeb ||
-         app_type == apps::mojom::AppType::kSystemWeb ||
-         app_type == apps::mojom::AppType::kStandaloneBrowserChromeApp;
+bool Accepts(apps::AppType app_type) {
+  return app_type == apps::AppType::kUnknown ||
+         app_type == apps::AppType::kArc || app_type == apps::AppType::kWeb ||
+         app_type == apps::AppType::kSystemWeb ||
+         app_type == apps::AppType::kStandaloneBrowserChromeApp;
 }
 
 bool Accepts(const std::vector<apps::mojom::AppPtr>& deltas) {
   for (const auto& delta : deltas) {
-    if (!Accepts(delta->app_type)) {
+    if (!Accepts(apps::ConvertMojomAppTypToAppType(delta->app_type))) {
       return false;
     }
   }
@@ -63,9 +62,32 @@
       &SubscriberCrosapi::OnCrosapiDisconnected, base::Unretained(this)));
 }
 
+void SubscriberCrosapi::OnApps(const std::vector<apps::AppPtr>& deltas) {
+  if (!subscriber_.is_bound()) {
+    return;
+  }
+
+  std::vector<AppPtr> apps;
+  for (const auto& delta : deltas) {
+    if (Accepts(delta->app_type)) {
+      apps.push_back(delta->Clone());
+    }
+  }
+
+  // Apps are sent to Lacros side for preferred apps only, so we don't need to
+  // set initialized status.
+  subscriber_->OnApps(std::move(apps), AppType::kUnknown,
+                      /*should_notify_initialized=*/false);
+}
+
 void SubscriberCrosapi::OnApps(std::vector<apps::mojom::AppPtr> deltas,
-                               apps::mojom::AppType app_type,
+                               apps::mojom::AppType mojom_app_type,
                                bool should_notify_initialized) {
+  if (base::FeatureList::IsEnabled(AppServiceCrosApiOnAppsWithoutMojom)) {
+    return;
+  }
+
+  auto app_type = ConvertMojomAppTypToAppType(mojom_app_type);
   if (Accepts(app_type) && Accepts(deltas) && subscriber_.is_bound()) {
     std::vector<AppPtr> apps;
     for (const auto& mojom_app : deltas) {
@@ -73,8 +95,7 @@
         apps.push_back(ConvertMojomAppToApp(mojom_app));
       }
     }
-    subscriber_->OnApps(std::move(apps), ConvertMojomAppTypToAppType(app_type),
-                        should_notify_initialized);
+    subscriber_->OnApps(std::move(apps), app_type, should_notify_initialized);
   }
 }
 
diff --git a/chrome/browser/apps/app_service/subscriber_crosapi.h b/chrome/browser/apps/app_service/subscriber_crosapi.h
index d42d549..9a3947c 100644
--- a/chrome/browser/apps/app_service/subscriber_crosapi.h
+++ b/chrome/browser/apps/app_service/subscriber_crosapi.h
@@ -28,6 +28,8 @@
 // crosapi and App Service.
 //
 // See components/services/app_service/README.md.
+//
+// TODO(crbug.com/1253250): Remove dependency on apps::mojom::Subscriber.
 class SubscriberCrosapi : public KeyedService,
                           public apps::mojom::Subscriber,
                           public crosapi::mojom::AppServiceProxy {
@@ -40,10 +42,12 @@
   void RegisterAppServiceProxyFromCrosapi(
       mojo::PendingReceiver<crosapi::mojom::AppServiceProxy> receiver);
 
+  void OnApps(const std::vector<apps::AppPtr>& deltas);
+
  protected:
   // apps::mojom::Subscriber overrides.
   void OnApps(std::vector<apps::mojom::AppPtr> deltas,
-              apps::mojom::AppType app_type,
+              apps::mojom::AppType mojom_app_type,
               bool should_notify_initialized) override;
   void OnCapabilityAccesses(
       std::vector<apps::mojom::CapabilityAccessPtr> deltas) override;
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index 401bb1e..5007493 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -62,9 +62,9 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/constants/devicetype.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_member.h"
diff --git a/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge_unittest.cc b/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge_unittest.cc
index 43f10a63..d982d03b 100644
--- a/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge_unittest.cc
+++ b/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge_unittest.cc
@@ -15,7 +15,7 @@
 #include "ash/components/arc/test/test_browser_context.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
index f48c75bf..08c1054 100644
--- a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
+++ b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
@@ -10,6 +10,7 @@
 #include "ash/components/arc/arc_prefs.h"
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "ash/components/arc/test/arc_util_test_support.h"
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "base/base64.h"
 #include "base/bind.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/ash/arc/enterprise/cert_store/cert_store_service.h"
 #include "chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.h"
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
+#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_factory.h"
 #include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
@@ -295,11 +297,11 @@
 
 }  // namespace
 
-class CertStoreServiceTest
-    : public MixinBasedInProcessBrowserTest,
-      public ::testing::WithParamInterface<std::vector<TestCertData>> {
+class CertStoreServiceTest : public MixinBasedInProcessBrowserTest,
+                             public ::testing::WithParamInterface<
+                                 std::tuple<std::vector<TestCertData>, bool>> {
  public:
-  CertStoreServiceTest() = default;
+  CertStoreServiceTest();
   CertStoreServiceTest(const CertStoreServiceTest& other) = delete;
   CertStoreServiceTest& operator=(const CertStoreServiceTest&) = delete;
 
@@ -343,6 +345,7 @@
   policy::DevicePolicyCrosTestHelper device_policy_helper_;
   policy::AffiliationMixin affiliation_mixin_{&mixin_host_,
                                               &device_policy_helper_};
+  std::vector<TestCertData> test_cert_data_vector_;
 
  private:
   // Creates ScopedCERTCertificates for each |certs_to_setup| and appends them
@@ -373,8 +376,29 @@
 
   // Owned by the CertStoreService instance.
   FakeArcKeymasterBridge* keymaster_bridge_;
+
+  base::test::ScopedFeatureList feature_list_;
+
+  ash::CryptohomeMixin cryptohome_mixin_{&mixin_host_};
 };
 
+CertStoreServiceTest::CertStoreServiceTest()
+    : test_cert_data_vector_(std::get<0>(GetParam())) {
+  cryptohome_mixin_.MarkUserAsExisting(affiliation_mixin_.account_id());
+
+  // TODO(crbug.com/1311355): This test is run with the feature
+  // kUseAuthsessionAuthentication enabled and disabled because of a
+  // transitive dependency of AffiliationTestHelper on that feature. Remove
+  // the parameter when kUseAuthsessionAuthentication is removed.
+  if (std::get<1>(GetParam())) {
+    feature_list_.InitAndEnableFeature(
+        ash::features::kUseAuthsessionAuthentication);
+  } else {
+    feature_list_.InitAndDisableFeature(
+        ash::features::kUseAuthsessionAuthentication);
+  }
+}
+
 void CertStoreServiceTest::SetUpCommandLine(base::CommandLine* command_line) {
   MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
   arc::SetArcAvailableCommandLineForTesting(command_line);
@@ -614,10 +638,10 @@
   ASSERT_TRUE(service);
 
   // Install all certs from parameter at once.
-  ASSERT_NO_FATAL_FAILURE(SetUpCerts(GetParam()));
+  ASSERT_NO_FATAL_FAILURE(SetUpCerts(test_cert_data_vector_));
 
   // Verify all certs are installed correctly.
-  ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts(GetParam(), service));
+  ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts(test_cert_data_vector_, service));
 }
 
 IN_PROC_BROWSER_TEST_P(CertStoreServiceTest,
@@ -631,23 +655,24 @@
   ASSERT_TRUE(service);
 
   // Install certs from parameter one by one.
-  for (size_t i = 0; i < GetParam().size(); ++i) {
-    ASSERT_NO_FATAL_FAILURE(SetUpCerts({GetParam()[i]}));
+  for (size_t i = 0; i < test_cert_data_vector_.size(); ++i) {
+    ASSERT_NO_FATAL_FAILURE(SetUpCerts({test_cert_data_vector_[i]}));
 
     // Verify only the first (i+1) certs are installed so far.
-    ASSERT_NO_FATAL_FAILURE(
-        CheckInstalledCerts(std::vector<TestCertData>(
-                                GetParam().begin(), GetParam().begin() + i + 1),
-                            service));
+    ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts(
+        std::vector<TestCertData>(test_cert_data_vector_.begin(),
+                                  test_cert_data_vector_.begin() + i + 1),
+        service));
   }
 
   // Uninstall certs from parameter one by one, from last to first.
-  for (size_t i = GetParam().size(); i--;) {
+  for (size_t i = test_cert_data_vector_.size(); i--;) {
     DeleteCert(installed_certs_.back().nss_cert.get());
 
     // Verify only the first i certs are left after the uninstall.
     ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts(
-        std::vector<TestCertData>(GetParam().begin(), GetParam().begin() + i),
+        std::vector<TestCertData>(test_cert_data_vector_.begin(),
+                                  test_cert_data_vector_.begin() + i),
         service));
   }
 }
@@ -655,44 +680,46 @@
 INSTANTIATE_TEST_SUITE_P(
     CertStoreTests,
     CertStoreServiceTest,
-    ::testing::Values(
-        // No corporate usage keys.
-        std::vector<TestCertData>{
-            TestCertData(kFileName1,
-                         false /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser),
-            TestCertData(kFileName2,
-                         false /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser)},
-        // Corporate usage keys in user slot.
-        std::vector<TestCertData>{
-            TestCertData(kFileName1,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser),
-            TestCertData(kFileName2,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser)},
-        // Corporate usage keys in system slot.
-        std::vector<TestCertData>{
-            TestCertData(kFileName1,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kSystem),
-            TestCertData(kFileName2,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kSystem)},
-        // Corporate usage keys in both slots.
-        std::vector<TestCertData>{
-            TestCertData(kFileName1,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser),
-            TestCertData(kFileName2,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kSystem),
-            TestCertData(kFileName3,
-                         false /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kUser),
-            TestCertData(kFileName4,
-                         true /* is_corporate_usage */,
-                         keymaster::mojom::ChapsSlot::kSystem)}));
+    ::testing::Combine(
+        ::testing::Values(
+            // No corporate usage keys.
+            std::vector<TestCertData>{
+                TestCertData(kFileName1,
+                             false /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser),
+                TestCertData(kFileName2,
+                             false /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser)},
+            // Corporate usage keys in user slot.
+            std::vector<TestCertData>{
+                TestCertData(kFileName1,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser),
+                TestCertData(kFileName2,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser)},
+            // Corporate usage keys in system slot.
+            std::vector<TestCertData>{
+                TestCertData(kFileName1,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kSystem),
+                TestCertData(kFileName2,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kSystem)},
+            // Corporate usage keys in both slots.
+            std::vector<TestCertData>{
+                TestCertData(kFileName1,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser),
+                TestCertData(kFileName2,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kSystem),
+                TestCertData(kFileName3,
+                             false /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kUser),
+                TestCertData(kFileName4,
+                             true /* is_corporate_usage */,
+                             keymaster::mojom::ChapsSlot::kSystem)}),
+        ::testing::Bool()));
 
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc b/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
index 6f71fa9..a1e8a8a 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
@@ -35,9 +35,9 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/system/fake_statistics_provider.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
index d87ca46..04351f8b 100644
--- a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
@@ -28,10 +28,10 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/consent_auditor/fake_consent_auditor.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
index 14f78c4..df01677 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
@@ -54,11 +54,11 @@
 #include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/userdataauth/fake_cryptohome_misc_client.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
index a035d20..a2cc30b 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
+++ b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
@@ -26,7 +26,7 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.h b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.h
index 291e9f73b..05c9a5cf 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.h
+++ b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.h
@@ -11,7 +11,7 @@
 #include "base/cancelable_callback.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/authpolicy/kerberos_files_handler.h"
-#include "chromeos/dbus/authpolicy/active_directory_info.pb.h"
+#include "chromeos/ash/components/dbus/authpolicy/active_directory_info.pb.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "components/account_id/account_id.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
diff --git a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager_unittest.cc b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager_unittest.cc
index aff1a00..1010a02 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager_unittest.cc
+++ b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager_unittest.cc
@@ -15,7 +15,7 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/network/network_handler_test_helper.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/authpolicy/authpolicy_helper.cc b/chrome/browser/ash/authpolicy/authpolicy_helper.cc
index 6593468c..7cc88f43 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_helper.cc
+++ b/chrome/browser/ash/authpolicy/authpolicy_helper.cc
@@ -12,9 +12,9 @@
 #include "base/strings/string_util.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/authpolicy/data_pipe_utils.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/account_id/account_id.h"
 #include "crypto/encryptor.h"
 #include "crypto/hmac.h"
diff --git a/chrome/browser/ash/authpolicy/authpolicy_helper.h b/chrome/browser/ash/authpolicy/authpolicy_helper.h
index 417dfa34..3897bac9 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_helper.h
+++ b/chrome/browser/ash/authpolicy/authpolicy_helper.h
@@ -9,7 +9,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/authpolicy/authpolicy_helper_unittest.cc b/chrome/browser/ash/authpolicy/authpolicy_helper_unittest.cc
index a017e0e8..156ad60 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_helper_unittest.cc
+++ b/chrome/browser/ash/authpolicy/authpolicy_helper_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/components/tpm/stub_install_attributes.h"
 #include "base/bind.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/userdataauth/fake_install_attributes_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer_browsertest.cc b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer_browsertest.cc
index ff37b1b5..8c98031 100644
--- a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer_browsertest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_enforcer_browsertest.cc
@@ -409,7 +409,14 @@
   EXPECT_TRUE(IsErrorPageBeingShownInWebContents(web_contents3));
 }
 
-IN_PROC_BROWSER_TEST_F(WebTimeLimitEnforcerThrottleTest, WebContentTitleSet) {
+// TODO(crbug.com/1313933): Flaky on ChromeOS
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_WebContentTitleSet DISABLED_WebContentTitleSet
+#else
+#define MAYBE_WebContentTitleSet WebContentTitleSet
+#endif
+IN_PROC_BROWSER_TEST_F(WebTimeLimitEnforcerThrottleTest,
+                       MAYBE_WebContentTitleSet) {
   GURL url = embedded_test_server()->GetURL(kExampleHost,
                                             "/supervised_user/simple.html");
   NavigateParams params(browser(), url,
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 57beded7..a779210 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -201,6 +201,7 @@
     "//chrome/common",
     "//chrome/common:channel_info",
     "//chrome/common:constants",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
     "//chromeos/components/sensors",
     "//chromeos/crosapi/cpp",
@@ -211,7 +212,6 @@
     "//chromeos/dbus/resourced",
     "//chromeos/dbus/session_manager",
     "//chromeos/dbus/update_engine",
-    "//chromeos/dbus/upstart",
     "//chromeos/dbus/userdataauth:userdataauth_proto",
     "//chromeos/login/login_state",
     "//chromeos/network",
@@ -330,9 +330,9 @@
     "//chrome/browser/chromeos:test_support",
     "//chrome/common/printing",
     "//chrome/test:test_support",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/crosapi/cpp:cpp",
     "//chromeos/dbus/shill",
-    "//chromeos/dbus/upstart",
     "//chromeos/login/login_state",
     "//chromeos/printing",
     "//chromeos/startup:constants",
diff --git a/chrome/browser/ash/crosapi/browser_loader.h b/chrome/browser/ash/crosapi/browser_loader.h
index 2f69f40..1a9c2322 100644
--- a/chrome/browser/ash/crosapi/browser_loader.h
+++ b/chrome/browser/ash/crosapi/browser_loader.h
@@ -13,7 +13,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/component_updater/cros_component_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "components/component_updater/component_updater_service.h"
 
 namespace crosapi {
diff --git a/chrome/browser/ash/crosapi/browser_loader_unittest.cc b/chrome/browser/ash/crosapi/browser_loader_unittest.cc
index dabd156..1bef35b 100644
--- a/chrome/browser/ash/crosapi/browser_loader_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_loader_unittest.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/fake_cros_component_manager.h"
 #include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "components/component_updater/mock_component_updater_service.h"
 #include "components/update_client/update_client.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/crosapi/sharesheet_ash.cc b/chrome/browser/ash/crosapi/sharesheet_ash.cc
index 26199b88..de972df4 100644
--- a/chrome/browser/ash/crosapi/sharesheet_ash.cc
+++ b/chrome/browser/ash/crosapi/sharesheet_ash.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/sharesheet/sharesheet_service.h"
 #include "chrome/browser/sharesheet/sharesheet_service_factory.h"
 #include "chromeos/crosapi/mojom/sharesheet_mojom_traits.h"
+#include "ui/views/widget/widget.h"
 
 namespace {
 
@@ -20,6 +21,13 @@
   return crosapi::GetShellSurfaceWindow(window_id);
 }
 
+void OnClosedCallbackWrapper(
+    crosapi::SharesheetAsh::ShowBubbleWithOnClosedCallback callback,
+    views::Widget::ClosedReason reason) {
+  if (callback)
+    std::move(callback).Run();
+}
+
 }  // namespace
 
 namespace crosapi {
@@ -46,7 +54,8 @@
                                crosapi::mojom::IntentPtr intent,
                                ShowBubbleCallback callback) {
   DCHECK(profile_);
-  DCHECK_NE(source, sharesheet::LaunchSource::kUnknown);
+  if (source == sharesheet::LaunchSource::kUnknown)
+    return;
 
   sharesheet::SharesheetService* const sharesheet_service =
       sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
@@ -56,4 +65,37 @@
       base::BindOnce(&GetNativeWindowFromId, window_id), std::move(callback));
 }
 
+void SharesheetAsh::ShowBubbleWithOnClosed(
+    const std::string& window_id,
+    sharesheet::LaunchSource source,
+    crosapi::mojom::IntentPtr intent,
+    ShowBubbleWithOnClosedCallback callback) {
+  DCHECK(profile_);
+  if (source == sharesheet::LaunchSource::kUnknown)
+    return;
+
+  sharesheet::SharesheetService* const sharesheet_service =
+      sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
+  sharesheet_service->ShowBubble(
+      apps_util::ConvertCrosapiToAppServiceIntent(intent, profile_),
+      /*contains_hosted_document=*/false, source,
+      base::BindOnce(&GetNativeWindowFromId, window_id), base::NullCallback(),
+      base::BindOnce(&OnClosedCallbackWrapper, std::move(callback)));
+}
+
+void SharesheetAsh::CloseBubble(const std::string& window_id) {
+  gfx::NativeWindow window = crosapi::GetShellSurfaceWindow(window_id);
+  if (!window)
+    return;
+
+  sharesheet::SharesheetService* const sharesheet_service =
+      sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
+  sharesheet::SharesheetController* sharesheet_controller =
+      sharesheet_service->GetSharesheetController(window);
+  if (!sharesheet_controller)
+    return;
+
+  sharesheet_controller->CloseBubble(sharesheet::SharesheetResult::kCancel);
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/sharesheet_ash.h b/chrome/browser/ash/crosapi/sharesheet_ash.h
index 24dc86b5..a2e46a47 100644
--- a/chrome/browser/ash/crosapi/sharesheet_ash.h
+++ b/chrome/browser/ash/crosapi/sharesheet_ash.h
@@ -35,6 +35,11 @@
                   sharesheet::LaunchSource source,
                   crosapi::mojom::IntentPtr intent,
                   ShowBubbleCallback callback) override;
+  void ShowBubbleWithOnClosed(const std::string& window_id,
+                              sharesheet::LaunchSource source,
+                              crosapi::mojom::IntentPtr intent,
+                              ShowBubbleWithOnClosedCallback callback) override;
+  void CloseBubble(const std::string& window_id) override;
 
  private:
   Profile* profile_ = nullptr;
diff --git a/chrome/browser/ash/crosapi/sharesheet_ash_browsertest.cc b/chrome/browser/ash/crosapi/sharesheet_ash_browsertest.cc
index f2e84d5..3489444a 100644
--- a/chrome/browser/ash/crosapi/sharesheet_ash_browsertest.cc
+++ b/chrome/browser/ash/crosapi/sharesheet_ash_browsertest.cc
@@ -115,6 +115,7 @@
   SharesheetAshBrowserTest() = default;
   ~SharesheetAshBrowserTest() override = default;
 
+  // SystemWebAppIntegrationTest:
   void SetUpOnMainThread() override {
     SystemWebAppIntegrationTest::SetUpOnMainThread();
     WaitForTestSystemAppInstall();
@@ -124,6 +125,9 @@
     sharesheet::SharesheetService::SetSelectedAppForTesting(
         base::UTF8ToUTF16(web_app::kSampleSystemWebAppId));
   }
+  void TearDownOnMainThread() override {
+    sharesheet::SharesheetService::SetSelectedAppForTesting(std::u16string());
+  }
 };
 
 IN_PROC_BROWSER_TEST_P(SharesheetAshBrowserTest, Success) {
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
index 832ec31..cf181b2b 100644
--- a/chrome/browser/ash/dbus/ash_dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -14,13 +14,14 @@
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/common/chrome_paths.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/components/chromebox_for_meetings/buildflags/buildflags.h"  // PLATFORM_CFM
 #include "chromeos/components/hibernate/buildflags.h"  // ENABLE_HIBERNATE
 #include "chromeos/dbus/arc/arc_camera_client.h"
 #include "chromeos/dbus/arc/arc_sensor_service_client.h"
 #include "chromeos/dbus/attestation/attestation_client.h"
 #include "chromeos/dbus/audio/cras_audio_client.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
 #include "chromeos/dbus/biod/biod_client.h"
 #include "chromeos/dbus/cdm_factory_daemon/cdm_factory_daemon_client.h"
 #include "chromeos/dbus/cicerone/cicerone_client.h"
@@ -56,7 +57,6 @@
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
 #include "chromeos/dbus/typecd/typecd_client.h"
 #include "chromeos/dbus/u2f/u2f_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/userdataauth/arc_quota_client.h"
 #include "chromeos/dbus/userdataauth/cryptohome_misc_client.h"
 #include "chromeos/dbus/userdataauth/cryptohome_pkcs11_client.h"
diff --git a/chrome/browser/ash/login/active_directory_login_browsertest.cc b/chrome/browser/ash/login/active_directory_login_browsertest.cc
index b3736fa..17826226 100644
--- a/chrome/browser/ash/login/active_directory_login_browsertest.cc
+++ b/chrome/browser/ash/login/active_directory_login_browsertest.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_fatal_error_screen_handler.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "components/user_manager/user_names.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/network_service_util.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_view.h b/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
index 610dc82..334e19b 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
@@ -9,7 +9,7 @@
 
 #include "chrome/browser/ash/login/enrollment/enterprise_enrollment_helper.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chromeos/dbus/authpolicy/active_directory_info.pb.h"
+#include "chromeos/ash/components/dbus/authpolicy/active_directory_info.pb.h"
 
 class GoogleServiceAuthError;
 
diff --git a/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc b/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
index af67237..0e54cb4 100644
--- a/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
+++ b/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
@@ -24,10 +24,10 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/enrollment_status.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/ash/login/existing_user_controller_browsertest.cc b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
index 62a69d6..5290665 100644
--- a/chrome/browser/ash/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
@@ -7,6 +7,7 @@
 
 #include "ash/components/arc/enterprise/arc_data_snapshotd_manager.h"
 #include "ash/components/arc/test/arc_util_test_support.h"
+#include "ash/components/cryptohome/cryptohome_parameters.h"
 #include "ash/components/login/auth/key.h"
 #include "ash/components/login/auth/stub_authenticator_builder.h"
 #include "ash/components/login/auth/user_context.h"
@@ -70,7 +71,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/dbus/userdataauth/fake_userdataauth_client.h"
@@ -1010,6 +1011,36 @@
   testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
 };
 
+class ExistingUserControllerActiveDirectoryTestCreateProfileDir
+    : public ExistingUserControllerActiveDirectoryTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  ExistingUserControllerActiveDirectoryTestCreateProfileDir() {
+    cryptohome_mixin_.MarkUserAsExisting(ad_account_id_);
+
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kUseAuthsessionAuthentication);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          features::kUseAuthsessionAuthentication);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  CryptohomeMixin cryptohome_mixin_{&mixin_host_};
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ActiveDirectory,
+    ExistingUserControllerActiveDirectoryTestCreateProfileDir,
+    ::testing::Bool());
+
 class ExistingUserControllerActiveDirectoryUserAllowlistTest
     : public ExistingUserControllerActiveDirectoryTest {
  public:
@@ -1074,8 +1105,9 @@
 
 // Tests that Active Directory offline login succeeds on the Active Directory
 // managed device.
-IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
-                       ActiveDirectoryOfflineLogin_Success) {
+IN_PROC_BROWSER_TEST_P(
+    ExistingUserControllerActiveDirectoryTestCreateProfileDir,
+    ActiveDirectoryOfflineLogin_Success) {
   ExpectLoginSuccess();
   UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
                            ad_account_id_);
diff --git a/chrome/browser/ash/login/saml/security_token_saml_test.cc b/chrome/browser/ash/login/saml/security_token_saml_test.cc
index fd2fdff..6bd14ad 100644
--- a/chrome/browser/ash/login/saml/security_token_saml_test.cc
+++ b/chrome/browser/ash/login/saml/security_token_saml_test.cc
@@ -8,6 +8,7 @@
 #include <iterator>
 #include <string>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -76,6 +77,10 @@
     : saml_idp_mixin_(&mixin_host_,
                       &gaia_mixin_,
                       /*client_cert_authorities=*/{GetClientCertCaName()}) {
+  // TODO(crbug.com/1274116): Remove eventually after support for auth session
+  // empty passwords
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kUseAuthsessionAuthentication);
   // Allow the forced installation of extensions in the background.
   needs_background_networking_ = true;
 
diff --git a/chrome/browser/ash/login/saml/security_token_saml_test.h b/chrome/browser/ash/login/saml/security_token_saml_test.h
index 4fa59ff..18fb63f 100644
--- a/chrome/browser/ash/login/saml/security_token_saml_test.h
+++ b/chrome/browser/ash/login/saml/security_token_saml_test.h
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/ash/certificate_provider/test_certificate_provider_extension.h"
 #include "chrome/browser/ash/certificate_provider/test_certificate_provider_extension_mixin.h"
@@ -121,6 +122,7 @@
           &mixin_host_, &extension_force_install_mixin_};
   int pin_dialog_shown_count_ = 0;
   base::RunLoop* pin_dialog_shown_run_loop_ = nullptr;
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::WeakPtrFactory<SecurityTokenSamlTest> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ash/login/test/active_directory_login_mixin.cc b/chrome/browser/ash/login/test/active_directory_login_mixin.cc
index d208546..b009baf 100644
--- a/chrome/browser/ash/login/test/active_directory_login_mixin.cc
+++ b/chrome/browser/ash/login/test/active_directory_login_mixin.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "content/public/test/browser_test_utils.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/test/cryptohome_mixin.cc b/chrome/browser/ash/login/test/cryptohome_mixin.cc
index 34deeca0..92a2b47 100644
--- a/chrome/browser/ash/login/test/cryptohome_mixin.cc
+++ b/chrome/browser/ash/login/test/cryptohome_mixin.cc
@@ -29,8 +29,9 @@
 
 void CryptohomeMixin::SetUpOnMainThread() {
   while (!pending_users_.empty()) {
-    chromeos::FakeUserDataAuthClient::Get()->AddExistingUser(
-        pending_users_.front());
+    auto user = pending_users_.front();
+    chromeos::FakeUserDataAuthClient::Get()->AddExistingUser(user);
+    chromeos::FakeUserDataAuthClient::Get()->CreateUserProfileDir(user);
     pending_users_.pop();
   }
 }
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 3b553396..dc400ff 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -84,12 +84,12 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/components/onc/certificate_scope.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 #include "chromeos/dbus/cryptohome/UserDataAuth.pb.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/proxy/proxy_config_service_impl.h"
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager_unittest.cc b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager_unittest.cc
index d4c68102..f536ce35 100644
--- a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager_unittest.cc
+++ b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager_unittest.cc
@@ -16,7 +16,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_mixin.cc b/chrome/browser/ash/policy/affiliation/affiliation_mixin.cc
index ac38726..5e20dfc 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_mixin.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_mixin.cc
@@ -9,8 +9,8 @@
 
 #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
index 4a152ea..9f1a16a 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/chrome_paths.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/constants/dbus_paths.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc b/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
index b669f80..00ad08c5 100644
--- a/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
+++ b/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
@@ -21,9 +21,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
diff --git a/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc b/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
index 832fcdb..342e0a7 100644
--- a/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
+++ b/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
@@ -6,12 +6,14 @@
 
 #include "ash/components/arc/test/arc_util_test_support.h"
 #include "ash/components/settings/cros_settings_names.h"
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
+#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
@@ -39,11 +41,24 @@
 
 class UnaffiliatedArcAllowedTest
     : public DevicePolicyCrosBrowserTest,
-      public ::testing::WithParamInterface<Params> {
+      public ::testing::WithParamInterface<std::tuple<Params, bool>> {
  public:
   UnaffiliatedArcAllowedTest() {
     set_exit_when_last_browser_closes(false);
-    affiliation_mixin_.set_affiliated(GetParam().affiliated);
+    affiliation_mixin_.set_affiliated(std::get<0>(GetParam()).affiliated);
+    cryptohome_mixin_.MarkUserAsExisting(affiliation_mixin_.account_id());
+
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (std::get<1>(GetParam())) {
+      feature_list_.InitAndEnableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    }
   }
 
   UnaffiliatedArcAllowedTest(const UnaffiliatedArcAllowedTest&) = delete;
@@ -86,6 +101,10 @@
   }
 
   AffiliationMixin affiliation_mixin_{&mixin_host_, policy_helper()};
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  ash::CryptohomeMixin cryptohome_mixin_{&mixin_host_};
 };
 
 IN_PROC_BROWSER_TEST_P(UnaffiliatedArcAllowedTest, PRE_ProfileTest) {
@@ -97,7 +116,7 @@
   const user_manager::User* user = user_manager::UserManager::Get()->FindUser(
       affiliation_mixin_.account_id());
   const Profile* profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
-  const bool affiliated = GetParam().affiliated;
+  const bool affiliated = std::get<0>(GetParam()).affiliated;
 
   EXPECT_EQ(affiliated, user->IsAffiliated());
   EXPECT_TRUE(arc::IsArcAllowedForProfile(profile))
@@ -120,5 +139,7 @@
 
 INSTANTIATE_TEST_SUITE_P(Blub,
                          UnaffiliatedArcAllowedTest,
-                         ::testing::Values(Params(true), Params(false)));
+                         ::testing::Combine(::testing::Values(Params(true),
+                                                              Params(false)),
+                                            ::testing::Bool()));
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
index 67f6c16..abefd3d5 100644
--- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
+++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
@@ -73,9 +73,9 @@
 #include "chrome/browser/policy/networking/device_network_configuration_updater_ash.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "chromeos/network/network_cert_loader.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/onc/onc_certificate_importer_impl.h"
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
index f8b36d9..db1deca5 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
@@ -33,10 +33,10 @@
 #include "chrome/browser/policy/enrollment_status.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
 #include "components/policy/core/common/cloud/signing_service.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.h b/chrome/browser/ash/policy/enrollment/enrollment_handler.h
index 6a1951d..8e1b663a 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_handler.h
+++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.h
@@ -16,7 +16,7 @@
 #include "chrome/browser/ash/policy/core/device_cloud_policy_validator.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
 #include "chrome/browser/policy/device_account_initializer.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
 #include "chromeos/dbus/constants/attestation_constants.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
diff --git a/chrome/browser/ash/policy/status_collector/managed_session_service.h b/chrome/browser/ash/policy/status_collector/managed_session_service.h
index aadcf7f..81b9e3b5 100644
--- a/chrome/browser/ash/policy/status_collector/managed_session_service.h
+++ b/chrome/browser/ash/policy/status_collector/managed_session_service.h
@@ -98,6 +98,10 @@
   // chromeos::PowerManagerClient::Observer
   void SuspendDone(base::TimeDelta sleep_duration) override;
 
+  void OnPasswordChangeDetected(const ash::UserContext& user_context) override {
+  }
+  void OnOldEncryptionDetected(const ash::UserContext& user_context,
+                               bool has_incomplete_migration) override {}
   void OnAuthSuccess(const ash::UserContext& user_context) override {}
 
   void OnAuthFailure(const ash::AuthFailure& error) override;
diff --git a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.cc b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.cc
index f05e06e..358aad9 100644
--- a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.cc
+++ b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.cc
@@ -12,6 +12,7 @@
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_util.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
 #include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/engine/nigori/nigori.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -20,29 +21,6 @@
 
 namespace {
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojo(const syncer::Nigori& nigori) {
-  std::string deprecated_user_key;
-  std::string encryption_key;
-  std::string mac_key;
-  nigori.ExportKeys(&deprecated_user_key, &encryption_key, &mac_key);
-
-  crosapi::mojom::NigoriKeyPtr mojo_result = crosapi::mojom::NigoriKey::New();
-  mojo_result->encryption_key =
-      std::vector<uint8_t>(encryption_key.begin(), encryption_key.end());
-  mojo_result->mac_key = std::vector<uint8_t>(mac_key.begin(), mac_key.end());
-  return mojo_result;
-}
-
-std::unique_ptr<syncer::Nigori> NigoriFromMojo(
-    const crosapi::mojom::NigoriKey& mojo_key) {
-  const std::string encryption_key(mojo_key.encryption_key.begin(),
-                                   mojo_key.encryption_key.end());
-  const std::string mac_key(mojo_key.mac_key.begin(), mojo_key.mac_key.end());
-  // |user_key| is deprecated, it's safe to pass empty string.
-  return syncer::Nigori::CreateByImport(
-      /*user_key=*/std::string(), encryption_key, mac_key);
-}
-
 bool IsPassphraseAvailable(const syncer::SyncService& sync_service) {
   return sync_service.GetUserSettings()->IsUsingExplicitPassphrase() &&
          !sync_service.GetUserSettings()->IsPassphraseRequired();
@@ -50,11 +28,6 @@
 
 }  // namespace
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojoForTesting(  // IN-TEST
-    const syncer::Nigori& nigori) {
-  return NigoriToMojo(nigori);
-}
-
 SyncExplicitPassphraseClientAsh::SyncExplicitPassphraseClientAsh(
     syncer::SyncService* sync_service)
     : sync_service_(sync_service),
@@ -104,7 +77,7 @@
     return;
   }
 
-  std::move(callback).Run(NigoriToMojo(*decryption_key));
+  std::move(callback).Run(syncer::NigoriToMojo(*decryption_key));
 }
 
 void SyncExplicitPassphraseClientAsh::SetDecryptionNigoriKey(
@@ -114,7 +87,8 @@
     return;
   }
 
-  std::unique_ptr<syncer::Nigori> nigori_key = NigoriFromMojo(*mojo_nigori_key);
+  std::unique_ptr<syncer::Nigori> nigori_key =
+      syncer::NigoriFromMojo(*mojo_nigori_key);
   if (!nigori_key) {
     // Deserialization failed, |mojo_nigori_key| doesn't represent an actual
     // Nigori key.
diff --git a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.h b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.h
index 168e722..746a613 100644
--- a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.h
+++ b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.h
@@ -14,10 +14,6 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 
-namespace syncer {
-class Nigori;
-}
-
 namespace ash {
 
 class SyncExplicitPassphraseClientAsh
@@ -69,9 +65,6 @@
       observers_;
 };
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojoForTesting(
-    const syncer::Nigori& nigori);
-
 }  // namespace ash
 
 #endif  // CHROME_BROWSER_ASH_SYNC_SYNC_EXPLICIT_PASSPHRASE_CLIENT_ASH_H_
diff --git a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash_unittest.cc b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash_unittest.cc
index 7c3ae48..bd4708d8 100644
--- a/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash_unittest.cc
+++ b/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/task_environment.h"
 #include "chromeos/crosapi/mojom/sync.mojom-test-utils.h"
+#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
 #include "components/sync/driver/mock_sync_service.h"
 #include "components/sync/driver/sync_user_settings_mock.h"
 #include "components/sync/engine/nigori/key_derivation_params.h"
@@ -28,7 +29,7 @@
 
 crosapi::mojom::NigoriKeyPtr MakeTestMojoNigoriKey() {
   std::unique_ptr<syncer::Nigori> nigori_key = MakeTestNigoriKey();
-  return NigoriToMojoForTesting(*nigori_key);
+  return syncer::NigoriToMojo(*nigori_key);
 }
 
 class TestSyncExplicitPassphraseClientObserver
diff --git a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager.cc b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager.cc
index 9fb84c8..6cc62ca 100644
--- a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager.cc
+++ b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_network_context.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
 #include "components/user_manager/user.h"
diff --git a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
index 1ad8c869..a8d26ec4 100644
--- a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
+++ b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_network_context.h"
 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
 #include "components/user_manager/scoped_user_manager.h"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 60bd2c6..5e6280a 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -232,6 +232,9 @@
     "//chrome/services/wilco_dtc_supportd/public/mojom",
     "//chromeos",
     "//chromeos:chromeos_export",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/assistant:buildflags",
     "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
     "//chromeos/components/chromebox_for_meetings/buildflags",
@@ -259,8 +262,6 @@
     "//chromeos/dbus/attestation",
     "//chromeos/dbus/attestation:attestation_proto",
     "//chromeos/dbus/audio",
-    "//chromeos/dbus/authpolicy",
-    "//chromeos/dbus/authpolicy:authpolicy_proto",
     "//chromeos/dbus/biod",
     "//chromeos/dbus/cdm_factory_daemon",
     "//chromeos/dbus/chunneld",
@@ -318,7 +319,6 @@
     "//chromeos/dbus/u2f:u2f_proto",
     "//chromeos/dbus/update_engine",
     "//chromeos/dbus/update_engine:proto",
-    "//chromeos/dbus/upstart",
     "//chromeos/dbus/userdataauth",
     "//chromeos/dbus/userdataauth:userdataauth_proto",
     "//chromeos/dbus/util",
@@ -4936,12 +4936,13 @@
     "//chrome/test:test_support_ui",
     "//chrome/test:test_support_unit",
     "//chromeos",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/upstart",
     "//chromeos/components/sensors:test_support",
     "//chromeos/dbus:test_support",
     "//chromeos/dbus/anomaly_detector",
     "//chromeos/dbus/attestation",
     "//chromeos/dbus/attestation:attestation_proto",
-    "//chromeos/dbus/authpolicy",
     "//chromeos/dbus/cros_disks",
     "//chromeos/dbus/cryptohome",
     "//chromeos/dbus/cryptohome:attestation_proto",
@@ -4957,7 +4958,6 @@
     "//chromeos/dbus/smbprovider:proto",
     "//chromeos/dbus/system_clock",
     "//chromeos/dbus/update_engine",
-    "//chromeos/dbus/upstart",
     "//chromeos/dbus/virtual_file_provider",
     "//chromeos/dbus/vm_plugin_dispatcher",
     "//chromeos/ime:gencode",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc
index a4e9bf1f..3e4934f 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc
@@ -2,12 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/mixin_based_extension_apitest.h"
 #include "chrome/browser/policy/extension_force_install_mixin.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
@@ -15,7 +13,6 @@
 #include "content/public/test/test_launcher.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -62,14 +59,6 @@
     listener_.reset();
   }
 
-  void SetUp() override {
-    base::FilePath test_data_dir;
-    base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
-    embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
-
-    extensions::MixinBasedExtensionApiTest::SetUp();
-  }
-
   void SetUpInProcessBrowserTestFixture() override {
     extensions::MixinBasedExtensionApiTest::SetUpInProcessBrowserTestFixture();
 
@@ -82,39 +71,28 @@
   }
 
   void SetUpOnMainThread() override {
-    ASSERT_TRUE(embedded_test_server()->Start());
-
-    policy::ProfilePolicyConnector* const connector =
-        profile()->GetProfilePolicyConnector();
-    connector->OverrideIsManagedForTesting(true);
-
     extension_force_install_mixin_.InitWithMockPolicyProvider(
         profile(), &mock_policy_provider_);
 
     extensions::MixinBasedExtensionApiTest::SetUpOnMainThread();
   }
 
-  void TearDownOnMainThread() override {
-    extensions::MixinBasedExtensionApiTest::TearDownOnMainThread();
-
-    EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
-  }
-
   void RunTest(const std::string& test_name) {
     catcher_ = std::make_unique<extensions::ResultCatcher>();
     listener_ =
         std::make_unique<ExtensionTestMessageListener>(kListenerMessage,
                                                        /* will_reply= */ true);
 
-    base::FilePath extension_dir =
-        base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
-            .Append(FILE_PATH_LITERAL(kInSessionExtensionCrxPath));
-
     extensions::ExtensionId extension_id;
-    ASSERT_TRUE(extension_force_install_mixin_.ForceInstallFromCrx(
-        extension_dir, ExtensionForceInstallMixin::WaitMode::kLoad,
-        &extension_id));
-    ASSERT_EQ(kInSessionExtensionId, extension_id);
+    EXPECT_TRUE(extension_force_install_mixin_.ForceInstallFromCrx(
+        base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+            .Append(FILE_PATH_LITERAL(kInSessionExtensionCrxPath)),
+        ExtensionForceInstallMixin::WaitMode::kLoad, &extension_id));
+
+    const extensions::Extension* extension =
+        extension_force_install_mixin_.GetEnabledExtension(extension_id);
+    ASSERT_TRUE(extension);
+    ASSERT_EQ(extension_id, kInSessionExtensionId);
 
     ASSERT_TRUE(listener_->WaitUntilSatisfied());
     listener_->Reply(test_name);
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java
index d502b5bd..cef5c41 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java
@@ -208,7 +208,7 @@
             if (!action.hasActionId() || !action.hasText()) continue;
             String actionText = getActionText(action.getActionId());
             if (TextUtils.isEmpty(actionText)) continue;
-            actions.add(new ActionData(action.getActionId(), action.getText()));
+            actions.add(new ActionData(action.getActionId(), actionText));
         }
         return actions;
     }
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java
index ffd286e..5327c35d 100644
--- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java
+++ b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java
@@ -55,7 +55,7 @@
     private static final String ACTION_ID_0 = "visit_site";
     private static final String ACTION_ID_1 = "turn_off_alert";
     private static final String ACTION_TEXT_0 = "Visit site";
-    private static final String ACTION_TEXT_1 = "Turn off alert";
+    private static final String ACTION_TEXT_1 = "Stop tracking product";
 
     private PriceTrackingNotificationBridge mPriceTrackingNotificationBridge;
 
diff --git a/chrome/browser/dev_ui_browser_resources.grd b/chrome/browser/dev_ui_browser_resources.grd
index 6a71c22..9b48f4a 100644
--- a/chrome/browser/dev_ui_browser_resources.grd
+++ b/chrome/browser/dev_ui_browser_resources.grd
@@ -47,6 +47,7 @@
       <include name="IDR_PREDICTORS_HTML" file="resources\predictors\predictors.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_PREDICTORS_JS" file="resources\predictors\predictors.js" type="BINDATA" />
       <include name="IDR_PREDICTORS_RESOURCE_PREFETCH_PREDICTOR_JS" file="resources\predictors\resource_prefetch_predictor.js" type="BINDATA" />
+      <include name="IDR_PROFILE_INTERNALS_HTML" file="resources\profiles\profile_internals.html" type="BINDATA" />
       <include name="IDR_MEDIA_SESSION_MOJOM_WEBUI_JS" file="${root_gen_dir}\mojom-webui\services\media_session\public\mojom\media_session.mojom-webui.js" use_base_dir="false" type="BINDATA" />
 
       <if expr="is_android or is_linux">
diff --git a/chrome/browser/download/bubble/download_bubble_controller.cc b/chrome/browser/download/bubble/download_bubble_controller.cc
index 3d8dc3c..9a1a6d7 100644
--- a/chrome/browser/download/bubble/download_bubble_controller.cc
+++ b/chrome/browser/download/bubble/download_bubble_controller.cc
@@ -37,19 +37,31 @@
 }
 
 using DownloadUIModelPtrList = std::list<DownloadUIModelPtr>;
+
+// Sorting order is 1) Active in-progress downloads, 2) Paused in-progress
+// downloads, 3) Other downloads
+int GetSortOrder(DownloadUIModel* a) {
+  if (a->GetState() == download::DownloadItem::IN_PROGRESS) {
+    return a->IsPaused() ? 2 : 1;
+  }
+  return 3;
+}
+
 struct StartTimeComparator {
   bool operator()(const DownloadUIModelPtrList::iterator& a_iter,
                   const DownloadUIModelPtrList::iterator& b_iter) const {
     DownloadUIModel* a = (*a_iter).get();
     DownloadUIModel* b = (*b_iter).get();
-    // Offline items have start time not populated, so display only not finished
-    // ones.
-    bool is_a_active_offline = (a->GetStartTime().is_null() && !a->IsDone());
-    bool is_b_active_offline = (b->GetStartTime().is_null() && !b->IsDone());
-    // a definitely shown before b if, 1) b is not an active offline item, and
-    // 2) Either a is active offline item, or a is more recent
-    return (!is_b_active_offline &&
-            (is_a_active_offline || (a->GetStartTime() > b->GetStartTime())));
+    int a_sort_order = GetSortOrder(a);
+    int b_sort_order = GetSortOrder(b);
+    if (a_sort_order < b_sort_order) {
+      return true;
+    } else if (a_sort_order > b_sort_order) {
+      return false;
+    } else {
+      // For the same sort order, sub-order by reverse chronological order.
+      return (a->GetStartTime() > b->GetStartTime());
+    }
   }
 };
 using SortedDownloadUIModelSet =
diff --git a/chrome/browser/download/bubble/download_bubble_controller_unittest.cc b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
index 89866e1c..f89d0935 100644
--- a/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
+++ b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
@@ -46,6 +46,22 @@
   MOCK_METHOD0(OnRemovedItem, void());
 };
 
+struct DownloadSortingState {
+  std::string id;
+  base::TimeDelta offset;
+  DownloadState state;
+  bool is_paused;
+  DownloadSortingState(const std::string& id,
+                       base::TimeDelta offset,
+                       DownloadState state,
+                       bool is_paused) {
+    this->id = id;
+    this->offset = offset;
+    this->state = state;
+    this->is_paused = is_paused;
+  }
+};
+
 }  // namespace
 
 class DownloadBubbleUIControllerTest : public testing::Test {
@@ -126,6 +142,7 @@
     EXPECT_CALL(item(index), GetDownloadCreationType())
         .WillRepeatedly(Return(download::DownloadItem::DownloadCreationType::
                                    TYPE_ACTIVE_DOWNLOAD));
+    EXPECT_CALL(item(index), IsPaused()).WillRepeatedly(Return(false));
     std::vector<download::DownloadItem*> items;
     for (size_t i = 0; i < items_.size(); ++i) {
       items.push_back(&item(i));
@@ -138,9 +155,10 @@
     controller().OnDownloadCreated(&manager(), &item(index));
   }
 
-  void UpdateDownloadItem(int item_index, DownloadState state) {
+  void UpdateDownloadItem(int item_index,
+                          DownloadState state,
+                          bool is_paused = false) {
     DCHECK_GT(items_.size(), static_cast<size_t>(item_index));
-
     EXPECT_CALL(item(item_index), GetState()).WillRepeatedly(Return(state));
     if (state == DownloadState::COMPLETE) {
       EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(true));
@@ -149,6 +167,7 @@
     } else {
       EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false));
     }
+    EXPECT_CALL(item(item_index), IsPaused()).WillRepeatedly(Return(is_paused));
     item(item_index).NotifyObserversDownloadUpdated();
   }
 
@@ -225,25 +244,38 @@
 }
 
 TEST_F(DownloadBubbleUIControllerTest, ListIsSorted) {
-  std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3",
-                                  "Offline 1"};
-  std::vector<base::TimeDelta> start_time_offsets = {
-      base::Hours(1), base::Hours(4), base::Hours(2)};
-  std::vector<std::string> sorted_ids = {"Offline 1", "Download 1",
-                                         "Download 3", "Download 2"};
+  std::vector<DownloadSortingState> sort_states = {
+      DownloadSortingState("Download 1", base::Hours(2),
+                           DownloadState::IN_PROGRESS, /*is_paused=*/false),
+      DownloadSortingState("Download 2", base::Hours(4),
+                           DownloadState::IN_PROGRESS, /*is_paused=*/true),
+      DownloadSortingState("Download 3", base::Hours(3),
+                           DownloadState::COMPLETE, /*is_paused=*/false),
+      DownloadSortingState("Download 4", base::Hours(0),
+                           DownloadState::IN_PROGRESS, /*is_paused=*/false),
+      DownloadSortingState("Download 5", base::Hours(1),
+                           DownloadState::COMPLETE, /*is_paused=*/false)};
+
+  // Offline item will be in-progress. Non in-progress offline items do not
+  // surface.
+  std::string offline_item = "Offline 1";
+  // First non-paused in-progress, then paused in-progress, then completed,
+  // sub-sorted by starting times.
+  std::vector<std::string> sorted_ids = {"Download 4", "Download 1",
+                                         "Offline 1",  "Download 2",
+                                         "Download 5", "Download 3"};
   base::Time now = base::Time::Now();
-  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
-                   download::DownloadItem::IN_PROGRESS, ids[0],
-                   /*is_transient=*/false, now - start_time_offsets[0]);
-  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
-                   download::DownloadItem::IN_PROGRESS, ids[1],
-                   /*is_transient=*/false, now - start_time_offsets[1]);
-  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"),
-                   download::DownloadItem::IN_PROGRESS, ids[2],
-                   /*is_transient=*/false, now - start_time_offsets[2]);
-  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]);
+  for (unsigned long i = 0; i < sort_states.size(); i++) {
+    InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                     DownloadState::IN_PROGRESS, sort_states[i].id,
+                     /*is_transient=*/false, now - sort_states[i].offset);
+    UpdateDownloadItem(/*item_index=*/i, sort_states[i].state,
+                       sort_states[i].is_paused);
+  }
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, offline_item);
+
   std::vector<DownloadUIModelPtr> models = controller().GetMainView();
-  EXPECT_EQ(models.size(), 4ul);
+  EXPECT_EQ(models.size(), sorted_ids.size());
   for (unsigned long i = 0; i < models.size(); i++) {
     EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]);
   }
@@ -254,8 +286,8 @@
                                   "Offline 1"};
   std::vector<base::TimeDelta> start_time_offsets = {
       base::Hours(1), base::Hours(25), base::Hours(2)};
-  std::vector<std::string> sorted_ids = {"Offline 1", "Download 1",
-                                         "Download 3"};
+  std::vector<std::string> sorted_ids = {"Download 1", "Download 3",
+                                         "Offline 1"};
   base::Time now = base::Time::Now();
   InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
                    download::DownloadItem::IN_PROGRESS, ids[0],
@@ -268,7 +300,7 @@
                    /*is_transient=*/false, now - start_time_offsets[2]);
   InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]);
   std::vector<DownloadUIModelPtr> models = controller().GetMainView();
-  EXPECT_EQ(models.size(), 3ul);
+  EXPECT_EQ(models.size(), sorted_ids.size());
   for (unsigned long i = 0; i < models.size(); i++) {
     EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]);
   }
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc
index d59fa8b..33af9a3 100644
--- a/chrome/browser/download/bubble/download_display_controller.cc
+++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/download/bubble/download_icon_state.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/ui/download/download_item_mode.h"
 #include "components/offline_items_collection/core/offline_item.h"
 #include "components/offline_items_collection/core/offline_item_state.h"
 
@@ -101,7 +102,6 @@
     ShowToolbarButton();
     icon_info_.icon_state = DownloadIconState::kProgress;
     icon_info_.is_active = true;
-    display_->UpdateDownloadIcon();
   } else {
     icon_info_.icon_state = DownloadIconState::kComplete;
     if (HasRecentCompleteDownload(kToolbarIconActiveTimeInterval,
@@ -111,8 +111,22 @@
     } else {
       icon_info_.is_active = false;
     }
-    display_->UpdateDownloadIcon();
   }
+
+  // Check for deep scanning state. Only check for downloads because deep
+  // scanning is not supported for offline items.
+  content::DownloadManager::DownloadVector items;
+  download_manager_->GetAllDownloads(&items);
+  for (auto* item : items) {
+    if (item->GetDangerType() ==
+            download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING &&
+        item->GetState() != download::DownloadItem::CANCELLED) {
+      icon_info_.icon_state = DownloadIconState::kDeepScanning;
+      break;
+    }
+  }
+
+  display_->UpdateDownloadIcon();
 }
 
 void DownloadDisplayController::UpdateDownloadIconToInactive() {
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc
index e4e3545..b369ae53 100644
--- a/chrome/browser/download/bubble/download_display_controller_unittest.cc
+++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/mock_download_item.h"
 #include "components/offline_items_collection/core/offline_item.h"
 #include "content/public/test/browser_task_environment.h"
@@ -153,6 +154,8 @@
     EXPECT_CALL(item(index), GetId())
         .WillRepeatedly(Return(static_cast<uint32_t>(items_.size() + 1)));
     EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state));
+    EXPECT_CALL(item(index), GetDangerType())
+        .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
     int received_bytes =
         state == download::DownloadItem::IN_PROGRESS ? 50 : 100;
     EXPECT_CALL(item(index), GetReceivedBytes())
@@ -189,10 +192,15 @@
     controller().OnUpdatedItem(state == OfflineItemState::COMPLETE);
   }
 
-  void UpdateDownloadItem(int item_index, DownloadState state) {
+  void UpdateDownloadItem(int item_index,
+                          DownloadState state,
+                          download::DownloadDangerType danger_type =
+                              download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
     DCHECK_GT(items_.size(), static_cast<size_t>(item_index));
 
     EXPECT_CALL(item(item_index), GetState()).WillRepeatedly(Return(state));
+    EXPECT_CALL(item(item_index), GetDangerType())
+        .WillRepeatedly(Return(danger_type));
     if (state == DownloadState::COMPLETE) {
       EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(true));
       in_progress_count_--;
@@ -394,6 +402,30 @@
                                  /*is_active=*/false));
 }
 
+TEST_F(DownloadDisplayControllerTest, UpdateToolbarButtonState_DeepScanning) {
+  EXPECT_TRUE(VerifyDisplayState(/*shown=*/false, /*detail_shown=*/false,
+                                 /*icon_state=*/DownloadIconState::kComplete,
+                                 /*is_active=*/false));
+
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS);
+  EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true,
+                                 /*icon_state=*/DownloadIconState::kProgress,
+                                 /*is_active=*/true));
+
+  UpdateDownloadItem(/*item_index=*/0, DownloadState::IN_PROGRESS,
+                     download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING);
+  EXPECT_TRUE(
+      VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true,
+                         /*icon_state=*/DownloadIconState::kDeepScanning,
+                         /*is_active=*/true));
+
+  UpdateDownloadItem(/*item_index=*/0, DownloadState::COMPLETE);
+  EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true,
+                                 /*icon_state=*/DownloadIconState::kComplete,
+                                 /*is_active=*/true));
+}
+
 TEST_F(DownloadDisplayControllerTest, InitialState_OldLastDownload) {
   base::Time current_time = base::Time::Now();
   // Set the last complete time to more than 1 day ago.
diff --git a/chrome/browser/download/bubble/download_icon_state.h b/chrome/browser/download/bubble/download_icon_state.h
index 5b3a6f18..bb6238a 100644
--- a/chrome/browser/download/bubble/download_icon_state.h
+++ b/chrome/browser/download/bubble/download_icon_state.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_ICON_STATE_H_
 
 namespace download {
-enum class DownloadIconState { kFailed, kProgress, kComplete };
+enum class DownloadIconState { kFailed, kProgress, kComplete, kDeepScanning };
 }  // namespace download
 
 #endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_ICON_STATE_H_
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 7248d610..c34206d 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -31,6 +31,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
+#include "ui/views/vector_icons.h"
 #endif
 
 using download::DownloadItem;
@@ -731,7 +732,6 @@
   return *this;
 }
 
-// TODO(bhatiarohit): Upload the appropriate icons for each case.
 DownloadUIModel::BubbleUIInfo DownloadUIModel::GetBubbleUIInfoForInterrupted(
     FailState fail_state) const {
   switch (fail_state) {
@@ -739,31 +739,35 @@
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_BLOCKED_ORGANIZATION))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                           ui::kColorAlertHighSeverity);
+          .AddIconAndColor(views::kInfoIcon, ui::kColorAlertHighSeverity);
     case FailState::FILE_NAME_TOO_LONG:
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_PATH_TOO_LONG))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
+          .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
                            ui::kColorAlertHighSeverity);
     case FailState::FILE_NO_SPACE:
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_DISK_FULL))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
+          .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
                            ui::kColorAlertHighSeverity);
     case FailState::SERVER_UNAUTHORIZED:
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_FILE_UNAVAILABLE))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
+          .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
                            ui::kColorAlertHighSeverity);
-    case FailState::FILE_ACCESS_DENIED:
+    // No Retry in these cases.
     case FailState::FILE_TOO_LARGE:
     case FailState::FILE_VIRUS_INFECTED:
     case FailState::FILE_SECURITY_CHECK_FAILED:
-    case FailState::FILE_TOO_SHORT:
+    case FailState::FILE_ACCESS_DENIED:
+    case FailState::SERVER_FORBIDDEN:
+      return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false)
+          .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
+                           ui::kColorAlertHighSeverity);
+    // Try retry in these cases, and in the default case.
     case FailState::FILE_SAME_AS_SOURCE:
     case FailState::NETWORK_INVALID_REQUEST:
     case FailState::NETWORK_FAILED:
@@ -771,10 +775,6 @@
     case FailState::NETWORK_TIMEOUT:
     case FailState::NETWORK_DISCONNECTED:
     case FailState::NETWORK_SERVER_DOWN:
-    case FailState::SERVER_FAILED:
-    case FailState::SERVER_CERT_PROBLEM:
-    case FailState::SERVER_UNREACHABLE:
-    case FailState::SERVER_FORBIDDEN:
     case FailState::SERVER_BAD_CONTENT:
     case FailState::USER_CANCELED:
     case FailState::FILE_TRANSIENT_ERROR:
@@ -786,13 +786,20 @@
     case FailState::SERVER_CROSS_ORIGIN_REDIRECT:
     case FailState::FILE_FAILED:
     case FailState::FILE_HASH_MISMATCH:
-    case FailState::NO_FAILURE:
+    case FailState::SERVER_FAILED:
+    case FailState::SERVER_CERT_PROBLEM:
+    case FailState::SERVER_UNREACHABLE:
+    case FailState::FILE_TOO_SHORT:
       break;
+    case FailState::NO_FAILURE:
+      return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false);
   }
-  return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false);
+  // TODO(bhatiarohit): Check if it is possible to retry downloads.
+  return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false)
+      .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
+                       ui::kColorAlertHighSeverity);
 }
 
-// TODO(bhatiarohit): Upload the appropriate icons for each case.
 DownloadUIModel::BubbleUIInfo DownloadUIModel::GetBubbleUIInfoForWarning()
     const {
   switch (GetMixedContentStatus()) {
@@ -824,8 +831,7 @@
                        IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_UNKNOWN_SOURCE,
                        l10n_util::GetStringUTF16(
                            IDS_EXTENSION_WEB_STORE_TITLE)))
-            .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                             ui::kColorAlertMediumSeverity)
+            .AddIconAndColor(views::kInfoIcon, ui::kColorAlertMediumSeverity)
             .AddSubpageButton(
                 l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE),
                 DownloadCommands::Command::KEEP)
@@ -872,8 +878,7 @@
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ENCRYPTED))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                           ui::kColorSecondaryForeground)
+          .AddIconAndColor(views::kInfoIcon, ui::kColorSecondaryForeground)
           .AddSubpageButton(
               l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE),
               DownloadCommands::Command::KEEP)
@@ -916,8 +921,7 @@
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_TOO_BIG))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                           ui::kColorSecondaryForeground)
+          .AddIconAndColor(views::kInfoIcon, ui::kColorSecondaryForeground)
           .AddSubpageButton(
               l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE),
               DownloadCommands::Command::DISCARD);
@@ -946,8 +950,7 @@
         return DownloadUIModel::BubbleUIInfo(
                    l10n_util::GetStringUTF16(
                        IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_UNCOMMON_FILE))
-            .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                             ui::kColorAlertMediumSeverity)
+            .AddIconAndColor(views::kInfoIcon, ui::kColorAlertMediumSeverity)
             .AddPrimaryButton(
                 l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE),
                 DownloadCommands::Command::DISCARD)
@@ -963,8 +966,7 @@
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SENSITIVE_CONTENT))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                           ui::kColorAlertMediumSeverity)
+          .AddIconAndColor(views::kInfoIcon, ui::kColorAlertMediumSeverity)
           .AddSubpageButton(
               l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE),
               DownloadCommands::Command::KEEP)
@@ -975,9 +977,19 @@
       return DownloadUIModel::BubbleUIInfo(
                  l10n_util::GetStringUTF16(
                      IDS_DOWNLOAD_BUBBLE_WARNING_SUBPAGE_SUMMARY_BLOCKED_ORGANIZATION))
-          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
-                           ui::kColorAlertMediumSeverity);
+          .AddIconAndColor(views::kInfoIcon, ui::kColorAlertMediumSeverity);
     case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
+      return DownloadUIModel::BubbleUIInfo(
+                 l10n_util::GetStringUTF16(
+                     IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT))
+          .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
+                           ui::kColorAlertMediumSeverity)
+          .AddPrimaryButton(l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_SCAN),
+                            DownloadCommands::Command::DEEP_SCAN)
+          .AddSubpageButton(l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_SCAN),
+                            DownloadCommands::Command::DEEP_SCAN)
+          .AddSubpageButton(l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_OPEN),
+                            DownloadCommands::Command::BYPASS_DEEP_SCANNING);
     case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE:
     case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
     case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
@@ -1008,7 +1020,9 @@
       [[fallthrough]];
     case DownloadItem::CANCELLED:
     case DownloadItem::MAX_DOWNLOAD_STATE:
-      return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false);
+      return DownloadUIModel::BubbleUIInfo(/*has_progress_and_cancel=*/false)
+          .AddIconAndColor(vector_icons::kFileDownloadOffIcon,
+                           ui::kColorSecondaryForeground);
   }
 }
 #endif
@@ -1165,6 +1179,8 @@
       return l10n_util::GetStringUTF16(
           IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_BLOCKED_ORGANIZATION);
     case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
+      return l10n_util::GetStringUTF16(
+          IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT);
     case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE:
     case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
     case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
index 067f402..9f46b60 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_browsertest.cc
@@ -372,7 +372,7 @@
   safe_browsing::EventReportValidator validator(client());
   validator.ExpectDangerousDeepScanningResult(
       /*url*/ "about:blank",
-      /*filename*/ created_file_paths()[1].AsUTF8Unsafe(),
+      /*filename*/ "bad.exe",
       // printf "bad file content" | sha256sum |  tr '[:lower:]' '[:upper:]'
       /*sha*/
       "77AE96C38386429D28E53F5005C46C7B4D8D39BE73D757CE61E0AE65CC1A5A5D",
@@ -542,9 +542,9 @@
   validator.ExpectUnscannedFileEvents(
       /*url*/ "about:blank",
       {
-          created_file_paths()[0].AsUTF8Unsafe(),
-          created_file_paths()[1].AsUTF8Unsafe(),
-          created_file_paths()[2].AsUTF8Unsafe(),
+          created_file_paths()[0].BaseName().AsUTF8Unsafe(),
+          created_file_paths()[1].BaseName().AsUTF8Unsafe(),
+          created_file_paths()[2].BaseName().AsUTF8Unsafe(),
       },
       {
           // printf "a content" | sha256sum | tr '[:lower:]' '[:upper:]'
@@ -676,7 +676,7 @@
   safe_browsing::EventReportValidator validator(client());
   validator.ExpectUnscannedFileEvent(
       /*url*/ "about:blank",
-      /*filename*/ test_zip.AsUTF8Unsafe(),
+      /*filename*/ "encrypted.zip",
       // sha256sum < chrome/test/data/safe_browsing/download_protection/\
       // encrypted.zip |  tr '[:lower:]' '[:upper:]'
       /*sha*/
@@ -750,7 +750,7 @@
   safe_browsing::EventReportValidator validator(client());
   validator.ExpectUnscannedFileEvent(
       /*url*/ "about:blank",
-      /*filename*/ created_file_paths()[0].AsUTF8Unsafe(),
+      /*filename*/ "a.png",
       // printf "\x89PNG\x0D\x0A\x1A\x0A" | sha256sum |  tr '[:lower:]' \
       // '[:upper:]'
       "4C4B6A3BE1314AB86138BEF4314DDE022E600960D8689A2C8F8631802D20DAB6",
@@ -838,7 +838,7 @@
   safe_browsing::EventReportValidator validator(client());
   validator.ExpectUnscannedFileEvent(
       /*url*/ "about:blank",
-      /*filename*/ created_file_paths()[0].AsUTF8Unsafe(),
+      /*filename*/ "large.doc",
       // python3 -c "print('a' * (42 * 50 * 1024 * 1024), end='')" |\
       // sha256sum |  tr '[:lower:]' '[:upper:]'
       /*sha*/
@@ -935,7 +935,7 @@
       BinaryUploadService::Result::SUCCESS, response);
   validator.ExpectDangerousDeepScanningResultAndSensitiveDataEvent(
       /*url*/ "about:blank",
-      /*filename*/ created_file_paths()[0].AsUTF8Unsafe(),
+      /*filename*/ "foo.doc",
       // printf "foo content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
       /*sha*/
       "B3A2E2EDBAA3C798B4FC267792B1641B94793DE02D870124E5CBE663750B4CFC",
diff --git a/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc b/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
index d0bcdf01..e74209d89 100644
--- a/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/constants/ash_features.h"
 #include "base/files/file_path.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
@@ -56,10 +57,11 @@
 
 class EnterpriseDeviceAttributesTest
     : public ForceInstalledAffiliatedExtensionApiTest,
-      public ::testing::WithParamInterface<bool> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   EnterpriseDeviceAttributesTest()
-      : ForceInstalledAffiliatedExtensionApiTest(GetParam()) {
+      : ForceInstalledAffiliatedExtensionApiTest(std::get<0>(GetParam()),
+                                                 std::get<1>(GetParam())) {
     fake_statistics_provider_.SetMachineStatistic(
         chromeos::system::kSerialNumberKeyForTest, kSerialNumber);
   }
@@ -95,7 +97,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, Success) {
-  const bool is_affiliated = GetParam();
+  const bool is_affiliated = std::get<0>(GetParam());
   EXPECT_EQ(is_affiliated, user_manager::UserManager::Get()
                                ->FindUser(affiliation_mixin_.account_id())
                                ->IsAffiliated());
@@ -120,7 +122,8 @@
 // Both cases of affiliated and non-affiliated users are tested.
 INSTANTIATE_TEST_SUITE_P(AffiliationCheck,
                          EnterpriseDeviceAttributesTest,
-                         ::testing::Bool());
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool()));
 
 // Ensure that extensions that are not pre-installed by policy throw an install
 // warning if they request the enterprise.deviceAttributes permission in the
diff --git a/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_ash_apitest.cc b/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_ash_apitest.cc
index 725b8033..906874c3 100644
--- a/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_ash_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_ash_apitest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/constants/ash_features.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
 #include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h"
@@ -69,10 +70,11 @@
 
 class EnterpriseNetworkingAttributesTest
     : public ForceInstalledAffiliatedExtensionApiTest,
-      public ::testing::WithParamInterface<bool> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   EnterpriseNetworkingAttributesTest()
-      : ForceInstalledAffiliatedExtensionApiTest(GetParam()) {}
+      : ForceInstalledAffiliatedExtensionApiTest(std::get<0>(GetParam()),
+                                                 std::get<1>(GetParam())) {}
 
   void SetupDisconnectedNetwork() {
     chromeos::ShillDeviceClient::TestInterface* shill_device_client =
@@ -146,6 +148,9 @@
                                              base::Value(shill::kStateOnline));
     base::RunLoop().RunUntilIdle();
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(EnterpriseNetworkingAttributesTest,
@@ -154,7 +159,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(EnterpriseNetworkingAttributesTest, GetNetworkDetails) {
-  const bool is_affiliated = GetParam();
+  const bool is_affiliated = std::get<0>(GetParam());
   EXPECT_EQ(is_affiliated, user_manager::UserManager::Get()
                                ->FindUser(affiliation_mixin_.account_id())
                                ->IsAffiliated());
@@ -184,7 +189,8 @@
 // Both cases of affiliated and non-affiliated users are tested.
 INSTANTIATE_TEST_SUITE_P(AffiliationCheck,
                          EnterpriseNetworkingAttributesTest,
-                         ::testing::Bool());
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool()));
 
 // Ensure that extensions that are not pre-installed by policy throw an install
 // warning if they request the enterprise.networkingAttributes permission in the
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
index 80442799..125593e 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -202,12 +203,24 @@
 
 class EnterprisePlatformKeysTest
     : public PlatformKeysTestBase,
-      public ::testing::WithParamInterface<Params> {
+      public ::testing::WithParamInterface<std::tuple<Params, bool>> {
  public:
   EnterprisePlatformKeysTest()
-      : PlatformKeysTestBase(GetParam().system_token_status_,
-                             GetParam().enrollment_status_,
-                             GetParam().user_status_) {}
+      : PlatformKeysTestBase(std::get<0>(GetParam()).system_token_status_,
+                             std::get<0>(GetParam()).enrollment_status_,
+                             std::get<0>(GetParam()).user_status_) {
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (std::get<1>(GetParam())) {
+      feature_list_.InitAndEnableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    }
+  }
 
   EnterprisePlatformKeysTest(const EnterprisePlatformKeysTest&) = delete;
   EnterprisePlatformKeysTest& operator=(const EnterprisePlatformKeysTest&) =
@@ -262,6 +275,7 @@
   // destructor).
   ash::platform_keys::test_util::ScopedChapsUtilOverride
       scoped_chaps_util_override_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 }  // namespace
@@ -298,19 +312,22 @@
 INSTANTIATE_TEST_SUITE_P(
     CheckSystemTokenAvailability,
     EnterprisePlatformKeysTest,
-    ::testing::Values(
-        Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
-               PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-               PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
-        Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
-               PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-               PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
-        Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
-               PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
-               PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
-        Params(PlatformKeysTestBase::SystemTokenStatus::DOES_NOT_EXIST,
-               PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-               PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN)));
+    ::testing::Combine(
+        ::testing::Values(
+            Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
+                   PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                   PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
+            Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
+                   PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                   PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
+            Params(PlatformKeysTestBase::SystemTokenStatus::EXISTS,
+                   PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
+                   PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
+            Params(
+                PlatformKeysTestBase::SystemTokenStatus::DOES_NOT_EXIST,
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN)),
+        ::testing::Bool()));
 
 // Ensure that extensions that are not pre-installed by policy throw an install
 // warning if they request the enterprise.platformKeys permission in the
diff --git a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
index bd2c11f..ab71284b 100644
--- a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
+++ b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/files/file_path.h"
 #include "base/json/json_writer.h"
 #include "base/path_service.h"
@@ -31,13 +32,27 @@
 namespace extensions {
 
 ForceInstalledAffiliatedExtensionApiTest::
-    ForceInstalledAffiliatedExtensionApiTest(bool is_affiliated)
+    ForceInstalledAffiliatedExtensionApiTest(bool is_affiliated,
+                                             bool is_auth_session_enabled)
     : test_install_attributes_(
           ash::StubInstallAttributes::CreateCloudManaged("fake-domain",
                                                          "fake-id")) {
+  // TODO(crbug.com/1311355): This test is run with the feature
+  // kUseAuthsessionAuthentication enabled and disabled because of a
+  // transitive dependency of AffiliationTestHelper on that feature. Remove
+  // the parameter when kUseAuthsessionAuthentication is removed.
+  if (is_auth_session_enabled) {
+    feature_list_.InitAndEnableFeature(
+        ash::features::kUseAuthsessionAuthentication);
+  } else {
+    feature_list_.InitAndDisableFeature(
+        ash::features::kUseAuthsessionAuthentication);
+  }
+
   set_exit_when_last_browser_closes(false);
   set_chromeos_user_ = false;
   affiliation_mixin_.set_affiliated(is_affiliated);
+  cryptohome_mixin_.MarkUserAsExisting(affiliation_mixin_.account_id());
 }
 
 ForceInstalledAffiliatedExtensionApiTest::
diff --git a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h
index cec0c83..95e2dd47 100644
--- a/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h
+++ b/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h
@@ -9,6 +9,7 @@
 
 #include "ash/components/tpm/stub_install_attributes.h"
 #include "base/values.h"
+#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/extensions/mixin_based_extension_apitest.h"
@@ -33,7 +34,9 @@
 class ForceInstalledAffiliatedExtensionApiTest
     : public MixinBasedExtensionApiTest {
  public:
-  explicit ForceInstalledAffiliatedExtensionApiTest(bool is_affiliated);
+  explicit ForceInstalledAffiliatedExtensionApiTest(
+      bool is_affiliated,
+      bool is_auth_session_enabled);
   ~ForceInstalledAffiliatedExtensionApiTest() override;
 
  protected:
@@ -57,6 +60,10 @@
   policy::DevicePolicyCrosTestHelper test_helper_;
   policy::AffiliationMixin affiliation_mixin_{&mixin_host_, &test_helper_};
   ExtensionForceInstallMixin force_install_mixin_{&mixin_host_};
+  ash::CryptohomeMixin cryptohome_mixin_{&mixin_host_};
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 }  //  namespace extensions
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
index 54dad84..14ddaf86 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
@@ -292,13 +293,29 @@
 
 class UnmanagedPlatformKeysTest
     : public PlatformKeysTest,
-      public ::testing::WithParamInterface<UnmanagedPlatformKeysTestParams> {
+      public ::testing::WithParamInterface<
+          std::tuple<UnmanagedPlatformKeysTestParams, bool>> {
  public:
   UnmanagedPlatformKeysTest()
-      : PlatformKeysTest(GetParam().enrollment_status_,
+      : PlatformKeysTest(std::get<0>(GetParam()).enrollment_status_,
                          UserStatus::UNMANAGED,
                          false /* unused */,
-                         GetParam().user_client_cert_slot_) {}
+                         std::get<0>(GetParam()).user_client_cert_slot_) {
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (std::get<1>(GetParam())) {
+      feature_list_.InitAndEnableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 struct ManagedPlatformKeysTestParams {
@@ -313,24 +330,56 @@
 
 class ManagedWithPermissionPlatformKeysTest
     : public PlatformKeysTest,
-      public ::testing::WithParamInterface<ManagedPlatformKeysTestParams> {
+      public ::testing::WithParamInterface<
+          std::tuple<ManagedPlatformKeysTestParams, bool>> {
  public:
   ManagedWithPermissionPlatformKeysTest()
-      : PlatformKeysTest(GetParam().enrollment_status_,
-                         GetParam().user_status_,
+      : PlatformKeysTest(std::get<0>(GetParam()).enrollment_status_,
+                         std::get<0>(GetParam()).user_status_,
                          true /* grant the extension key permission */,
-                         UserClientCertSlot::kPrivateSlot) {}
+                         UserClientCertSlot::kPrivateSlot) {
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (std::get<1>(GetParam())) {
+      feature_list_.InitAndEnableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 class ManagedWithoutPermissionPlatformKeysTest
     : public PlatformKeysTest,
-      public ::testing::WithParamInterface<ManagedPlatformKeysTestParams> {
+      public ::testing::WithParamInterface<
+          std::tuple<ManagedPlatformKeysTestParams, bool>> {
  public:
   ManagedWithoutPermissionPlatformKeysTest()
-      : PlatformKeysTest(GetParam().enrollment_status_,
-                         GetParam().user_status_,
+      : PlatformKeysTest(std::get<0>(GetParam()).enrollment_status_,
+                         std::get<0>(GetParam()).user_status_,
                          false /* do not grant key permission */,
-                         UserClientCertSlot::kPrivateSlot) {}
+                         UserClientCertSlot::kPrivateSlot) {
+    // TODO(crbug.com/1311355): This test is run with the feature
+    // kUseAuthsessionAuthentication enabled and disabled because of a
+    // transitive dependency of AffiliationTestHelper on that feature. Remove
+    // the parameter when kUseAuthsessionAuthentication is removed.
+    if (std::get<1>(GetParam())) {
+      feature_list_.InitAndEnableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          ash::features::kUseAuthsessionAuthentication);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 }  // namespace
@@ -375,18 +424,21 @@
 INSTANTIATE_TEST_SUITE_P(
     Unmanaged,
     UnmanagedPlatformKeysTest,
-    ::testing::Values(UnmanagedPlatformKeysTestParams(
-                          PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-                          PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
-                      UnmanagedPlatformKeysTestParams(
-                          PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
-                          PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
-                      UnmanagedPlatformKeysTestParams(
-                          PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-                          PlatformKeysTest::UserClientCertSlot::kPublicSlot),
-                      UnmanagedPlatformKeysTestParams(
-                          PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
-                          PlatformKeysTest::UserClientCertSlot::kPublicSlot)));
+    ::testing::Combine(
+        ::testing::Values(
+            UnmanagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
+            UnmanagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
+                PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
+            UnmanagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTest::UserClientCertSlot::kPublicSlot),
+            UnmanagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
+                PlatformKeysTest::UserClientCertSlot::kPublicSlot)),
+        ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
                        PRE_UserPermissionsBlocked) {
@@ -425,16 +477,18 @@
 INSTANTIATE_TEST_SUITE_P(
     ManagedWithoutPermission,
     ManagedWithoutPermissionPlatformKeysTest,
-    ::testing::Values(
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)));
+    ::testing::Combine(
+        ::testing::Values(
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)),
+        ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
                        PRE_PolicyGrantsAccessToCorporateKey) {
@@ -484,13 +538,15 @@
 INSTANTIATE_TEST_SUITE_P(
     ManagedWithPermission,
     ManagedWithPermissionPlatformKeysTest,
-    ::testing::Values(
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
-        ManagedPlatformKeysTestParams(
-            PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
-            PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)));
+    ::testing::Combine(
+        ::testing::Values(
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
+            ManagedPlatformKeysTestParams(
+                PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
+                PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)),
+        ::testing::Bool()));
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
index fa1f980..ef2c1885 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
@@ -50,6 +50,7 @@
   set_chromeos_user_ = false;
   // We log in without running browser.
   set_exit_when_last_browser_closes(false);
+  cryptohome_mixin_.MarkUserAsExisting(account_id_);
 }
 
 PlatformKeysTestBase::~PlatformKeysTestBase() {}
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h
index 46da2fa0..eeac4c4 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/components/tpm/stub_install_attributes.h"
+#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/extensions/mixin_based_extension_apitest.h"
 #include "components/account_id/account_id.h"
@@ -108,6 +109,7 @@
   FakeGaia fake_gaia_;
   net::EmbeddedTestServer gaia_server_{net::EmbeddedTestServer::TYPE_HTTPS};
   ash::ScopedStubInstallAttributes install_attributes_;
+  ash::CryptohomeMixin cryptohome_mixin_{&mixin_host_};
 };
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_PLATFORM_KEYS_PLATFORM_KEYS_TEST_BASE_H_
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index ab84cc4..a962e6a 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -9,9 +9,11 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/files/file_path.h"
 #include "base/json/values_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -274,6 +276,18 @@
     profile_client_->RemoveObserver(this);
 }
 
+// static
+std::string SafeBrowsingPrivateEventRouter::GetBaseName(
+    const std::string& filename) {
+  base::FilePath::StringType os_filename;
+#if BUILDFLAG(IS_WIN)
+  os_filename = base::UTF8ToWide(filename);
+#else
+  os_filename = filename;
+#endif
+  return base::FilePath(os_filename).BaseName().AsUTF8Unsafe();
+}
+
 void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordReuseDetected(
     const GURL& url,
     const std::string& user_name,
@@ -383,7 +397,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, params.url);
-  event.Set(kKeyFileName, params.file_name);
+  event.Set(kKeyFileName, GetBaseName(params.file_name));
   event.Set(kKeyDownloadDigestSha256, params.download_digest_sha256);
   event.Set(kKeyProfileUserName, params.user_name);
   event.Set(kKeyContentType, mime_type);
@@ -546,7 +560,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyThreatType, threat_type);
@@ -598,7 +612,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyContentType, mime_type);
@@ -642,7 +656,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyContentType, mime_type);
@@ -688,7 +702,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyContentType, mime_type);
@@ -739,7 +753,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyThreatType, threat_type);
@@ -793,7 +807,7 @@
 
   base::Value::Dict event;
   event.Set(kKeyUrl, url.spec());
-  event.Set(kKeyFileName, file_name);
+  event.Set(kKeyFileName, GetBaseName(file_name));
   event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
   event.Set(kKeyProfileUserName, GetProfileUserName());
   event.Set(kKeyThreatType, threat_type);
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index 04f3ddcf..5279da3 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -318,6 +318,9 @@
   // Determines if real-time reporting is available based on platform and user.
   static bool IsRealtimeReportingAvailable();
 
+  // Removes any path information and returns just the basename.
+  static std::string GetBaseName(const std::string& filename);
+
   // Returns the Gaia email address of the account signed in to the profile or
   // an empty string if the profile is not signed in.
   std::string GetProfileUserName() const;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 664027d6..587d6a9 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -438,7 +438,7 @@
   const base::Value::Dict* event = wrapper.FindDict(
       SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
   EXPECT_NE(nullptr, event);
-  EXPECT_EQ("/path/to/malware.exe",
+  EXPECT_EQ("malware.exe",
             *event->FindString(SafeBrowsingPrivateEventRouter::kKeyFileName));
   EXPECT_EQ("exe", *event->FindString(
                        SafeBrowsingPrivateEventRouter::kKeyContentType));
@@ -550,7 +550,7 @@
   const base::Value::Dict* event = wrapper.FindDict(
       SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
   EXPECT_NE(nullptr, event);
-  EXPECT_EQ("/path/to/warning.exe",
+  EXPECT_EQ("warning.exe",
             *event->FindString(SafeBrowsingPrivateEventRouter::kKeyFileName));
   EXPECT_EQ("exe", *event->FindString(
                        SafeBrowsingPrivateEventRouter::kKeyContentType));
@@ -588,7 +588,7 @@
   const base::Value::Dict* event = wrapper.FindDict(
       SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
   EXPECT_NE(nullptr, event);
-  EXPECT_EQ("/path/to/bypass.exe",
+  EXPECT_EQ("bypass.exe",
             *event->FindString(SafeBrowsingPrivateEventRouter::kKeyFileName));
   EXPECT_EQ("exe", *event->FindString(
                        SafeBrowsingPrivateEventRouter::kKeyContentType));
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index 0134b8c0a..6c6ad88 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -357,7 +357,14 @@
   ASSERT_TRUE(RunTest("webnavigation/forwardBack")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiBackForwardCacheTest, ForwardBack) {
+// TODO(crbug.com/1313923): Flaky on Mac10.14
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_ForwardBack DISABLED_ForwardBack
+#else
+#define MAYBE_ForwardBack ForwardBack
+#endif
+IN_PROC_BROWSER_TEST_F(WebNavigationApiBackForwardCacheTest,
+                       MAYBE_ForwardBack) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("webnavigation/backForwardCache")) << message_;
 }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 1f96d65..e71a637 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3265,7 +3265,7 @@
   {
     "name": "filling-across-affiliated-websites",
     "owners": [ "vsemeniuk", "vasilii" ],
-    "expiry_milestone": 96
+    "expiry_milestone": 102
   },
   {
     "name": "focus-follows-cursor",
@@ -5500,6 +5500,11 @@
     "expiry_milestone": 103
   },
   {
+    "name": "sync-chromeos-explicit-passphrase-sharing",
+    "owners": ["mmoskvitin@google.com", "chrome-sync-dev@google.com"],
+    "expiry_milestone": 104
+  },
+  {
     "name": "sync-settings-categorization",
     "owners": [ "bsazonov", "rsorokin", "cros-oac@google.com" ],
     "expiry_milestone": 103
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 79f08847..9a6a1e5 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2468,10 +2468,6 @@
     "Allows users to see an animation when entering or leaving the "
     "Grid Tab Switcher on phones.";
 
-const char kAppsShortcutDefaultOffName[] = "Apps Shortcut Default Off";
-const char kAppsShortcutDefaultOffDescription[] =
-    "Changes the apps shortcut on the bookmarks bar to default to off.";
-
 const char kTabGroupsNewBadgePromoName[] = "Tab Groups 'New' Badge Promo";
 const char kTabGroupsNewBadgePromoDescription[] =
     "Causes a 'New' badge to appear on the entry point for creating a tab "
@@ -5540,6 +5536,11 @@
 const char kMessagesPreinstallName[] = "Preinstall  Messages PWA";
 const char kMessagesPreinstallDescription[] =
     "Enables preinstallation of the Messages for Web PWA for unmanaged users.";
+
+const char kSyncChromeOSExplicitPassphraseSharingName[] =
+    "Sync passphrase sharing";
+const char kSyncChromeOSExplicitPassphraseSharingDescription[] =
+    "Allows sharing custom sync passphrase between OS and Browser on ChromeOS";
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index bd01991..e144d0d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1399,9 +1399,6 @@
 extern const char kTabToGTSAnimationAndroidName[];
 extern const char kTabToGTSAnimationAndroidDescription[];
 
-extern const char kAppsShortcutDefaultOffName[];
-extern const char kAppsShortcutDefaultOffDescription[];
-
 extern const char kTabGroupsNewBadgePromoName[];
 extern const char kTabGroupsNewBadgePromoDescription[];
 
@@ -3188,6 +3185,9 @@
 
 extern const char kMessagesPreinstallName[];
 extern const char kMessagesPreinstallDescription[];
+
+extern const char kSyncChromeOSExplicitPassphraseSharingName[];
+extern const char kSyncChromeOSExplicitPassphraseSharingDescription[];
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index 4bd6e3af..b4c85b9 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -110,12 +110,14 @@
 
 const std::vector<KeyboardShortcutData>&
 GetDelayedShortcutsNotPresentInMainMenu() {
+  // clang-format off
   static base::NoDestructor<std::vector<KeyboardShortcutData>> keys({
-      // cmd   shift  cntrl  option vkeycode               command
-      //---   -----  -----  ------ --------               -------
-      {true, false, false, false, kVK_LeftArrow, IDC_BACK},
-      {true, false, false, false, kVK_RightArrow, IDC_FORWARD},
+    // cmd    shift  cntrl  option vkeycode               command
+    // ---    -----  -----  ------ --------               -------
+      {true,  false, false, false, kVK_LeftArrow,         IDC_BACK},
+      {true,  false, false, false, kVK_RightArrow,        IDC_FORWARD},
   });
+  // clang-format on
   return *keys;
 }
 
@@ -131,73 +133,72 @@
   return {cmd, /*from_main_menu=*/false};
 }
 
+}  // namespace
+
 // Returns a vector of hidden keyboard shortcuts (i.e. ones that arent present
 // in the menus). Note that the hidden "Cmd =" shortcut is somehow enabled by
 // the ui::VKEY_OEM_PLUS entry in accelerators_cocoa.mm.
-std::vector<KeyboardShortcutData> CreateKeyboardShortcutVector() {
-  // clang-format off
-  std::vector<KeyboardShortcutData> keys({
-  // cmd    shift  cntrl  option vkeycode               command
-  // ---    -----  -----  ------ --------               -------
-    {true,  true,  false, false, kVK_ANSI_RightBracket, IDC_SELECT_NEXT_TAB},
-    {true,  true,  false, false, kVK_ANSI_LeftBracket,  IDC_SELECT_PREVIOUS_TAB},
-    {false, false, true,  false, kVK_PageDown,          IDC_SELECT_NEXT_TAB},
-    {false, false, true,  false, kVK_PageUp,            IDC_SELECT_PREVIOUS_TAB},
-    {true,  false, false, true,  kVK_RightArrow,        IDC_SELECT_NEXT_TAB},
-    {true,  false, false, true,  kVK_LeftArrow,         IDC_SELECT_PREVIOUS_TAB},
-
-    // Cmd-0..8 select the nth tab, with cmd-9 being "last tab".
-    {true,  false, false, false, kVK_ANSI_1,            IDC_SELECT_TAB_0},
-    {true,  false, false, false, kVK_ANSI_Keypad1,      IDC_SELECT_TAB_0},
-    {true,  false, false, false, kVK_ANSI_2,            IDC_SELECT_TAB_1},
-    {true,  false, false, false, kVK_ANSI_Keypad2,      IDC_SELECT_TAB_1},
-    {true,  false, false, false, kVK_ANSI_3,            IDC_SELECT_TAB_2},
-    {true,  false, false, false, kVK_ANSI_Keypad3,      IDC_SELECT_TAB_2},
-    {true,  false, false, false, kVK_ANSI_4,            IDC_SELECT_TAB_3},
-    {true,  false, false, false, kVK_ANSI_Keypad4,      IDC_SELECT_TAB_3},
-    {true,  false, false, false, kVK_ANSI_5,            IDC_SELECT_TAB_4},
-    {true,  false, false, false, kVK_ANSI_Keypad5,      IDC_SELECT_TAB_4},
-    {true,  false, false, false, kVK_ANSI_6,            IDC_SELECT_TAB_5},
-    {true,  false, false, false, kVK_ANSI_Keypad6,      IDC_SELECT_TAB_5},
-    {true,  false, false, false, kVK_ANSI_7,            IDC_SELECT_TAB_6},
-    {true,  false, false, false, kVK_ANSI_Keypad7,      IDC_SELECT_TAB_6},
-    {true,  false, false, false, kVK_ANSI_8,            IDC_SELECT_TAB_7},
-    {true,  false, false, false, kVK_ANSI_Keypad8,      IDC_SELECT_TAB_7},
-    {true,  false, false, false, kVK_ANSI_9,            IDC_SELECT_LAST_TAB},
-    {true,  false, false, false, kVK_ANSI_Keypad9,      IDC_SELECT_LAST_TAB},
-
-    {true,  true,  false, false, kVK_ANSI_M,            IDC_SHOW_AVATAR_MENU},
-    {true,  false, false, true,  kVK_ANSI_L,            IDC_SHOW_DOWNLOADS},
-    {true,  true,  false, false, kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
-    {true,  false, false, true,  kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
-    {true,  false, false, true,  kVK_DownArrow,         IDC_FOCUS_NEXT_PANE},
-    {true,  false, false, true,  kVK_UpArrow,           IDC_FOCUS_PREVIOUS_PANE},
-    {true,  true,  false, true,  kVK_ANSI_A,            IDC_FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY},
-  });
-  // clang-format on
-
-  if (base::FeatureList::IsEnabled(features::kUIDebugTools)) {
-    keys.push_back(
-        {false, true, true, true, kVK_ANSI_T, IDC_DEBUG_TOGGLE_TABLET_MODE});
-    keys.push_back(
-        {false, true, true, true, kVK_ANSI_V, IDC_DEBUG_PRINT_VIEW_TREE});
-    keys.push_back({false, true, true, true, kVK_ANSI_M,
-                    IDC_DEBUG_PRINT_VIEW_TREE_DETAILS});
-  }
-  return keys;
-}
-
-}  // namespace
-
 const std::vector<KeyboardShortcutData>& GetShortcutsNotPresentInMainMenu() {
-  static const base::NoDestructor<std::vector<KeyboardShortcutData>> keys(
-      CreateKeyboardShortcutVector());
+  static const base::NoDestructor<std::vector<KeyboardShortcutData>> keys([]() {
+    // clang-format off
+    std::vector<KeyboardShortcutData> keys({
+    // cmd    shift  cntrl  option vkeycode               command
+    // ---    -----  -----  ------ --------               -------
+      {true,  true,  false, false, kVK_ANSI_RightBracket, IDC_SELECT_NEXT_TAB},
+      {true,  true,  false, false, kVK_ANSI_LeftBracket,  IDC_SELECT_PREVIOUS_TAB},
+      {false, false, true,  false, kVK_PageDown,          IDC_SELECT_NEXT_TAB},
+      {false, false, true,  false, kVK_PageUp,            IDC_SELECT_PREVIOUS_TAB},
+      {true,  false, false, true,  kVK_RightArrow,        IDC_SELECT_NEXT_TAB},
+      {true,  false, false, true,  kVK_LeftArrow,         IDC_SELECT_PREVIOUS_TAB},
+      {false, true,  true,  false, kVK_PageDown,          IDC_MOVE_TAB_NEXT},
+      {false, true,  true,  false, kVK_PageUp,            IDC_MOVE_TAB_PREVIOUS},
+
+      // Cmd-0..8 select the nth tab, with cmd-9 being "last tab".
+      {true,  false, false, false, kVK_ANSI_1,            IDC_SELECT_TAB_0},
+      {true,  false, false, false, kVK_ANSI_Keypad1,      IDC_SELECT_TAB_0},
+      {true,  false, false, false, kVK_ANSI_2,            IDC_SELECT_TAB_1},
+      {true,  false, false, false, kVK_ANSI_Keypad2,      IDC_SELECT_TAB_1},
+      {true,  false, false, false, kVK_ANSI_3,            IDC_SELECT_TAB_2},
+      {true,  false, false, false, kVK_ANSI_Keypad3,      IDC_SELECT_TAB_2},
+      {true,  false, false, false, kVK_ANSI_4,            IDC_SELECT_TAB_3},
+      {true,  false, false, false, kVK_ANSI_Keypad4,      IDC_SELECT_TAB_3},
+      {true,  false, false, false, kVK_ANSI_5,            IDC_SELECT_TAB_4},
+      {true,  false, false, false, kVK_ANSI_Keypad5,      IDC_SELECT_TAB_4},
+      {true,  false, false, false, kVK_ANSI_6,            IDC_SELECT_TAB_5},
+      {true,  false, false, false, kVK_ANSI_Keypad6,      IDC_SELECT_TAB_5},
+      {true,  false, false, false, kVK_ANSI_7,            IDC_SELECT_TAB_6},
+      {true,  false, false, false, kVK_ANSI_Keypad7,      IDC_SELECT_TAB_6},
+      {true,  false, false, false, kVK_ANSI_8,            IDC_SELECT_TAB_7},
+      {true,  false, false, false, kVK_ANSI_Keypad8,      IDC_SELECT_TAB_7},
+      {true,  false, false, false, kVK_ANSI_9,            IDC_SELECT_LAST_TAB},
+      {true,  false, false, false, kVK_ANSI_Keypad9,      IDC_SELECT_LAST_TAB},
+
+      {true,  true,  false, false, kVK_ANSI_M,            IDC_SHOW_AVATAR_MENU},
+      {true,  false, false, true,  kVK_ANSI_L,            IDC_SHOW_DOWNLOADS},
+      {true,  true,  false, false, kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
+      {true,  false, false, true,  kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
+      {true,  false, false, true,  kVK_DownArrow,         IDC_FOCUS_NEXT_PANE},
+      {true,  false, false, true,  kVK_UpArrow,           IDC_FOCUS_PREVIOUS_PANE},
+      {true,  true,  false, true,  kVK_ANSI_A,            IDC_FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY},
+    });
+    // clang-format on
+
+    if (base::FeatureList::IsEnabled(features::kUIDebugTools)) {
+      keys.push_back(
+          {false, true, true, true, kVK_ANSI_T, IDC_DEBUG_TOGGLE_TABLET_MODE});
+      keys.push_back(
+          {false, true, true, true, kVK_ANSI_V, IDC_DEBUG_PRINT_VIEW_TREE});
+      keys.push_back({false, true, true, true, kVK_ANSI_M,
+                      IDC_DEBUG_PRINT_VIEW_TREE_DETAILS});
+    }
+    return keys;
+  }());
   return *keys;
 }
 
 const std::vector<NSMenuItem*>& GetMenuItemsNotPresentInMainMenu() {
-  static base::NoDestructor<std::vector<NSMenuItem*>> menu_items;
-  if (menu_items->empty()) {
+  static base::NoDestructor<std::vector<NSMenuItem*>> menu_items([]() {
+    std::vector<NSMenuItem*> menu_items;
     for (const auto& shortcut : GetShortcutsNotPresentInMainMenu()) {
       ui::Accelerator accelerator = AcceleratorFromShortcut(shortcut);
       NSString* key_equivalent = nil;
@@ -207,15 +208,16 @@
 
       // Intentionally leaked!
       NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:@""
-                                                    action:NULL
+                                                    action:nullptr
                                              keyEquivalent:key_equivalent];
       item.keyEquivalentModifierMask = modifier_mask;
 
       // We store the command in the tag.
       item.tag = shortcut.chrome_command;
-      menu_items->push_back(item);
+      menu_items.push_back(item);
     }
-  }
+    return menu_items;
+  }());
   return *menu_items;
 }
 
diff --git a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
index ed2cba5..d5c6df9e 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
@@ -40,24 +40,33 @@
                    ShiftKeyState shift = ShiftKeyState::kUp,
                    OptionKeyState option = OptionKeyState::kUp,
                    ControlKeyState control = ControlKeyState::kUp) {
-  NSUInteger modifierFlags = 0;
+  NSUInteger modifier_flags = 0;
   if (command == CommandKeyState::kDown)
-    modifierFlags |= NSCommandKeyMask;
+    modifier_flags |= NSCommandKeyMask;
   if (shift == ShiftKeyState::kDown)
-    modifierFlags |= NSShiftKeyMask;
+    modifier_flags |= NSShiftKeyMask;
   if (option == OptionKeyState::kDown)
-    modifierFlags |= NSAlternateKeyMask;
+    modifier_flags |= NSAlternateKeyMask;
   if (control == ControlKeyState::kDown)
-    modifierFlags |= NSControlKeyMask;
+    modifier_flags |= NSControlKeyMask;
 
   switch (vkey_code) {
     case kVK_UpArrow:
     case kVK_DownArrow:
     case kVK_LeftArrow:
     case kVK_RightArrow:
-      // Docs say this is set whenever a key came from the numpad *or* the arrow
-      // keys.
-      modifierFlags |= NSEventModifierFlagNumericPad;
+      // Docs say that this is set for numpad *and* arrow keys.
+      modifier_flags |= NSEventModifierFlagNumericPad;
+      [[fallthrough]];
+    case kVK_Help:
+    case kVK_ForwardDelete:
+    case kVK_Home:
+    case kVK_End:
+    case kVK_PageUp:
+    case kVK_PageDown:
+      // Docs say that this is set for function keys *and* the cluster of six
+      // navigation keys in the center of the keyboard *and* arrow keys.
+      modifier_flags |= NSEventModifierFlagFunction;
       break;
     default:
       break;
@@ -66,14 +75,14 @@
   unichar shifted_character;
   unichar character;
   int result = ui::MacKeyCodeForWindowsKeyCode(
-      ui::KeyboardCodeFromKeyCode(vkey_code), modifierFlags, &shifted_character,
-      &character);
+      ui::KeyboardCodeFromKeyCode(vkey_code), modifier_flags,
+      &shifted_character, &character);
   DCHECK_NE(result, -1);
 
   NSEvent* event = [NSEvent
                  keyEventWithType:NSKeyDown
                          location:NSZeroPoint
-                    modifierFlags:modifierFlags
+                    modifierFlags:modifier_flags
                         timestamp:0.0
                      windowNumber:0
                           context:nil
@@ -142,17 +151,17 @@
   // We only consider unshifted keys. A shifted numpad key gives a different
   // keyEquivalent than a shifted number key.
   const ShiftKeyState shift = ShiftKeyState::kUp;
-  for (unsigned int i = 0; i < std::size(equivalents); ++i) {
+  for (auto equivalent : equivalents) {
     for (CommandKeyState command :
          {CommandKeyState::kUp, CommandKeyState::kDown}) {
       for (OptionKeyState option :
            {OptionKeyState::kUp, OptionKeyState::kDown}) {
         for (ControlKeyState control :
              {ControlKeyState::kUp, ControlKeyState::kDown}) {
-          EXPECT_EQ(CommandForKeys(equivalents[i].keycode, command, shift,
-                                   option, control),
-                    CommandForKeys(equivalents[i].keypad_keycode, command,
-                                   shift, option, control));
+          EXPECT_EQ(CommandForKeys(equivalent.keycode, command, shift, option,
+                                   control),
+                    CommandForKeys(equivalent.keypad_keycode, command, shift,
+                                   option, control));
         }
       }
     }
diff --git a/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc b/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
index 8ab67c1..8d3beb8 100644
--- a/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
@@ -133,7 +133,7 @@
 
 // Verifies that print-to-PDF adds an associated item to holding space.
 IN_PROC_BROWSER_TEST_P(HoldingSpaceServicePrintToPdfIntegrationBrowserTest,
-                       DISABLED_AddPrintedPdfItem) {
+                       AddPrintedPdfItem) {
   // If holding space service interface is not available on this version of
   // ash-chrome, this test suite will no-op.
   if (!IsServiceAvailable())
diff --git a/chrome/browser/lacros/overview_lacros_browsertest.cc b/chrome/browser/lacros/overview_lacros_browsertest.cc
index a7bac9a..5b5032a 100644
--- a/chrome/browser/lacros/overview_lacros_browsertest.cc
+++ b/chrome/browser/lacros/overview_lacros_browsertest.cc
@@ -13,6 +13,23 @@
 #include "content/public/test/browser_test.h"
 #include "ui/aura/window.h"
 
+namespace {
+
+// If ash does not contain the relevant test controller functionality, then
+// there's nothing to do for this test.
+bool IsServiceAvailable() {
+  if (chromeos::LacrosService::Get()->GetInterfaceVersion(
+          crosapi::mojom::TestController::Uuid_) <
+      static_cast<int>(crosapi::mojom::TestController::MethodMinVersions::
+                           kEnterOverviewModeMinVersion)) {
+    LOG(WARNING) << "Unsupported ash version.";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
 using OverviewBrowserTest = InProcessBrowserTest;
 
 // We enter overview mode with a single window. When we close the window,
@@ -20,6 +37,9 @@
 // TODO(https://crbug.com/1157314): This test is not safe to run in parallel
 // with other lacros tests as overview mode applies to all processes.
 IN_PROC_BROWSER_TEST_F(OverviewBrowserTest, NoCrashWithSingleWindow) {
+  if (!IsServiceAvailable())
+    return;
+
   // Wait for the window to be visible.
   aura::Window* window = browser()->window()->GetNativeWindow();
   std::string id = browser_test_util::GetWindowId(window->GetRootWindow());
@@ -27,7 +47,6 @@
 
   // Enter overview mode.
   auto* lacros_service = chromeos::LacrosService::Get();
-  CHECK(lacros_service->IsAvailable<crosapi::mojom::TestController>());
   crosapi::mojom::TestControllerAsyncWaiter waiter(
       lacros_service->GetRemote<crosapi::mojom::TestController>().get());
   waiter.EnterOverviewMode();
@@ -43,6 +62,9 @@
 // TODO(https://crbug.com/1157314): This test is not safe to run in parallel
 // with other lacros tests as overview mode applies to all processes.
 IN_PROC_BROWSER_TEST_F(OverviewBrowserTest, NoCrashTwoWindows) {
+  if (!IsServiceAvailable())
+    return;
+
   // Wait for the window to be visible.
   aura::Window* main_window = browser()->window()->GetNativeWindow();
   std::string main_id =
@@ -62,7 +84,6 @@
 
   // Enter overview mode.
   auto* lacros_service = chromeos::LacrosService::Get();
-  CHECK(lacros_service->IsAvailable<crosapi::mojom::TestController>());
   crosapi::mojom::TestControllerAsyncWaiter waiter(
       lacros_service->GetRemote<crosapi::mojom::TestController>().get());
   waiter.EnterOverviewMode();
diff --git a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.cc b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.cc
index 7d56933..ae2d811 100644
--- a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.cc
+++ b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.cc
@@ -9,44 +9,19 @@
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_util.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/engine/nigori/nigori.h"
 
 namespace {
 
-// TODO(crbug.com/1233545): Move common parts of Ash and Lacros implementation
-// somewhere under components/ and reuse them (mojo utils, test helpers, maybe
-// observer of the sync service).
 crosapi::mojom::AccountKeyPtr GetMojoAccountKey(
     const syncer::SyncService& sync_service) {
   return account_manager::ToMojoAccountKey(account_manager::AccountKey(
       sync_service.GetAccountInfo().gaia, account_manager::AccountType::kGaia));
 }
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojo(const syncer::Nigori& nigori) {
-  std::string deprecated_user_key;
-  std::string encryption_key;
-  std::string mac_key;
-  nigori.ExportKeys(&deprecated_user_key, &encryption_key, &mac_key);
-
-  crosapi::mojom::NigoriKeyPtr mojo_result = crosapi::mojom::NigoriKey::New();
-  mojo_result->encryption_key =
-      std::vector<uint8_t>(encryption_key.begin(), encryption_key.end());
-  mojo_result->mac_key = std::vector<uint8_t>(mac_key.begin(), mac_key.end());
-  return mojo_result;
-}
-
-std::unique_ptr<syncer::Nigori> NigoriFromMojo(
-    const crosapi::mojom::NigoriKey& mojo_key) {
-  const std::string encryption_key(mojo_key.encryption_key.begin(),
-                                   mojo_key.encryption_key.end());
-  const std::string mac_key(mojo_key.mac_key.begin(), mojo_key.mac_key.end());
-  // |user_key| is deprecated, it's safe to pass empty string.
-  return syncer::Nigori::CreateByImport(
-      /*user_key=*/std::string(), encryption_key, mac_key);
-}
-
 bool IsPassphraseAvailable(const syncer::SyncService& sync_service) {
   return sync_service.GetUserSettings()->IsUsingExplicitPassphrase() &&
          !sync_service.GetUserSettings()->IsPassphraseRequired();
@@ -54,11 +29,8 @@
 
 }  // namespace
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojoForTesting(  // IN-TEST
-    const syncer::Nigori& nigori) {
-  return NigoriToMojo(nigori);
-}
-
+// TODO(crbug.com/1233545): Consider sharing sync service observer logic between
+// Ash and Lacros.
 SyncExplicitPassphraseClientLacros::LacrosSyncServiceObserver::
     LacrosSyncServiceObserver(
         syncer::SyncService* sync_service,
@@ -208,7 +180,7 @@
     return;
   }
   remote_->SetDecryptionNigoriKey(GetMojoAccountKey(*sync_service_),
-                                  NigoriToMojo(*decryption_key));
+                                  syncer::NigoriToMojo(*decryption_key));
 }
 
 void SyncExplicitPassphraseClientLacros::OnQueryDecryptionKeyFromAshCompleted(
@@ -218,7 +190,8 @@
     return;
   }
 
-  std::unique_ptr<syncer::Nigori> nigori_key = NigoriFromMojo(*mojo_nigori_key);
+  std::unique_ptr<syncer::Nigori> nigori_key =
+      syncer::NigoriFromMojo(*mojo_nigori_key);
   if (!nigori_key) {
     // Deserialization failed, |mojo_nigori_key| doesn't represent an actual
     // Nigori key.
diff --git a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.h b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.h
index f4a0044..47da51d 100644
--- a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.h
+++ b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros.h
@@ -14,7 +14,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace syncer {
-class Nigori;
 class SyncService;
 }  // namespace syncer
 
@@ -113,7 +112,4 @@
   mojo::Remote<crosapi::mojom::SyncExplicitPassphraseClient> remote_;
 };
 
-crosapi::mojom::NigoriKeyPtr NigoriToMojoForTesting(
-    const syncer::Nigori& nigori);
-
 #endif  // CHROME_BROWSER_LACROS_SYNC_SYNC_EXPLICIT_PASSPHRASE_CLIENT_LACROS_H_
diff --git a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros_unittest.cc b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros_unittest.cc
index 11564b0..982b811b 100644
--- a/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros_unittest.cc
+++ b/chrome/browser/lacros/sync/sync_explicit_passphrase_client_lacros_unittest.cc
@@ -10,6 +10,7 @@
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_util.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
 #include "components/sync/driver/mock_sync_service.h"
 #include "components/sync/driver/sync_service_observer.h"
 #include "components/sync/driver/sync_user_settings_mock.h"
@@ -33,7 +34,7 @@
 
 crosapi::mojom::NigoriKeyPtr MakeTestMojoNigoriKey() {
   std::unique_ptr<syncer::Nigori> nigori_key = MakeTestNigoriKey();
-  return NigoriToMojoForTesting(*nigori_key);
+  return syncer::NigoriToMojo(*nigori_key);
 }
 
 crosapi::mojom::AccountKeyPtr MakeMojoAccountKey(
diff --git a/chrome/browser/lacros/tablet_mode_lacros_browsertest.cc b/chrome/browser/lacros/tablet_mode_lacros_browsertest.cc
index 13ed89c..91aca4a 100644
--- a/chrome/browser/lacros/tablet_mode_lacros_browsertest.cc
+++ b/chrome/browser/lacros/tablet_mode_lacros_browsertest.cc
@@ -26,7 +26,9 @@
   ASSERT_TRUE(lacros_service->IsAvailable<crosapi::mojom::TestController>());
   // This test requires the tablet mode API.
   if (lacros_service->GetInterfaceVersion(
-          crosapi::mojom::TestController::Uuid_) < 2) {
+          crosapi::mojom::TestController::Uuid_) <
+      static_cast<int>(crosapi::mojom::TestController::MethodMinVersions::
+                           kEnterTabletModeMinVersion)) {
     LOG(WARNING) << "Unsupported ash version.";
     return;
   }
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
index 1b23085c..6bebc627 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
@@ -53,6 +53,15 @@
     private static final String LOCAL_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM =
             "PasswordManager.CredentialManager.LocalProfile.Launch.Success";
 
+    private static final String PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Latency";
+    private static final String PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Success";
+    private static final String PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Error";
+    private static final String PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.Launch.Success";
+
     /**
      * Launches the password settings or, if available, the credential manager from Google Play
      * Services.
@@ -92,8 +101,17 @@
         Optional<String> account = hasChosenToSyncPasswords(syncService)
                 ? Optional.of(CoreAccountInfo.getEmailFrom(syncService.getAccountInfo()))
                 : Optional.absent();
-        checkupClient.getPasswordCheckupPendingIntent(
-                referrer, account, PasswordManagerHelper::launchIntent, (error) -> {});
+        long startTimeMs = SystemClock.elapsedRealtime();
+        checkupClient.getPasswordCheckupPendingIntent(referrer, account,
+                (intent)
+                        -> PasswordManagerHelper.launchPasswordCheckup(intent, startTimeMs),
+                (error) -> {
+                    RecordHistogram.recordBooleanHistogram(
+                            PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM, false);
+                    RecordHistogram.recordEnumeratedHistogram(
+                            PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM, error,
+                            CredentialManagerError.COUNT);
+                });
     }
 
     /**
@@ -214,6 +232,17 @@
                 launchIntentSuccessfully);
     }
 
+    private static void launchPasswordCheckup(PendingIntent intent, long startTimeMs) {
+        RecordHistogram.recordTimesHistogram(PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM,
+                SystemClock.elapsedRealtime() - startTimeMs);
+        RecordHistogram.recordBooleanHistogram(PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM, true);
+
+        boolean launchIntentSuccessfully = launchIntent(intent);
+        RecordHistogram.recordBooleanHistogram(
+                PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM,
+                launchIntentSuccessfully);
+    }
+
     private static void recordSuccessMetrics(long elapsedTimeMs, boolean forAccount) {
         final String kGetIntentLatencyHistogram = forAccount ? ACCOUNT_GET_INTENT_LATENCY_HISTOGRAM
                                                              : LOCAL_GET_INTENT_LATENCY_HISTOGRAM;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index af1d259..9595530d 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -75,6 +75,15 @@
     private static final String LOCAL_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM =
             "PasswordManager.CredentialManager.LocalProfile.Launch.Success";
 
+    private static final String PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Latency";
+    private static final String PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Success";
+    private static final String PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.GetIntent.Error";
+    private static final String PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM =
+            "PasswordManager.PasswordCheckup.Launch.Success";
+
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
 
@@ -389,6 +398,74 @@
         verify(mPendingIntentMock).send();
     }
 
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testRecordsSuccessMetricsForPasswordCheckupIntent() {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+
+        PasswordManagerHelper.showPasswordCheckup(PasswordCheckReferrer.SAFETY_CHECK,
+                mPasswordCheckupClientHelperMock, mSyncServiceMock);
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM, 0));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM, 1));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 1));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testRecordsErrorMetricsForPasswordCheckupIntent() {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+        returnErrorWhenFetchingIntentForPasswordCheckup(CredentialManagerError.API_ERROR);
+
+        PasswordManagerHelper.showPasswordCheckup(PasswordCheckReferrer.SAFETY_CHECK,
+                mPasswordCheckupClientHelperMock, mSyncServiceMock);
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM,
+                        CredentialManagerError.API_ERROR));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM, 0));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testRecordsMetricsWhenPasswordCheckupIntentFails() throws CanceledException {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        doThrow(CanceledException.class).when(mPendingIntentMock).send();
+
+        PasswordManagerHelper.showPasswordCheckup(PasswordCheckReferrer.SAFETY_CHECK,
+                mPasswordCheckupClientHelperMock, mSyncServiceMock);
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_LATENCY_HISTOGRAM, 0));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_SUCCESS_HISTOGRAM, 1));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PASSWORD_CHECKUP_GET_INTENT_ERROR_HISTOGRAM));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 0));
+    }
+
     private void chooseToSyncPasswordsWithoutCustomPassphrase() {
         when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
         when(mSyncServiceMock.getChosenDataTypes())
@@ -452,4 +529,17 @@
                 .getCredentialManagerIntentForLocal(eq(ManagePasswordsReferrer.CHROME_SETTINGS),
                         any(Callback.class), any(Callback.class));
     }
+
+    private void returnErrorWhenFetchingIntentForPasswordCheckup(
+            @CredentialManagerError int error) {
+        doAnswer(invocation -> {
+            Callback<Integer> cb = invocation.getArgument(3);
+            cb.onResult(error);
+            return true;
+        })
+                .when(mPasswordCheckupClientHelperMock)
+                .getPasswordCheckupPendingIntent(eq(PasswordCheckReferrer.SAFETY_CHECK),
+                        eq(Optional.of(TEST_EMAIL_ADDRESS)), any(Callback.class),
+                        any(Callback.class));
+    }
 }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 231b5d6..41ee2f87 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -955,7 +955,11 @@
 
   if (content_type_->SupportsGroup(
           ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER) &&
-      params_.misspelled_word.empty()) {
+      params_.misspelled_word.empty() &&
+      (params_.page_url !=
+           chrome::GetSettingsUrl(chrome::kPasswordManagerSubPage) &&
+       params_.page_url !=
+           chrome::GetSettingsUrl(chrome::kPasswordCheckSubPage))) {
     AppendSearchProvider();
   }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index fc003e4..3048972 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -738,6 +739,34 @@
   EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_SAVELINKAS));
 }
 
+// Verify that item "Search web for" on password Manager is not present
+TEST_F(RenderViewContextMenuPrefsTest,
+       SearchWebForOptionOnPasswordsManagerIsDisabled) {
+  content::ContextMenuParams params =
+      CreateParams(MenuItem::SELECTION | MenuItem::EDITABLE);
+  params.page_url = chrome::GetSettingsUrl(chrome::kPasswordManagerSubPage);
+  auto menu = std::make_unique<TestRenderViewContextMenu>(
+      *web_contents()->GetMainFrame(), params);
+  menu->set_selection_navigation_url(GURL("https://www.foo.com/"));
+  menu->Init();
+
+  EXPECT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_SEARCHWEBFOR));
+}
+
+// Verify that item "Search web for" on password check is not present
+TEST_F(RenderViewContextMenuPrefsTest,
+       SearchWebForOptionOnPasswordCheckIsDisabled) {
+  content::ContextMenuParams params =
+      CreateParams(MenuItem::SELECTION | MenuItem::EDITABLE);
+  params.page_url = chrome::GetSettingsUrl(chrome::kPasswordCheckSubPage);
+  auto menu = std::make_unique<TestRenderViewContextMenu>(
+      *web_contents()->GetMainFrame(), params);
+  menu->set_selection_navigation_url(GURL("https://www.foo.com/"));
+  menu->Init();
+
+  EXPECT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_SEARCHWEBFOR));
+}
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 // Verify that the Lens Region Search menu item is displayed when the feature
 // is enabled.
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index 6ad00c06..add9011 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -68,6 +68,7 @@
     "account_manager/components:closure_compile",
     "add_supervision:closure_compile",
     "arc_account_picker:closure_compile",
+    "assistant_optin:closure_compile",
     "bluetooth_pairing_dialog:closure_compile",
     "crostini_installer:closure_compile",
     "crostini_upgrader:closure_compile",
diff --git a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
index aa25e708..3d8a6c28 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
+++ b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
@@ -3,10 +3,13 @@
 # found in the LICENSE file.
 
 import("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("//ui/webui/resources/tools/js_modulizer.gni")
+import("//tools/polymer/polymer.gni")
+import("./modulization_utils.gni")
 
 assert(is_chromeos_ash, "Only available in ash Chrome.")
 
@@ -101,12 +104,66 @@
   in_files = [ "browser_proxy.m.js" ]
 }
 
+#############################
+#### CLOSURE COMPILATION ####
+#############################
+
+js_type_check("closure_compile") {
+  is_polymer3 = true
+  closure_flags = default_closure_args
+  deps = [
+    #":assistant_voice_match.m",
+    ":voice_match_entry.m",
+  ]
+}
+
+js_library("voice_match_entry.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.m.js" ]
+  deps = [
+    "../login/components/behaviors:oobe_i18n_behavior.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+  extra_deps = [ ":voice_match_entry_module" ]
+}
+
+js_library("assistant_voice_match.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.m.js" ]
+  deps = [
+    "../login/components/behaviors:oobe_i18n_behavior.m",
+    "../login/components/behaviors:multi_step_behavior.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:load_time_data.m",
+    "//ui/webui/resources/js:util.m",
+  ]
+  extra_deps = [ ":assistant_voice_match_module" ]
+}
+
 ##########################
 #### POLYMER3 MODULES ####
 ##########################
 
 group("polymer3_elements") {
-  public_deps = [ ":modulize" ]
+  public_deps = [
+    ":assistant_voice_match_module",
+    ":voice_match_entry_module",
+    ":modulize"
+  ]
+}
+
+polymer_modulizer("assistant_voice_match") {
+  js_file = "assistant_voice_match.js"
+  html_file = "assistant_voice_match.html"
+  html_type = "dom-module"
+  auto_imports = assistant_auto_imports
+  namespace_rewrites = assistant_namespace_rewrites
+}
+
+polymer_modulizer("voice_match_entry") {
+  js_file = "voice_match_entry.js"
+  html_file = "voice_match_entry.html"
+  html_type = "dom-module"
+  auto_imports = assistant_auto_imports
+  namespace_rewrites = assistant_namespace_rewrites
 }
 
 js_modulizer("modulize") {
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
index 3f665c47..33eaa00b 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
@@ -6,16 +6,16 @@
 <include src="assistant_loading.html">
 <include src="assistant_related_info.html">
 <include src="assistant_value_prop.html">
-<include src="assistant_voice_match.html">
 <include src="icon.html">
 <include src="setting_zippy.html">
-<include src="voice_match_entry.html">
 
 <link rel="import" href="/components/common_styles/common_styles.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="/assistant_optin/assistant_voice_match.html">
+
 <dom-module id="assistant-optin-flow-element">
   <template>
     <style include="oobe-common-styles assistant-common-styles"></style>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
index 3781810a..0801112 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
@@ -4,12 +4,9 @@
 
 // <include src="utils.js">
 // <include src="setting_zippy.js">
-// <include src="voice_match_entry.js">
-// <include src="browser_proxy.js">
 // <include src="assistant_loading.js">
 // <include src="assistant_related_info.js">
 // <include src="assistant_value_prop.js">
-// <include src="assistant_voice_match.js">
 
 /**
  * @fileoverview Polymer element for displaying material design assistant
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
index f97bc841..ec9b94e 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
@@ -4,17 +4,22 @@
 
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="chrome://resources/html/util.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.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_next_button.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_cr_lottie.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_next_button.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_cr_lottie.html">
+
+<link rel="import" href="./voice_match_entry.html">
+<link rel="import" href="./browser_proxy.html">
 
 <dom-module id="assistant-voice-match">
   <template>
@@ -149,4 +154,5 @@
       </div>
     </oobe-adaptive-dialog>
   </template>
+  <script src="assistant_voice_match.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
index 56f798ed..6872476 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
@@ -2,6 +2,13 @@
 // 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 assistant
+ * voice match screen.
+ */
+
+/* #js_imports_placeholder */
+
 /** Maximum recording index. */
 const MAX_INDEX = 4;
 
@@ -19,62 +26,78 @@
 };
 
 /**
- * @fileoverview Polymer element for displaying material design assistant
- * voice match screen.
- *
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {MultiStepBehaviorInterface}
  */
-Polymer({
-  is: 'assistant-voice-match',
+const AssistantVoiceMatchBase = Polymer.mixinBehaviors(
+    [OobeI18nBehavior, MultiStepBehavior], Polymer.Element);
 
-  behaviors: [OobeI18nBehavior, MultiStepBehavior],
+/**
+ * @polymer
+ */
+class AssistantVoiceMatch extends AssistantVoiceMatchBase {
+  static get is() {
+    return `assistant-voice-match`;
+  }
 
-  properties: {
-    /**
-     * Indicates whether to use same design for accept/decline buttons.
-     */
-    equalWeightButtons_: {
-      type: Boolean,
-      value: false,
-    },
+  /* #html_template_placeholder */
+
+  static get properties() {
+    return {
+      /**
+       * Indicates whether to use same design for accept/decline buttons.
+       */
+      equalWeightButtons_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * The given name of the user, if a child account is in use; otherwise,
+       * this is an empty string.
+       */
+      childName_: {
+        type: String,
+        value: '',
+      },
+    };
+  }
+
+  constructor() {
+    super();
 
     /**
-     * The given name of the user, if a child account is in use; otherwise,
-     * this is an empty string.
+     * Whether voice match is the first screen of the flow.
+     * @type {boolean}
      */
-    childName_: {
-      type: String,
-      value: '',
-    },
-  },
+    this.isFirstScreen = false;
 
-  /**
-   * Whether voice match is the first screen of the flow.
-   * @type {boolean}
-   */
-  isFirstScreen: false,
+    /**
+     * Current recording index.
+     * @type {number}
+     * @private
+     */
+    this.currentIndex_ = 0;
 
-  /**
-   * Current recording index.
-   * @type {number}
-   * @private
-   */
-  currentIndex_: 0,
+    /**
+     * The delay in ms between speaker ID enrollment finishes and the
+     * voice-match-done action is reported to chrome.
+     * @private {number}
+     */
+    this.doneActionDelayMs_ = 3000;
 
-  /**
-   * The delay in ms between speaker ID enrollment finishes and the
-   * voice-match-done action is reported to chrome.
-   * @private {number}
-   */
-  doneActionDelayMs_: 3000,
-
-  /** @private {?assistant.BrowserProxy} */
-  browserProxy_: null,
+    /** @private {?assistant.BrowserProxy} */
+    this.browserProxy_ = assistant.BrowserProxyImpl.getInstance();
+  }
 
   defaultUIStep() {
     return VoiceMatchUIState.INTRO;
-  },
+  }
 
-  UI_STEPS: VoiceMatchUIState,
+  get UI_STEPS() {
+    return VoiceMatchUIState;
+  }
 
   /**
    * Overrides the default delay for sending voice-match-done action.
@@ -82,7 +105,7 @@
    */
   setDoneActionDelayForTesting(delay) {
     this.doneActionDelayMs_ = delay;
-  },
+  }
 
   /**
    * On-tap event handler for skip button.
@@ -92,7 +115,7 @@
   onSkipTap_() {
     this.$['voice-match-lottie'].playing = false;
     this.browserProxy_.userActed(VOICE_MATCH_SCREEN_ID, ['skip-pressed']);
-  },
+  }
 
   /**
    * On-tap event handler for agree button.
@@ -101,9 +124,10 @@
    */
   onAgreeTap_() {
     this.setUIStep(VoiceMatchUIState.RECORDING);
-    this.fire('loading');
+    this.dispatchEvent(
+        new CustomEvent('loading', {bubbles: true, composed: true}));
     this.browserProxy_.userActed(VOICE_MATCH_SCREEN_ID, ['record-pressed']);
-  },
+  }
 
   /**
    * Reset the status of page elements.
@@ -117,17 +141,12 @@
     this.$['later-button'].hidden = false;
     this.$['loading-animation'].hidden = true;
 
-    for (var i = 0; i < MAX_INDEX; ++i) {
-      var entry = this.$['voice-entry-' + i];
+    for (let i = 0; i < MAX_INDEX; ++i) {
+      let entry = this.$['voice-entry-' + i];
       entry.removeAttribute('active');
       entry.removeAttribute('completed');
     }
-  },
-
-  /** @override */
-  created() {
-    this.browserProxy_ = assistant.BrowserProxyImpl.getInstance();
-  },
+  }
 
   /**
    * Reload the page with the given settings data.
@@ -135,7 +154,7 @@
   reloadContent(data) {
     this.equalWeightButtons_ = data['equalWeightButtons'];
     this.childName_ = data['childName'];
-  },
+  }
 
   /**
    * Reloads voice match flow.
@@ -145,29 +164,31 @@
     this.$['agree-button'].focus();
     this.resetElements_();
     this.browserProxy_.userActed(VOICE_MATCH_SCREEN_ID, ['reload-requested']);
-    this.fire('loaded');
-  },
+    this.dispatchEvent(
+        new CustomEvent('loaded', {bubbles: true, composed: true}));
+  }
 
   /**
    * Called when the server is ready to listening for hotword.
    */
   listenForHotword() {
     if (this.currentIndex_ == 0) {
-      this.fire('loaded');
+      this.dispatchEvent(
+          new CustomEvent('loaded', {bubbles: true, composed: true}));
       announceAccessibleMessage(
           loadTimeData.getString('assistantVoiceMatchRecording'));
       announceAccessibleMessage(
           loadTimeData.getString('assistantVoiceMatchA11yMessage'));
     }
-    var currentEntry = this.$['voice-entry-' + this.currentIndex_];
+    let currentEntry = this.$['voice-entry-' + this.currentIndex_];
     currentEntry.setAttribute('active', true);
-  },
+  }
 
   /**
    * Called when the server has detected and processing hotword.
    */
   processingHotword() {
-    var currentEntry = this.$['voice-entry-' + this.currentIndex_];
+    let currentEntry = this.$['voice-entry-' + this.currentIndex_];
     currentEntry.removeAttribute('active');
     currentEntry.setAttribute('completed', true);
     this.currentIndex_++;
@@ -181,10 +202,11 @@
       announceAccessibleMessage(
           loadTimeData.getString('assistantVoiceMatchComplete'));
     }
-  },
+  }
 
   voiceMatchDone() {
-    this.fire('loaded');
+    this.dispatchEvent(
+        new CustomEvent('loaded', {bubbles: true, composed: true}));
     announceAccessibleMessage(
         loadTimeData.getString('assistantVoiceMatchCompleted'));
     if (this.currentIndex_ != MAX_INDEX) {
@@ -199,7 +221,7 @@
       this.$['voice-match-lottie'].playing = false;
       this.browserProxy_.userActed(VOICE_MATCH_SCREEN_ID, ['voice-match-done']);
     }, this.doneActionDelayMs_);
-  },
+  }
 
   /**
    * Signal from host to show the screen.
@@ -208,15 +230,19 @@
     if (this.isFirstScreen) {
       // If voice match is the first screen, slightly delay showing the content
       // for the lottie animations to load.
-      this.fire('loading');
-      window.setTimeout(() => this.fire('loaded'), 100);
+      this.dispatchEvent(
+          new CustomEvent('loading', {bubbles: true, composed: true}));
+      window.setTimeout(() => {
+        this.dispatchEvent(
+            new CustomEvent('loaded', {bubbles: true, composed: true}));
+      }, 100);
     }
 
     this.browserProxy_.screenShown(VOICE_MATCH_SCREEN_ID);
     this.$['voice-match-lottie'].playing = true;
     Polymer.RenderStatus.afterNextRender(
         this, () => this.$['agree-button'].focus());
-  },
+  }
 
   /**
    * Returns the text for dialog title.
@@ -233,7 +259,7 @@
     } else if (uiStep === VoiceMatchUIState.COMPLETED) {
       return this.i18n('assistantVoiceMatchCompleted');
     }
-  },
+  }
 
   /**
    * Returns the text for subtitle.
@@ -250,5 +276,7 @@
       return this.i18nAdvanced(
           'assistantVoiceMatchFooterForChild', {substitutions: [childName]});
     }
-  },
-});
+  }
+}
+
+customElements.define(AssistantVoiceMatch.is, AssistantVoiceMatch);
diff --git a/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni b/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni
new file mode 100644
index 0000000..15e8da9
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni
@@ -0,0 +1,24 @@
+# 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.
+
+# This file contains directives that assists the Polymer modulizer.
+# For an explanation, see ../login/oobe_auto_imports.gni
+
+assistant_auto_imports = [
+  # Note that the path below does not contain 'login' after 'chromeos'. This is needed in order
+  # to overcome a limitation of the Polymer moduliser tooling when the folder structure does not
+  # perfectly match the resource paths used in the WebUI.
+  "chrome/browser/resources/chromeos/components/behaviors/oobe_i18n_behavior.html|OobeI18nBehavior,OobeI18nBehaviorInterface",
+  "chrome/browser/resources/chromeos/components/behaviors/multi_step_behavior.html|MultiStepBehavior,MultiStepBehaviorInterface",
+  "chrome/browser/resources/chromeos/assistant_optin/browser_proxy.html|BrowserProxyImpl",
+  "ui/webui/resources/html/polymer.html|afterNextRender,Polymer,PolymerElement,html,mixinBehaviors",
+  "ui/webui/resources/html/util.html|announceAccessibleMessage",
+]
+
+assistant_namespace_rewrites = [
+  "assistant.BrowserProxyImpl|BrowserProxyImpl",
+  "assistant.BrowserProxy|BrowserProxy",
+  "Polymer.mixinBehaviors|mixinBehaviors",
+  "Polymer.Element|PolymerElement",
+]
diff --git a/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.html b/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.html
index d148f04..bf7433e 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.html
@@ -2,10 +2,14 @@
      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/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="/components/common_styles/common_styles.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
+
+<link rel="import" href="../components/behaviors/oobe_i18n_behavior.html">
+<link rel="import" href="../components/common_styles/common_styles.html">
 
 <dom-module id="voice-match-entry">
   <template>
@@ -85,4 +89,5 @@
       </div>
     </div>
   </template>
+  <script src="voice_match_entry.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.js b/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.js
index 2c26462..5648c41 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.js
@@ -2,20 +2,38 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-Polymer({
-  is: 'voice-match-entry',
+/* #js_imports_placeholder */
 
-  behaviors: [OobeI18nBehavior],
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ */
+ const VoiceMatchEntryBase = Polymer.mixinBehaviors(
+  [OobeI18nBehavior],
+  Polymer.Element);
 
-  properties: {
-    active: {
-      type: Boolean,
-      value: false,
-    },
+/**
+ * @polymer
+ */
+class VoiceMatchEntry extends VoiceMatchEntryBase {
 
-    completed: {
-      type: Boolean,
-      value: false,
-    },
-  },
-});
+  static get is() { return 'voice-match-entry'; }
+
+  /* #html_template_placeholder */
+
+  static get properties() {
+    return {
+      active: {
+        type: Boolean,
+        value: false,
+      },
+
+      completed: {
+        type: Boolean,
+        value: false,
+      },
+    };
+  }
+}
+
+customElements.define(VoiceMatchEntry.is, VoiceMatchEntry);
diff --git a/chrome/browser/resources/profiles/profile_internals.html b/chrome/browser/resources/profiles/profile_internals.html
new file mode 100644
index 0000000..8e03670
--- /dev/null
+++ b/chrome/browser/resources/profiles/profile_internals.html
@@ -0,0 +1 @@
+Under construction.
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 752acdc8..4edd5741 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -1472,6 +1472,9 @@
   if (IsInPasswordAlertMode(password_type)) {
     return RequestOutcome::PASSWORD_ALERT_MODE;
   }
+  if (url != GURL("about:blank") && !CanGetReputationOfURL(url)) {
+    return RequestOutcome::URL_NOT_VALID_FOR_REPUTATION_COMPUTING;
+  }
   return RequestOutcome::DISABLED_DUE_TO_USER_POPULATION;
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 4d451c2..fc7eb34 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -1443,6 +1443,17 @@
               service_->GetPingNotSentReason(
                   LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                   GURL("about:blank"), reused_password_type));
+    profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
+                                      PASSWORD_PROTECTION_OFF);
+  }
+  {
+    // Internal URL
+    ReusedPasswordAccountType reused_password_type;
+    service_->ConfigService(false /*incognito*/, true /*SBER*/);
+    EXPECT_EQ(RequestOutcome::URL_NOT_VALID_FOR_REPUTATION_COMPUTING,
+              service_->GetPingNotSentReason(
+                  LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                  GURL("http://192.168.1.1/"), reused_password_type));
   }
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc
index 11c9573..81b74f1d 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc
@@ -786,8 +786,7 @@
   EventReportValidator validator(client());
   validator.ExpectDangerousDeepScanningResult(
       /*url*/ url.spec(),
-      /*filename*/
-      (*download_items().begin())->GetTargetFilePath().AsUTF8Unsafe(),
+      /*filename*/ "zipfile_two_archives.zip",
       // sha256sum chrome/test/data/safe_browsing/download_protection/\
       // zipfile_two_archives.zip |  tr '[:lower:]' '[:upper:]'
       /*sha*/
@@ -880,8 +879,7 @@
   EventReportValidator validator(client());
   validator.ExpectSensitiveDataEventAndDangerousDeepScanningResult(
       /*url*/ url.spec(),
-      /*filename*/
-      (*download_items().begin())->GetTargetFilePath().AsUTF8Unsafe(),
+      /*filename*/ "zipfile_two_archives.zip",
       // sha256sum chrome/test/data/safe_browsing/download_protection/\
       // zipfile_two_archives.zip |  tr '[:lower:]' '[:upper:]'
       /*sha*/
@@ -975,7 +973,10 @@
                                      "application/x-zip-compressed"};
   validator.ExpectDangerousDownloadEvent(
       /*url*/ url.spec(),
-      (*download_items().begin())->GetTargetFilePath().AsUTF8Unsafe(),
+      (*download_items().begin())
+          ->GetTargetFilePath()
+          .BaseName()
+          .AsUTF8Unsafe(),
       // sha256sum chrome/test/data/safe_browsing/download_protection/\
       // zipfile_two_archives.zip |  tr '[:lower:]' '[:upper:]'
       /*sha*/
@@ -1260,8 +1261,7 @@
 
     validator.ExpectDangerousDeepScanningResult(
         /*url*/ url.spec(),
-        /*filename*/
-        (*download_items().begin())->GetTargetFilePath().AsUTF8Unsafe(),
+        /*filename*/ "zipfile_two_archives.zip",
         // sha256sum chrome/test/data/safe_browsing/download_protection/\
         // zipfile_two_archives.zip |  tr '[:lower:]' '[:upper:]'
         /*sha*/
@@ -1593,7 +1593,7 @@
   std::set<std::string> mimetypes = {"text/plain"};
   validator.ExpectSensitiveDataEvent(
       /*url*/ url.spec(),
-      /*filename*/ main_file.AsUTF8Unsafe(),
+      /*filename*/ "text.htm",
       // sha256sum chrome/test/data/save_page/text.txt | tr a-f A-F
       "9789A2E12D50EFA4B891D4EF95C5189FA4C98E34C84E1F8017CD8F574CA035DD",
       /*trigger*/
@@ -1657,7 +1657,7 @@
   std::set<std::string> mimetypes = {"text/plain"};
   validator.ExpectSensitiveDataEvent(
       /*url*/ url.spec(),
-      /*filename*/ main_file.AsUTF8Unsafe(),
+      /*filename*/ "text.htm",
       // sha256sum chrome/test/data/save_page/text.txt | tr a-f A-F
       "9789A2E12D50EFA4B891D4EF95C5189FA4C98E34C84E1F8017CD8F574CA035DD",
       /*trigger*/
@@ -1690,7 +1690,7 @@
   // download and move the file to its final destination.
   validator.ExpectSensitiveDataEvent(
       /*url*/ url.spec(),
-      /*filename*/ main_file.AsUTF8Unsafe(),
+      /*filename*/ "text.htm",
       // sha256sum chrome/test/data/save_page/text.txt | tr a-f A-F
       "9789A2E12D50EFA4B891D4EF95C5189FA4C98E34C84E1F8017CD8F574CA035DD",
       /*trigger*/
@@ -1754,7 +1754,7 @@
   std::set<std::string> mimetypes = {"text/plain"};
   validator.ExpectSensitiveDataEvent(
       /*url*/ url.spec(),
-      /*filename*/ main_file.AsUTF8Unsafe(),
+      /*filename*/ "text.htm",
       // sha256sum chrome/test/data/save_page/text.txt | tr a-f A-F
       "9789A2E12D50EFA4B891D4EF95C5189FA4C98E34C84E1F8017CD8F574CA035DD",
       /*trigger*/
@@ -1849,7 +1849,7 @@
   std::set<std::string> mimetypes = {"text/plain"};
   validator.ExpectSensitiveDataEvent(
       /*url*/ url.spec(),
-      /*filename*/ main_file.AsUTF8Unsafe(),
+      /*filename*/ "text.htm",
       // sha256sum chrome/test/data/save_page/text.txt | tr a-f A-F
       "9789A2E12D50EFA4B891D4EF95C5189FA4C98E34C84E1F8017CD8F574CA035DD",
       /*trigger*/
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
index e5aa001c..aa6197b 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
@@ -660,7 +660,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectDangerousDeepScanningResultAndSensitiveDataEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]'
         // '[:upper:]'
         /*sha256*/
@@ -725,7 +725,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectDangerousDeepScanningResultAndSensitiveDataEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]'
         // '[:upper:]'
         /*sha256*/
@@ -782,7 +782,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectSensitiveDataEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -837,7 +837,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectSensitiveDataEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -896,7 +896,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectSensitiveDataEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -945,7 +945,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -995,7 +995,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -1050,7 +1050,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -1178,7 +1178,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ secondary_files_targets_[0].AsUTF8Unsafe(),
+        /*filename*/ secondary_files_targets_[0].BaseName().AsUTF8Unsafe(),
         // printf "foo.txt" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "DDAB29FF2C393EE52855D21A240EB05F775DF88E3CE347DF759F0C4B80356C35",
@@ -1264,8 +1264,8 @@
     validator.ExpectSensitiveDataEvents(
         /*url*/ "https://example.com/download.exe",
         {
-            secondary_files_targets_[0].AsUTF8Unsafe(),
-            secondary_files_targets_[1].AsUTF8Unsafe(),
+            secondary_files_targets_[0].BaseName().AsUTF8Unsafe(),
+            secondary_files_targets_[1].BaseName().AsUTF8Unsafe(),
         },
         // printf "foo.txt" | sha256sum |  tr '[:lower:]' '[:upper:]'
         // printf "bar.txt" | sha256sum |  tr '[:lower:]' '[:upper:]'
@@ -1323,7 +1323,7 @@
   EventReportValidator validator(client_.get());
   validator.ExpectUnscannedFileEvent(
       /*url*/ "https://example.com/download.exe",
-      /*filename*/ download_path_.AsUTF8Unsafe(),
+      /*filename*/ "download.exe",
       // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
       /*sha256*/
       "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -1430,7 +1430,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectDangerousDownloadEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]'
         // '[:upper:]'
         /*sha256*/
@@ -1484,7 +1484,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectDangerousDownloadEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]'
         // '[:upper:]'
         /*sha256*/
@@ -1530,7 +1530,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
@@ -1582,7 +1582,7 @@
     EventReportValidator validator(client_.get());
     validator.ExpectUnscannedFileEvent(
         /*url*/ "https://example.com/download.exe",
-        /*filename*/ download_path_.AsUTF8Unsafe(),
+        /*filename*/ "download.exe",
         // printf "download contents" | sha256sum |  tr '[:lower:]' '[:upper:]'
         /*sha256*/
         "76E00EB33811F5778A5EE557512C30D9341D4FEB07646BCE3E4DB13F9428573C",
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index 5a66c03..d484b7dc 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -2795,10 +2795,10 @@
   std::set<std::string> expected_mimetypes{""};
   EventReportValidator validator(client_.get());
   validator.ExpectDangerousDownloadEvent(
-      "",                          // URL, not set in this test
-      final_path_.AsUTF8Unsafe(),  // Full path, including the directory
-      "68617368",                  // SHA256 of the fake download
-      "DANGEROUS_FILE_TYPE",       // expected_threat_type
+      "",                     // URL, not set in this test
+      "a.exe",                // Simple filename without the directory
+      "68617368",             // SHA256 of the fake download
+      "DANGEROUS_FILE_TYPE",  // expected_threat_type
       extensions::SafeBrowsingPrivateEventRouter::
           kTriggerFileDownload,  // expected_trigger
       &expected_mimetypes,
@@ -2856,9 +2856,9 @@
   std::set<std::string> expected_mimetypes{"fake/mimetype"};
   EventReportValidator validator(client_.get());
   validator.ExpectSensitiveDataEvent(
-      "",                          // URL, not set in this test
-      final_path_.AsUTF8Unsafe(),  // Full path, including the directory
-      "68617368",                  // SHA256 of the fake download
+      "",          // URL, not set in this test
+      "a.exe",     // Simple filename without the directory
+      "68617368",  // SHA256 of the fake download
       extensions::SafeBrowsingPrivateEventRouter::
           kTriggerFileDownload,  // expected_trigger
       response.results()[0], &expected_mimetypes,
@@ -2903,10 +2903,10 @@
   std::set<std::string> expected_mimetypes{""};
   EventReportValidator validator(client_.get());
   validator.ExpectDangerousDownloadEvent(
-      "",                          // URL, not set in this test
-      final_path_.AsUTF8Unsafe(),  // Full path, including the directory
-      "68617368",                  // SHA256 of the fake download
-      "DANGEROUS_FILE_TYPE",       // expected_threat_type
+      "",                     // URL, not set in this test
+      "a.exe",                // Simple filename without the directory
+      "68617368",             // SHA256 of the fake download
+      "DANGEROUS_FILE_TYPE",  // expected_threat_type
       extensions::SafeBrowsingPrivateEventRouter::
           kTriggerFileDownload,  // expected_trigger
       &expected_mimetypes,
@@ -2960,9 +2960,9 @@
   std::set<std::string> expected_mimetypes{"fake/mimetype"};
   EventReportValidator validator(client_.get());
   validator.ExpectSensitiveDataEvent(
-      "",                          // URL, not set in this test
-      final_path_.AsUTF8Unsafe(),  // Full path, including the directory
-      "68617368",                  // SHA256 of the fake download
+      "",          // URL, not set in this test
+      "a.exe",     // Simple filename without the directory
+      "68617368",  // SHA256 of the fake download
       extensions::SafeBrowsingPrivateEventRouter::
           kTriggerFileDownload,  // expected_trigger
       response.results()[0], &expected_mimetypes,
@@ -3016,9 +3016,9 @@
   std::set<std::string> expected_mimetypes{"fake/mimetype"};
   EventReportValidator validator(client_.get());
   validator.ExpectSensitiveDataEvent(
-      "",                          // URL, not set in this test
-      final_path_.AsUTF8Unsafe(),  // Full path, including the directory
-      "68617368",                  // SHA256 of the fake download
+      "",          // URL, not set in this test
+      "a.exe",     // Simple filename without the directory
+      "68617368",  // SHA256 of the fake download
       extensions::SafeBrowsingPrivateEventRouter::
           kTriggerFileDownload,  // expected_trigger
       response.results()[0], &expected_mimetypes,
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
index f5c3ce9..e1b800c 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
@@ -17,25 +17,17 @@
 bool ShouldOfferFeature(content::WebContents* web_contents) {
   if (!web_contents)
     return false;
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  send_tab_to_self::SendTabToSelfSyncService* service =
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile);
 
-  GURL page_url = web_contents->GetURL();
-  content::NavigationEntry* navigation_entry =
-      web_contents->GetController().GetLastCommittedEntry();
-  bool has_last_entry = (navigation_entry != nullptr);
-
-  return ShouldOfferToShareUrl(service, page_url) && has_last_entry;
-}
-
-bool ShouldOfferToShareUrl(
-    SendTabToSelfSyncService* send_tab_to_self_sync_service,
-    const GURL& url) {
-  if (!url.SchemeIsHTTPOrHTTPS())
+  if (!web_contents->GetURL().SchemeIsHTTPOrHTTPS())
     return false;
 
+  if (!web_contents->GetController().GetLastCommittedEntry())
+    return false;
+
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  send_tab_to_self::SendTabToSelfSyncService* send_tab_to_self_sync_service =
+      SendTabToSelfSyncServiceFactory::GetForProfile(profile);
   if (!send_tab_to_self_sync_service) {
     // Can happen in incognito or guest profile.
     return false;
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util.h b/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
index dfac043..d70ff9a 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
@@ -5,24 +5,15 @@
 #ifndef CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_UTIL_H_
 #define CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_UTIL_H_
 
-class GURL;
-
 namespace content {
 class WebContents;
 }
 
 namespace send_tab_to_self {
 
-class SendTabToSelfSyncService;
-
 // Returns true if the feature should be offered in menus.
 bool ShouldOfferFeature(content::WebContents* web_contents);
 
-// |send_tab_to_self_sync_service| can be null (in incognito or guest profile).
-bool ShouldOfferToShareUrl(
-    SendTabToSelfSyncService* send_tab_to_self_sync_service,
-    const GURL& url);
-
 // Returns true if the omnibox icon for the feature should be offered.
 bool ShouldOfferOmniboxIcon(content::WebContents* web_contents);
 
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
index 782cc767..98429fc 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
@@ -55,49 +55,6 @@
   FakeSendTabToSelfModel model_;
 };
 
-class SendTabToSelfUtilTest : public testing::Test {
- public:
-  FakeSendTabToSelfSyncService* service() { return &service_; }
-
- private:
-  FakeSendTabToSelfSyncService service_;
-};
-
-TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureIfModelNotReady) {
-  service()->GetSendTabToSelfModel()->SetIsReady(false);
-  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(true);
-
-  EXPECT_FALSE(ShouldOfferToShareUrl(service(), GURL(kHttpsUrl)));
-}
-
-TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureIfHasNoValidTargetDevice) {
-  service()->GetSendTabToSelfModel()->SetIsReady(true);
-  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(false);
-
-  EXPECT_FALSE(ShouldOfferToShareUrl(service(), GURL(kHttpsUrl)));
-}
-
-TEST_F(SendTabToSelfUtilTest, ShouldOnlyOfferFeatureIfHttpOrHttps) {
-  service()->GetSendTabToSelfModel()->SetIsReady(true);
-  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(true);
-
-  EXPECT_TRUE(ShouldOfferToShareUrl(service(), GURL(kHttpsUrl)));
-  EXPECT_TRUE(ShouldOfferToShareUrl(service(), GURL(kHttpUrl)));
-  EXPECT_FALSE(ShouldOfferToShareUrl(service(), GURL("192.168.0.0")));
-  EXPECT_FALSE(
-      ShouldOfferToShareUrl(service(), GURL("chrome-untrusted://url")));
-  EXPECT_FALSE(ShouldOfferToShareUrl(service(), GURL("chrome://flags")));
-  EXPECT_FALSE(ShouldOfferToShareUrl(service(), GURL("tel:07399999999")));
-}
-
-TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureInIncognitoMode) {
-  // Note: if changing this, audit profile-finding logic in the feature.
-  // For example, NotificationManager.java in the Android code assumes
-  // incognito is not supported.
-  EXPECT_FALSE(ShouldOfferToShareUrl(/*send_tab_to_self_sync_service=*/nullptr,
-                                     GURL(kHttpsUrl)));
-}
-
 std::unique_ptr<KeyedService> BuildFakeSendTabToSelfSyncService(
     content::BrowserContext*) {
   auto service = std::make_unique<FakeSendTabToSelfSyncService>();
@@ -106,39 +63,103 @@
   return service;
 }
 
-class SendTabToSelfUtilTestWithWindow : public BrowserWithTestWindowTest {
+class SendTabToSelfUtilTest : public BrowserWithTestWindowTest {
  public:
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
 
-    // RegisterCreateServicesCallbackForTesting() should ideally be used
-    // here instead, but doesn't seem to work with BrowserWithTestWindowTest.
-    SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
-        profile(), base::BindRepeating(&BuildFakeSendTabToSelfSyncService));
+    AddTab(browser(), GURL("about:blank"));
+  }
+
+  TestingProfile::TestingFactories GetTestingFactories() override {
+    return {{SendTabToSelfSyncServiceFactory::GetInstance(),
+             base::BindRepeating(&BuildFakeSendTabToSelfSyncService)}};
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  FakeSendTabToSelfSyncService* service() {
+    return static_cast<FakeSendTabToSelfSyncService*>(
+        SendTabToSelfSyncServiceFactory::GetForProfile(profile()));
   }
 };
 
-TEST_F(SendTabToSelfUtilTestWithWindow,
-       ShouldNotOfferFeatureInOmniboxWhileNavigating) {
-  AddTab(browser(), GURL(kHttpsUrl));
+TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureIfModelNotReady) {
+  service()->GetSendTabToSelfModel()->SetIsReady(false);
+  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(true);
 
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
+  NavigateAndCommitActiveTab(GURL(kHttpsUrl));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+}
 
-  ASSERT_FALSE(web_contents->IsWaitingForResponse());
-  EXPECT_TRUE(ShouldOfferOmniboxIcon(web_contents));
+TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureIfHasNoValidTargetDevice) {
+  service()->GetSendTabToSelfModel()->SetIsReady(true);
+  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(false);
+
+  NavigateAndCommitActiveTab(GURL(kHttpsUrl));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+}
+
+TEST_F(SendTabToSelfUtilTest, ShouldOnlyOfferFeatureIfHttpOrHttps) {
+  service()->GetSendTabToSelfModel()->SetIsReady(true);
+  service()->GetSendTabToSelfModel()->SetHasValidTargetDevice(true);
+
+  NavigateAndCommitActiveTab(GURL(kHttpsUrl));
+  EXPECT_TRUE(ShouldOfferFeature(web_contents()));
+
+  NavigateAndCommitActiveTab(GURL(kHttpUrl));
+  EXPECT_TRUE(ShouldOfferFeature(web_contents()));
+
+  NavigateAndCommitActiveTab(GURL("192.168.0.0"));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+
+  NavigateAndCommitActiveTab(GURL("chrome-untrusted://url"));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+
+  NavigateAndCommitActiveTab(GURL("chrome://flags"));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+
+  NavigateAndCommitActiveTab(GURL("tel:07399999999"));
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+}
+
+TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureInIncognitoMode) {
+  // TODO(crbug.com/1313539): This isn't a great way to fake an off-the-record
+  // profile, but BrowserWithTestWindowTest lacks support. More concretely, this
+  // harness relies on TestingProfileManager, and the only fitting method there
+  // is broken (CreateGuestProfile()).
+  SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(),
+      base::BindRepeating(
+          [](content::BrowserContext*) -> std::unique_ptr<KeyedService> {
+            return nullptr;
+          }));
+
+  // Note: if changing this, audit profile-finding logic in the feature.
+  // For example, NotificationManager.java in the Android code assumes
+  // incognito is not supported.
+  EXPECT_FALSE(ShouldOfferFeature(web_contents()));
+}
+
+TEST_F(SendTabToSelfUtilTest, ShouldNotOfferFeatureInOmniboxWhileNavigating) {
+  NavigateAndCommitActiveTab(GURL(kHttpsUrl));
+
+  ASSERT_FALSE(web_contents()->IsWaitingForResponse());
+  EXPECT_TRUE(ShouldOfferOmniboxIcon(web_contents()));
 
   std::unique_ptr<content::NavigationSimulator> simulator =
       content::NavigationSimulator::CreateRendererInitiated(
-          GURL(kHttpsUrl2), web_contents->GetMainFrame());
+          GURL(kHttpsUrl2), web_contents()->GetMainFrame());
   simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
   simulator->Start();
-  ASSERT_TRUE(web_contents->IsWaitingForResponse());
-  EXPECT_FALSE(ShouldOfferOmniboxIcon(web_contents));
+  ASSERT_TRUE(web_contents()->IsWaitingForResponse());
+  EXPECT_FALSE(ShouldOfferOmniboxIcon(web_contents()));
 
   simulator->Commit();
-  ASSERT_FALSE(web_contents->IsWaitingForResponse());
-  EXPECT_TRUE(ShouldOfferOmniboxIcon(web_contents));
+  ASSERT_FALSE(web_contents()->IsWaitingForResponse());
+  EXPECT_TRUE(ShouldOfferOmniboxIcon(web_contents()));
 }
 
 }  // namespace
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index 0a5d61d..5306868 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -473,9 +473,11 @@
                         -> {
                     PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
                         ShoppingPersistedTabData shoppingPersistedTabData =
-                                ShoppingPersistedTabData.from(tab);
+                                tab.isDestroyed() ? null : ShoppingPersistedTabData.from(tab);
                         PostTask.postTask(TaskTraits.USER_BLOCKING_MAY_BLOCK, () -> {
-                            shoppingPersistedTabData.deserializeAndLog(data);
+                            if (shoppingPersistedTabData != null) {
+                                shoppingPersistedTabData.deserializeAndLog(data);
+                            }
                             PostTask.postTask(UiThreadTaskTraits.DEFAULT,
                                     () -> { factoryCallback.onResult(shoppingPersistedTabData); });
                         });
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 15f13cb409..05418619 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -296,6 +296,8 @@
     "webui/predictors/predictors_ui.h",
     "webui/prefs_internals_source.cc",
     "webui/prefs_internals_source.h",
+    "webui/profiles/profile_internals_ui.cc",
+    "webui/profiles/profile_internals_ui.h",
     "webui/segmentation_internals/segmentation_internals_page_handler_impl.cc",
     "webui/segmentation_internals/segmentation_internals_page_handler_impl.h",
     "webui/segmentation_internals/segmentation_internals_ui.cc",
@@ -3069,6 +3071,7 @@
       "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings",
       "//chrome/browser/web_applications",
       "//chrome/services/file_util/public/cpp",
+      "//chromeos/ash/components/dbus/upstart",
       "//chromeos/assistant:buildflags",
       "//chromeos/components/hps",
       "//chromeos/components/local_search_service/public/cpp",
@@ -3097,7 +3100,6 @@
       "//chromeos/dbus/tpm_manager",
       "//chromeos/dbus/tpm_manager:tpm_manager_proto",
       "//chromeos/dbus/update_engine",
-      "//chromeos/dbus/upstart",
       "//chromeos/dbus/userdataauth",
       "//chromeos/dbus/userdataauth:userdataauth_proto",
       "//chromeos/dbus/util",
diff --git a/chrome/browser/ui/passwords/settings/password_manager_porter.cc b/chrome/browser/ui/passwords/settings/password_manager_porter.cc
index c12f3e9a..0248424 100644
--- a/chrome/browser/ui/passwords/settings/password_manager_porter.cc
+++ b/chrome/browser/ui/passwords/settings/password_manager_porter.cc
@@ -181,6 +181,10 @@
 // This method should never be called on Android (as there is no file selector),
 // and the relevant IDS constants are not present for Android.
 #if !BUILDFLAG(IS_ANDROID)
+  // Early return if the select file dialog is already active.
+  if (select_file_dialog_)
+    return;
+
   DCHECK(web_contents);
   profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
@@ -231,12 +235,16 @@
       ExportPasswordsToPath(path);
       break;
   }
+
+  select_file_dialog_.reset();
 }
 
 void PasswordManagerPorter::FileSelectionCanceled(void* params) {
   if (reinterpret_cast<uintptr_t>(params) == PASSWORD_EXPORT) {
     exporter_->Cancel();
   }
+
+  select_file_dialog_.reset();
 }
 
 void PasswordManagerPorter::ImportPasswordsFromPath(
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 6a99ee14..bffa021d 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
@@ -27,8 +28,8 @@
 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx
 const AcceleratorMapping kAcceleratorMap[] = {
 // To add an accelerator to macOS that uses modifier keys, either:
-//   1) Update MainMenu.xib to include a new menu item with the appropriate
-//      modifier.
+//   1) Update the main menu built in main_menu_builder.mm to include a new menu
+//      item with the appropriate modifier.
 //   2) Update GetShortcutsNotPresentInMainMenu() in
 //      global_keyboard_shortcuts_mac.mm.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -261,16 +262,10 @@
 };
 
 const int kRepeatableCommandIds[] = {
-  IDC_FIND_NEXT,
-  IDC_FIND_PREVIOUS,
-  IDC_FOCUS_NEXT_PANE,
-  IDC_FOCUS_PREVIOUS_PANE,
-  IDC_MOVE_TAB_NEXT,
-  IDC_MOVE_TAB_PREVIOUS,
-  IDC_SELECT_NEXT_TAB,
-  IDC_SELECT_PREVIOUS_TAB,
+    IDC_FIND_NEXT,           IDC_FIND_PREVIOUS,       IDC_FOCUS_NEXT_PANE,
+    IDC_FOCUS_PREVIOUS_PANE, IDC_MOVE_TAB_NEXT,       IDC_MOVE_TAB_PREVIOUS,
+    IDC_SELECT_NEXT_TAB,     IDC_SELECT_PREVIOUS_TAB,
 };
-const size_t kRepeatableCommandIdsLength = std::size(kRepeatableCommandIds);
 
 } // namespace
 
@@ -306,9 +301,9 @@
 bool GetStandardAcceleratorForCommandId(int command_id,
                                         ui::Accelerator* accelerator) {
 #if BUILDFLAG(IS_MAC)
-  // On macOS, the cut/copy/paste accelerators are defined in MainMenu.xib and
-  // the accelerator is user configurable. All of this is handled by
-  // CommandDispatcher.
+  // On macOS, the cut/copy/paste accelerators are defined in the main menu
+  // built in main_menu_builder.mm and the accelerator is user configurable. All
+  // of this is handled by CommandDispatcher.
   NOTREACHED();
   return false;
 #else
@@ -330,9 +325,5 @@
 }
 
 bool IsCommandRepeatable(int command_id) {
-  for (size_t i = 0; i < kRepeatableCommandIdsLength; ++i) {
-    if (kRepeatableCommandIds[i] == command_id)
-      return true;
-  }
-  return false;
+  return base::Contains(kRepeatableCommandIds, command_id);
 }
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
index ab083f5..cee972c 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
@@ -25,6 +25,7 @@
 
 namespace {
 constexpr int kCheckboxHeight = 32;
+constexpr int kBuffer = 40;
 }  // namespace
 
 void DownloadBubbleSecurityView::AddHeader() {
@@ -133,9 +134,10 @@
   // The label defaults to a single line, which would force the dialog wider;
   // instead give it a width that's the minimum we want it to have. Then the
   // Layout will stretch it back out into any additional space available.
+  // Reduce by extra buffer so it has space for word wrapping.
   const int min_label_width =
       bubble_width - side_margin * 4 - icon->GetImageModel().Size().width() -
-      2 * GetLayoutInsets(DOWNLOAD_ICON).width() - icon_label_spacing;
+      2 * GetLayoutInsets(DOWNLOAD_ICON).width() - icon_label_spacing - kBuffer;
   styled_label->SizeToFit(min_label_width);
 
   if (info_.has_checkbox) {
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index bd43be8c..f66318a 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -55,6 +55,9 @@
   Profile* profile = browser_->profile();
   SetVisible(false);
 
+  scanning_animation_.SetSlideDuration(base::Milliseconds(2500));
+  scanning_animation_.SetTweenType(gfx::Tween::LINEAR);
+
   bubble_controller_ = std::make_unique<DownloadBubbleUIController>(profile);
   // Wait until we're done with everything else before creating `controller_`
   // since it can call `Show()` synchronously.
@@ -70,8 +73,12 @@
 void DownloadToolbarButtonView::PaintButtonContents(gfx::Canvas* canvas) {
   DownloadDisplayController::ProgressInfo progress_info =
       controller_->GetProgress();
+  DownloadDisplayController::IconInfo icon_info = controller_->GetIconInfo();
   // Do not show the progress ring when there is no in progress download.
   if (progress_info.download_count == 0) {
+    if (scanning_animation_.is_animating()) {
+      scanning_animation_.End();
+    }
     return;
   }
 
@@ -80,6 +87,21 @@
   int diameter = 2 * kProgressRingRadius;
   gfx::RectF ring_bounds(x, y, /*width=*/diameter, /*height=*/diameter);
 
+  if (icon_info.icon_state == download::DownloadIconState::kDeepScanning) {
+    if (!scanning_animation_.is_animating()) {
+      scanning_animation_.Reset();
+      scanning_animation_.Show();
+    }
+    views::DrawSpinningRing(
+        canvas, gfx::RectFToSkRect(ring_bounds),
+        GetColorProvider()->GetColor(kColorDownloadToolbarButtonRingBackground),
+        GetColorProvider()->GetColor(kColorDownloadToolbarButtonActive),
+        kProgressRingStrokeWidth, /*start_angle=*/
+        gfx::Tween::IntValueBetween(scanning_animation_.GetCurrentValue(), 0,
+                                    360));
+    return;
+  }
+
   views::DrawProgressRing(
       canvas, gfx::RectFToSkRect(ring_bounds),
       GetColorProvider()->GetColor(kColorDownloadToolbarButtonRingBackground),
@@ -137,7 +159,8 @@
       icon_info.is_active
           ? GetColorProvider()->GetColor(kColorDownloadToolbarButtonActive)
           : GetColorProvider()->GetColor(kColorDownloadToolbarButtonInactive);
-  if (icon_info.icon_state == download::DownloadIconState::kProgress) {
+  if (icon_info.icon_state == download::DownloadIconState::kProgress ||
+      icon_info.icon_state == download::DownloadIconState::kDeepScanning) {
     new_icon = &kDownloadInProgressIcon;
   } else {
     new_icon = &kDownloadToolbarButtonIcon;
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
index 8208597..453c111 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/download/download_ui_model.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/gfx/animation/throb_animation.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
 class Browser;
@@ -84,6 +85,8 @@
   raw_ptr<views::BubbleDialogDelegate> bubble_delegate_ = nullptr;
   raw_ptr<View> switcher_view_ = nullptr;
 
+  gfx::SlideAnimation scanning_animation_{this};
+
   base::WeakPtrFactory<DownloadToolbarButtonView> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ui/views/passwords/views_utils.h b/chrome/browser/ui/views/passwords/views_utils.h
index e84ca92..41bd58a 100644
--- a/chrome/browser/ui/views/passwords/views_utils.h
+++ b/chrome/browser/ui/views/passwords/views_utils.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PASSWORDS_VIEWS_UTILS_H_
 #define CHROME_BROWSER_UI_VIEWS_PASSWORDS_VIEWS_UTILS_H_
 
+#include <memory>
 #include <string>
 
 #include "base/callback_forward.h"
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index 969fd09..653338d 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include "base/barrier_closure.h"
 #include "base/callback_helpers.h"
+#include "base/cfi_buildflags.h"
 #include "base/json/values_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
@@ -854,8 +855,15 @@
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
+// TODO(crbug.com/1289326) Test is flaky on Linux CFI
+#if BUILDFLAG(CFI_ICALL_CHECK) && BUILDFLAG(IS_LINUX)
+#define MAYBE_CreateSignedInProfileSettings \
+  DISABLED_CreateSignedInProfileSettings
+#else
+#define MAYBE_CreateSignedInProfileSettings CreateSignedInProfileSettings
+#endif
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
-                       CreateSignedInProfileSettings) {
+                       MAYBE_CreateSignedInProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   // Simulate a successful sign-in and wait for the sign-in to propagate to the
   // flow, resulting in sync confirmation screen getting displayed.
@@ -1179,8 +1187,14 @@
       metrics::StartupProfilingFinishReason::kDone, 1);
 }
 
+// TODO(crbug.com/1289326) Test is flaky on Linux CFI
+#if BUILDFLAG(CFI_ICALL_CHECK) && BUILDFLAG(IS_LINUX)
+#define MAYBE_OpenProfile_Settings DISABLED_OpenProfile_Settings
+#else
+#define MAYBE_OpenProfile_Settings OpenProfile_Settings
+#endif
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
-                       OpenProfile_Settings) {
+                       MAYBE_OpenProfile_Settings) {
   AvatarToolbarButton::SetIPHMinDelayAfterCreationForTesting(base::Seconds(0));
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   // Create a second profile.
@@ -1390,8 +1404,16 @@
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
+// TODO(crbug.com/1289326) Test is flaky on Linux CFI
+#if BUILDFLAG(CFI_ICALL_CHECK) && BUILDFLAG(IS_LINUX)
+#define MAYBE_CreateSignedInEnterpriseProfileSettings \
+  DISABLED_CreateSignedInEnterpriseProfileSettings
+#else
+#define MAYBE_CreateSignedInEnterpriseProfileSettings \
+  CreateSignedInEnterpriseProfileSettings
+#endif
 IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
-                       CreateSignedInProfileSettings) {
+                       MAYBE_CreateSignedInEnterpriseProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   // Simulate a successful sign-in and wait for the sign-in to propagate to the
   // flow, resulting in enterprise welcome screen getting displayed.
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 278a4921..bd8979f 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1913,13 +1913,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-// TODO(crbug.com/1270961): Flaky on Win and Mac.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
-#define MAYBE_WebAppFileHandler DISABLED_WebAppFileHandler
-#else
-#define MAYBE_WebAppFileHandler WebAppFileHandler
-#endif
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_FileHandler, MAYBE_WebAppFileHandler) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_FileHandler, WebAppFileHandler) {
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
diff --git a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
index d5074ed..9872ac4 100644
--- a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
@@ -137,6 +137,13 @@
         profile_, selected_app_id_,
         apps_util::ConvertCrosapiToAppServiceIntent(intent, profile_));
   }
+  void ShowBubbleWithOnClosed(
+      const std::string& window_id,
+      sharesheet::LaunchSource source,
+      crosapi::mojom::IntentPtr intent,
+      crosapi::mojom::Sharesheet::ShowBubbleWithOnClosedCallback callback)
+      override {}
+  void CloseBubble(const std::string& window_id) override {}
 
   Profile* profile_ = nullptr;
   web_app::AppId selected_app_id_;
@@ -201,15 +208,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   }
 
-  void SetSelectedSharesheetApp(const AppId& app_id) {
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    service_.set_selected_app_id(app_id);
-#else
-    sharesheet::SharesheetService::SetSelectedAppForTesting(
-        base::UTF8ToUTF16(app_id));
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-  }
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   void SetUpOnMainThread() override {
     WebAppControllerBrowserTest::SetUpOnMainThread();
diff --git a/chrome/browser/ui/webui/certificates_handler.cc b/chrome/browser/ui/webui/certificates_handler.cc
index 954fd22..38699c73 100644
--- a/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chrome/browser/ui/webui/certificates_handler.cc
@@ -287,7 +287,7 @@
 CertificatesHandler::~CertificatesHandler() {
   if (select_file_dialog_.get())
     select_file_dialog_->ListenerDestroyed();
-  select_file_dialog_ = nullptr;
+  select_file_dialog_.reset();
 }
 
 void CertificatesHandler::RegisterMessages() {
@@ -388,6 +388,8 @@
     default:
       NOTREACHED();
   }
+
+  select_file_dialog_.reset();
 }
 
 void CertificatesHandler::FileSelectionCanceled(void* params) {
@@ -497,6 +499,10 @@
 }
 
 void CertificatesHandler::HandleExportPersonal(const base::Value::List& args) {
+  // Early return if the select file dialog is already active.
+  if (select_file_dialog_)
+    return;
+
   CHECK_EQ(2U, args.size());
   if (!AssignWebUICallbackId(args)) {
     RejectJavascriptCallback(base::Value(args[0].GetString()), base::Value());
@@ -592,6 +598,10 @@
 }
 
 void CertificatesHandler::HandleImportPersonal(const base::Value::List& args) {
+  // Early return if the select file dialog is already active.
+  if (select_file_dialog_)
+    return;
+
   // When the "allowed" value changes while user on the certificate manager
   // page, the UI doesn't update without page refresh and user can still see and
   // use import button. Because of this 'return' the button will do nothing.
@@ -762,10 +772,14 @@
   // away so they don't try and call back to us.
   if (select_file_dialog_.get())
     select_file_dialog_->ListenerDestroyed();
-  select_file_dialog_ = nullptr;
+  select_file_dialog_.reset();
 }
 
 void CertificatesHandler::HandleImportServer(const base::Value::List& args) {
+  // Early return if the select file dialog is already active.
+  if (select_file_dialog_)
+    return;
+
   CHECK_EQ(1U, args.size());
   if (!AssignWebUICallbackId(args)) {
     RejectJavascriptCallback(base::Value(args[0].GetString()), base::Value());
@@ -836,6 +850,10 @@
 }
 
 void CertificatesHandler::HandleImportCA(const base::Value::List& args) {
+  // Early return if the select file dialog is already active.
+  if (select_file_dialog_)
+    return;
+
   // When the "allowed" value changes while user on the certificate manager
   // page, the UI doesn't update without page refresh and user can still see and
   // use import button. Because of this 'return' the button will do nothing.
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index e098eb1..e6fc310 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -57,6 +57,7 @@
 #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
 #include "chrome/browser/ui/webui/policy/policy_ui.h"
 #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
+#include "chrome/browser/ui/webui/profiles/profile_internals_ui.h"
 #include "chrome/browser/ui/webui/segmentation_internals/segmentation_internals_ui.h"
 #include "chrome/browser/ui/webui/signin_internals_ui.h"
 #include "chrome/browser/ui/webui/sync_internals/sync_internals_ui.h"
@@ -791,6 +792,8 @@
     return &NewWebUI<PasswordManagerInternalsUI>;
   if (url.host_piece() == chrome::kChromeUIPredictorsHost)
     return &NewWebUI<PredictorsUI>;
+  if (url.host_piece() == chrome::kChromeUIProfileInternalsHost)
+    return &NewWebUI<ProfileInternalsUI>;
   if (url.host_piece() == safe_browsing::kChromeUISafeBrowsingHost)
     return &NewWebUI<safe_browsing::SafeBrowsingUI>;
   if (url.host_piece() == chrome::kChromeUISegmentationInternalsHost)
diff --git a/chrome/browser/ui/webui/chromeos/login/active_directory_login_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/active_directory_login_screen_handler.cc
index bd872d66..79c09fe5 100644
--- a/chrome/browser/ui/webui/chromeos/login/active_directory_login_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/active_directory_login_screen_handler.cc
@@ -54,7 +54,7 @@
 }
 
 void ActiveDirectoryLoginScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
index 2b02e95a..f26350d 100644
--- a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
@@ -77,7 +77,7 @@
 }
 
 void AppLaunchSplashScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -130,7 +130,7 @@
     return;
 
   state_ = state;
-  if (page_is_ready()) {
+  if (IsJavascriptAllowed()) {
     SetLaunchText(
         l10n_util::GetStringUTF8(GetProgressMessageFromState(state_)));
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
index 0799d55..2cba424 100644
--- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -60,9 +60,13 @@
   OobeUI* oobe_ui = GetOobeUI();
   if (oobe_ui)
     oobe_ui->RemoveObserver(this);
-  chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
-      this, FROM_HERE);
-  system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+  if (network_time_zone_observing_) {
+    network_time_zone_observing_ = false;
+    chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+        this, FROM_HERE);
+    system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+  }
+
   for (auto& observer : observer_list_)
     observer.OnViewDestroyed(this);
 }
@@ -275,7 +279,7 @@
 }
 
 void ArcTermsOfServiceScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -291,7 +295,12 @@
 }
 
 void ArcTermsOfServiceScreenHandler::Hide() {
-  system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+  if (network_time_zone_observing_) {
+    network_time_zone_observing_ = false;
+    chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+        this, FROM_HERE);
+    system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+  }
   pref_handler_.reset();
 }
 
@@ -300,7 +309,6 @@
 }
 
 void ArcTermsOfServiceScreenHandler::StartNetworkAndTimeZoneObserving() {
-  // TODO(crbug.com/1180291) - Clean up work. Fix this logic.
   if (network_time_zone_observing_)
     return;
 
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index fbfc06ae..fdfa780 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -206,7 +206,7 @@
 void AssistantOptInFlowScreenHandler::Bind(AssistantOptInFlowScreen* screen) {
   BaseScreenHandler::SetBaseScreenDeprecated(screen);
   screen_ = screen;
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -216,7 +216,7 @@
 }
 
 void AssistantOptInFlowScreenHandler::Show() {
-  if (!page_is_ready() || !screen_) {
+  if (!IsJavascriptAllowed() || !screen_) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
index d4225b6..4ce89b8 100644
--- a/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
@@ -25,7 +25,7 @@
 }
 
 void AutoEnrollmentCheckScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -34,7 +34,7 @@
 
 void AutoEnrollmentCheckScreenHandler::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -47,7 +47,7 @@
 }
 
 void AutoEnrollmentCheckScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !delegate_)
+  if (!IsJavascriptAllowed() || !delegate_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
index 9455682..df6f7a4 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
@@ -20,8 +20,6 @@
 BaseWebUIHandler::~BaseWebUIHandler() = default;
 
 void BaseWebUIHandler::OnJavascriptAllowed() {
-  CHECK(!page_is_ready_);
-  page_is_ready_ = true;
   auto deferred_calls = std::exchange(deferred_calls_, {});
   for (auto& call : deferred_calls)
     std::move(call).Run();
diff --git a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
index 29d1829..3e9e0a22 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
@@ -80,16 +80,24 @@
       return;
     }
 
-    if (page_is_ready()) {
-      // Ignore call because javascript was disallowed after the initialization.
-      return;
-    }
-
     deferred_calls_.push_back(base::BindOnce(
         &BaseWebUIHandler::CallJS<Args...>, base::Unretained(this),
         function_name, std::move(args)...));
   }
 
+  template <typename... Args>
+  void FireWebUIListenerWhenAllowed(const std::string& event_name,
+                                    Args... args) {
+    if (IsJavascriptAllowed()) {
+      FireWebUIListener(event_name, args...);
+      return;
+    }
+
+    deferred_calls_.push_back(
+        base::BindOnce(&BaseWebUIHandler::FireWebUIListenerWhenAllowed<Args...>,
+                       base::Unretained(this), event_name, std::move(args)...));
+  }
+
   template <typename T, typename... Args>
   void AddCallback(const std::string& function_name,
                    void (T::*method)(Args...)) {
@@ -111,15 +119,9 @@
   // Returns current visible OOBE screen.
   OobeScreenId GetCurrentScreen();
 
-  // Whether page is ready.
-  bool page_is_ready() const { return page_is_ready_; }
-
  private:
   friend class OobeUI;
 
-  // Keeps whether page is ready.
-  bool page_is_ready_ = false;
-
   std::vector<base::OnceClosure> deferred_calls_;
 };
 
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 2759047..7774875 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -218,7 +218,7 @@
 
   show_oobe_ui_ = show;
 
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     UpdateOobeUIVisibility();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/enable_adb_sideloading_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enable_adb_sideloading_screen_handler.cc
index bc96b50..5365e3f4 100644
--- a/chrome/browser/ui/webui/chromeos/login/enable_adb_sideloading_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enable_adb_sideloading_screen_handler.cc
@@ -23,7 +23,7 @@
     default;
 
 void EnableAdbSideloadingScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -66,7 +66,7 @@
 }
 
 void EnableAdbSideloadingScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready())
+  if (!IsJavascriptAllowed())
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
index a973956..c03934a 100644
--- a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
@@ -33,7 +33,7 @@
 }
 
 void EnableDebuggingScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -47,7 +47,7 @@
 void EnableDebuggingScreenHandler::SetDelegate(EnableDebuggingScreen* screen) {
   screen_ = screen;
   BaseScreenHandler::SetBaseScreenDeprecated(screen_);
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -94,7 +94,7 @@
 }
 
 void EnableDebuggingScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !screen_)
+  if (!IsJavascriptAllowed() || !screen_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
index 3a5fb2b1..dba7e38 100644
--- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -27,7 +27,7 @@
 }
 
 void EncryptionMigrationScreenHandler::Show() {
-  if (!page_is_ready() || !delegate_) {
+  if (!IsJavascriptAllowed() || !delegate_) {
     show_on_init_ = true;
     return;
   }
@@ -42,7 +42,7 @@
     EncryptionMigrationScreen* delegate) {
   delegate_ = delegate;
   BaseScreenHandler::SetBaseScreenDeprecated(delegate);
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -95,7 +95,7 @@
 }
 
 void EncryptionMigrationScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !delegate_)
+  if (!IsJavascriptAllowed() || !delegate_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index 4ba4b05..0f7dbf2 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -244,7 +244,7 @@
 }
 
 void EnrollmentScreenHandler::Show() {
-  if (!page_is_ready())
+  if (!IsJavascriptAllowed())
     show_on_init_ = true;
   else
     DoShow();
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
index 6451e5a..6f180504 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -27,7 +27,7 @@
 }
 
 void ErrorScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -139,7 +139,7 @@
 }
 
 void ErrorScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready())
+  if (!IsJavascriptAllowed())
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
index 0e6d1713..b43bea0 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -40,7 +40,7 @@
 }
 
 void EulaScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -53,7 +53,7 @@
 void EulaScreenHandler::Bind(EulaScreen* screen) {
   screen_ = screen;
   BaseScreenHandler::SetBaseScreenDeprecated(screen_);
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -121,7 +121,7 @@
 }
 
 void EulaScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !screen_)
+  if (!IsJavascriptAllowed() || !screen_)
     return;
 
   CallJS("login.EulaScreen.setUsageStats", screen_->IsUsageStatsEnabled());
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index f3096d0b..8635225 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -645,8 +645,6 @@
 
 void GaiaScreenHandler::InitializeDeprecated() {
   initialized_ = true;
-  // This should be called only once on page load.
-  AllowJavascript();
   if (show_on_init_) {
     show_on_init_ = false;
     ShowGaiaScreenIfReady();
diff --git a/chrome/browser/ui/webui/chromeos/login/gesture_navigation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gesture_navigation_screen_handler.cc
index 5d9248e..5249d6e 100644
--- a/chrome/browser/ui/webui/chromeos/login/gesture_navigation_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gesture_navigation_screen_handler.cc
@@ -21,7 +21,7 @@
 GestureNavigationScreenHandler::~GestureNavigationScreenHandler() = default;
 
 void GestureNavigationScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/guest_tos_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/guest_tos_screen_handler.cc
index bd578f3a..8dc9f78 100644
--- a/chrome/browser/ui/webui/chromeos/login/guest_tos_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/guest_tos_screen_handler.cc
@@ -50,7 +50,7 @@
                                  const std::string& cros_eula_url) {
   google_eula_url_ = google_eula_url;
   cros_eula_url_ = cros_eula_url;
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/hardware_data_collection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/hardware_data_collection_screen_handler.cc
index 92d567d..b970fa5 100644
--- a/chrome/browser/ui/webui/chromeos/login/hardware_data_collection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/hardware_data_collection_screen_handler.cc
@@ -28,7 +28,7 @@
 }
 
 void HWDataCollectionScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -66,7 +66,7 @@
 }
 
 void HWDataCollectionScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !screen_)
+  if (!IsJavascriptAllowed() || !screen_)
     return;
 
   CallJS("login.HWDataCollectionScreen.setHWDataUsage",
diff --git a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
index 91e961d..6acbaf8 100644
--- a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
@@ -32,7 +32,7 @@
 }
 
 void HIDDetectionScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -46,7 +46,7 @@
 void HIDDetectionScreenHandler::Bind(HIDDetectionScreen* screen) {
   screen_ = screen;
   BaseScreenHandler::SetBaseScreenDeprecated(screen_);
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
index 7a7d5c55..ca2e2d5 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
@@ -38,7 +38,7 @@
 }
 
 void KioskAutolaunchScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -49,7 +49,7 @@
 void KioskAutolaunchScreenHandler::SetDelegate(
     KioskAutolaunchScreen* delegate) {
   delegate_ = delegate;
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -87,7 +87,7 @@
 }
 
 void KioskAutolaunchScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !delegate_)
+  if (!IsJavascriptAllowed() || !delegate_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
index fb6faff..df949dcf 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
@@ -30,7 +30,7 @@
 }
 
 void KioskEnableScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -40,7 +40,7 @@
 void KioskEnableScreenHandler::SetScreen(KioskEnableScreen* screen) {
   BaseScreenHandler::SetBaseScreenDeprecated(screen);
   screen_ = screen;
-  if (page_is_ready() && screen_)
+  if (IsJavascriptAllowed() && screen_)
     InitializeDeprecated();
 }
 
@@ -58,7 +58,7 @@
 }
 
 void KioskEnableScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !screen_)
+  if (!IsJavascriptAllowed() || !screen_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
index 0ff4fc1..8ed340fd 100644
--- a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
@@ -60,7 +60,7 @@
 }
 
 void LacrosDataMigrationScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/management_transition_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/management_transition_screen_handler.cc
index 63d308ac..ce9cf9d 100644
--- a/chrome/browser/ui/webui/chromeos/login/management_transition_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/management_transition_screen_handler.cc
@@ -74,7 +74,7 @@
     ManagementTransitionScreen* screen) {
   BaseScreenHandler::SetBaseScreenDeprecated(screen);
   screen_ = screen;
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
@@ -85,7 +85,7 @@
 }
 
 void ManagementTransitionScreenHandler::Show() {
-  if (!page_is_ready() || !screen_) {
+  if (!IsJavascriptAllowed() || !screen_) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/multidevice_setup_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/multidevice_setup_screen_handler.cc
index a015a72..6c73b67f 100644
--- a/chrome/browser/ui/webui/chromeos/login/multidevice_setup_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/multidevice_setup_screen_handler.cc
@@ -32,9 +32,8 @@
 }
 
 void MultiDeviceSetupScreenHandler::Show() {
-  AllowJavascript();
   ShowInWebUI();
-  FireWebUIListener("multidevice_setup.initializeSetupFlow");
+  FireWebUIListenerWhenAllowed("multidevice_setup.initializeSetupFlow");
 }
 
 void MultiDeviceSetupScreenHandler::GetAdditionalParameters(
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index 80dc822..fc17413 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -31,7 +31,7 @@
 }
 
 void NetworkScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/offline_login_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/offline_login_screen_handler.cc
index 26c765d7..2e58f517 100644
--- a/chrome/browser/ui/webui/chromeos/login/offline_login_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/offline_login_screen_handler.cc
@@ -59,7 +59,7 @@
 }
 
 void OfflineLoginScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index 39f97dd00..67e59a2 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -734,15 +734,6 @@
     observer.OnCurrentScreenChanged(previous_screen_, new_screen);
 }
 
-bool OobeUI::IsScreenInitialized(OobeScreenId screen) {
-  for (BaseScreenHandler* handler : screen_handlers_) {
-    if (handler->oobe_screen() == screen) {
-      return handler->page_is_ready();
-    }
-  }
-  return false;
-}
-
 bool OobeUI::IsJSReady(base::OnceClosure display_is_ready_callback) {
   if (!ready_)
     ready_callbacks_.AddUnsafe(std::move(display_is_ready_callback));
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index be2eeed2..08331a70b 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -84,8 +84,6 @@
   // Called when the screen has changed.
   void CurrentScreenChanged(OobeScreenId screen);
 
-  bool IsScreenInitialized(OobeScreenId screen);
-
   bool IsJSReady(base::OnceClosure display_is_ready_callback);
 
   // Shows or hides OOBE UI elements.
diff --git a/chrome/browser/ui/webui/chromeos/login/packaged_license_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/packaged_license_screen_handler.cc
index ca03634..06ae858 100644
--- a/chrome/browser/ui/webui/chromeos/login/packaged_license_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/packaged_license_screen_handler.cc
@@ -21,7 +21,7 @@
 PackagedLicenseScreenHandler::~PackagedLicenseScreenHandler() {}
 
 void PackagedLicenseScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/quick_start_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/quick_start_screen_handler.cc
index f9390f3..35113b6 100644
--- a/chrome/browser/ui/webui/chromeos/login/quick_start_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/quick_start_screen_handler.cc
@@ -22,7 +22,7 @@
 }
 
 void QuickStartScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -33,7 +33,7 @@
 void QuickStartScreenHandler::Bind(QuickStartScreen* screen) {
   screen_ = screen;
   BaseScreenHandler::SetBaseScreenDeprecated(screen_);
-  if (page_is_ready())
+  if (IsJavascriptAllowed())
     InitializeDeprecated();
 }
 
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 0e8d65d..fcf4dfb 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
@@ -106,7 +106,7 @@
 }
 
 void RecommendAppsScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index 70c3495..2d9cf5f 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -42,7 +42,7 @@
 }
 
 void ResetScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -102,7 +102,7 @@
 }
 
 void ResetScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready())
+  if (!IsJavascriptAllowed())
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/smart_privacy_protection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/smart_privacy_protection_screen_handler.cc
index 15498f7..73d422e 100644
--- a/chrome/browser/ui/webui/chromeos/login/smart_privacy_protection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/smart_privacy_protection_screen_handler.cc
@@ -26,7 +26,7 @@
 }
 
 void SmartPrivacyProtectionScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -77,7 +77,7 @@
 }
 
 void SmartPrivacyProtectionScreenHandler::InitializeDeprecated() {
-  if (!page_is_ready() || !screen_)
+  if (!IsJavascriptAllowed() || !screen_)
     return;
 
   if (show_on_init_) {
diff --git a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
index 83a9b57..09cfb19c 100644
--- a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -66,7 +66,7 @@
 
 void TermsOfServiceScreenHandler::Show(const std::string& manager) {
   manager_ = manager;
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
@@ -106,7 +106,7 @@
 }
 
 void TermsOfServiceScreenHandler::UpdateTermsOfServiceInUI() {
-  if (!page_is_ready())
+  if (!IsJavascriptAllowed())
     return;
 
   // If either `load_error_` or `terms_of_service_` is set, the download of the
diff --git a/chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.cc
index b5c2bab..8bbb632 100644
--- a/chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.cc
@@ -56,7 +56,7 @@
 }
 
 void TpmErrorScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
index d0da4c7d..8157e4c 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
@@ -100,7 +100,7 @@
 }
 
 void UpdateRequiredScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
index b05b4ca..fd4664f 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -52,7 +52,7 @@
 }
 
 void UpdateScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
index 38b58bb..fee5935 100644
--- a/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.cc
@@ -74,7 +74,7 @@
 // WelcomeScreenHandler, WelcomeScreenView implementation: ---------------------
 
 void WelcomeScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
index 89cc6df..338e23c 100644
--- a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
@@ -25,7 +25,7 @@
 }
 
 void WrongHWIDScreenHandler::Show() {
-  if (!page_is_ready()) {
+  if (!IsJavascriptAllowed()) {
     show_on_init_ = true;
     return;
   }
diff --git a/chrome/browser/ui/webui/profiles/profile_internals_ui.cc b/chrome/browser/ui/webui/profiles/profile_internals_ui.cc
new file mode 100644
index 0000000..791fafb
--- /dev/null
+++ b/chrome/browser/ui/webui/profiles/profile_internals_ui.cc
@@ -0,0 +1,24 @@
+// 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/webui/profiles/profile_internals_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/dev_ui_browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+ProfileInternalsUI::ProfileInternalsUI(content::WebUI* web_ui)
+    : WebUIController(web_ui) {
+  // Set up the chrome://profile-internals source.
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::Create(chrome::kChromeUIProfileInternalsHost);
+  html_source->SetDefaultResource(IDR_PROFILE_INTERNALS_HTML);
+  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+}
+
+ProfileInternalsUI::~ProfileInternalsUI() = default;
diff --git a/chrome/browser/ui/webui/profiles/profile_internals_ui.h b/chrome/browser/ui/webui/profiles/profile_internals_ui.h
new file mode 100644
index 0000000..8233c13
--- /dev/null
+++ b/chrome/browser/ui/webui/profiles/profile_internals_ui.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PROFILES_PROFILE_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PROFILES_PROFILE_INTERNALS_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+// Controller for chrome://profile-internals page.
+class ProfileInternalsUI : public content::WebUIController {
+ public:
+  explicit ProfileInternalsUI(content::WebUI* web_ui);
+
+  ProfileInternalsUI(const ProfileInternalsUI&) = delete;
+  ProfileInternalsUI& operator=(const ProfileInternalsUI&) = delete;
+
+  ~ProfileInternalsUI() override;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_PROFILES_PROFILE_INTERNALS_UI_H_
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 122ef765..afa2ba36 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1649202914-29d10895b70bafca7d409ad201000f9637924b25.profdata
+chrome-linux-main-1649245281-7cb1ef494bb8d292a4b825d2ec135c727b83d326.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 0f1b595..b22f40c 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1649224491-802a82a720bed2f3bdcf1c027f731f42d95b8d5c.profdata
+chrome-mac-arm-main-1649245281-0261e6a52492edd9a911ab549fd5174c67afefb4.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 4e0c21a6..d87c255 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1649224491-03b0713b6815ec3e4eab77c13565a22a63dae868.profdata
+chrome-mac-main-1649245281-82d4babb2718a3fce21ec33ec20feb0cbfe5ac14.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 0397558f..59ba91d9 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1649224491-60944cc1341d5f59c10d231b83d2d8e32570c5fb.profdata
+chrome-win32-main-1649256535-e268f51ba808bfbb62a7dd13b072f6d2652a5188.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index ae20592..5bf963d 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1649224491-e93c9a486d3883dd4978e060351aa152ecb535d4.profdata
+chrome-win64-main-1649245281-034d4cff630a4bef4684526d7a9f827ad69feb0e.profdata
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index c2c6694..38a10ab 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -142,6 +142,7 @@
 const char kChromeUINewTabPageThirdPartyURL[] =
     "chrome://new-tab-page-third-party/";
 const char kChromeUINewTabURL[] = "chrome://newtab/";
+const char kChromeUIProfileInternalsHost[] = "profile-internals";
 const char kChromeUIOmniboxHost[] = "omnibox";
 const char kChromeUIOmniboxURL[] = "chrome://omnibox/";
 #if BUILDFLAG(IS_CHROMEOS)
@@ -625,6 +626,7 @@
     kChromeUIPolicyHost,
     kChromeUIPredictorsHost,
     kChromeUIPrefsInternalsHost,
+    kChromeUIProfileInternalsHost,
     kChromeUIQuotaInternalsHost,
     kChromeUISignInInternalsHost,
     kChromeUISiteEngagementHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 8c0e45ff..5fc5453 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -157,6 +157,7 @@
 extern const char kChromeUIPrintURL[];
 extern const char kChromeUIPrivacySandboxDialogHost[];
 extern const char kChromeUIPrivacySandboxDialogURL[];
+extern const char kChromeUIProfileInternalsHost[];
 extern const char kChromeUIQuitHost[];
 extern const char kChromeUIQuitURL[];
 extern const char kChromeUIResetPasswordHost[];
diff --git a/chrome/services/util_win/util_win_impl.cc b/chrome/services/util_win/util_win_impl.cc
index 9ffed27..26b2dd6 100644
--- a/chrome/services/util_win/util_win_impl.cc
+++ b/chrome/services/util_win/util_win_impl.cc
@@ -220,7 +220,6 @@
                                       base::FileEnumerator::DIRECTORIES);
   for (base::FilePath directory = directory_enum.Next(); !directory.empty();
        directory = directory_enum.Next()) {
-    current_exe.value();
     if (DirectoryContainsPinnedShortcutForProgram(
             directory, current_exe_compare, check_verbs)) {
       return true;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 28f49294f..f8d5fffd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2689,15 +2689,10 @@
         ]
         deps += [ "//chrome/browser/chromeos" ]
         data_deps += [
+          "//components/nacl/loader:nacl_helper",
           "//ppapi/native_client:irt",
           "//third_party/liblouis:liblouis_test_data",
         ]
-
-        # TODO(https://crbug.com/1299021): Implement building this NaCl target
-        # as ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-        if (target_cpu != "arm64") {
-          data_deps += [ "//components/nacl/loader:nacl_helper" ]
-        }
       }
 
       if (is_win || is_linux || is_chromeos) {
@@ -2709,10 +2704,7 @@
         # browser process as needed by this test. See http://crbug.com/157312.
         sources -= [ "../browser/nacl_host/test/gdb_debug_stub_browsertest.cc" ]
       }
-
-      # TODO(https://crbug.com/1299021): Implement building this NaCl target
-      # as ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-      if ((is_linux || is_chromeos) && target_cpu != "arm64") {
+      if (is_linux || is_chromeos) {
         data_deps += [ "//components/nacl/loader:nacl_helper" ]
       }
     }
@@ -4002,13 +3994,14 @@
         "//chrome/services/keymaster/public/mojom",
         "//chrome/services/wilco_dtc_supportd/public/mojom",
         "//chromeos:test_support",
+        "//chromeos/ash/components/dbus/authpolicy",
+        "//chromeos/ash/components/dbus/upstart",
         "//chromeos/assistant:buildflags",
         "//chromeos/components/onc:test_support",
         "//chromeos/components/quick_answers/public/cpp:cpp",
         "//chromeos/dbus:test_support",
         "//chromeos/dbus/attestation",
         "//chromeos/dbus/attestation:attestation_proto",
-        "//chromeos/dbus/authpolicy",
         "//chromeos/dbus/biod",
         "//chromeos/dbus/biod:biod_proto",
         "//chromeos/dbus/cros_disks",
@@ -4026,7 +4019,6 @@
         "//chromeos/dbus/system_proxy",
         "//chromeos/dbus/system_proxy:system_proxy_proto",
         "//chromeos/dbus/update_engine",
-        "//chromeos/dbus/upstart",
         "//chromeos/dbus/vm_plugin_dispatcher",
         "//chromeos/login/login_state:test_support",
         "//chromeos/process_proxy",
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 6a613b7..045c5a9 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -272,24 +272,10 @@
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_page_template_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_visibility_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/onc_mojo_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/kerberos_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_files_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/settings_scheduler_slider_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_main_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_ui_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_ui_test_2.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_reset_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_search_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_settings_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/search_subpage_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/search_engine_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.m.js",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
index e3f1df8..3195030 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
@@ -7,7 +7,7 @@
 
 import {fetchGooglePhotosAlbum, GooglePhotosAlbum, GooglePhotosEnablementState, GooglePhotosPhoto, GooglePhotosPhotosByAlbumId, initializeGooglePhotosData, WallpaperGridItem, WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
@@ -20,6 +20,15 @@
   let wallpaperProvider: TestWallpaperProvider;
 
   /**
+   * Returns the match for |selector| in |googlePhotosPhotosByAlbumIdElement|'s
+   * shadow DOM.
+   */
+  function querySelector(selector: string): Element|null {
+    return googlePhotosPhotosByAlbumIdElement!.shadowRoot!.querySelector(
+        selector);
+  }
+
+  /**
    * Returns all matches for |selector| in
    * |googlePhotosPhotosByAlbumIdElement|'s shadow DOM.
    */
@@ -99,7 +108,8 @@
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
 
     // Initially no album id selected. Photos should be absent.
-    const photoSelector = 'wallpaper-grid-item:not([hidden]).photo';
+    const photoSelector =
+        'wallpaper-grid-item:not([hidden]):not([placeholder]).photo';
     assertEquals(querySelectorAll(photoSelector)!.length, 0);
 
     // Select an album id. Photos should be absent since albums have not loaded.
@@ -305,6 +315,62 @@
     assertEquals(photoEls[1]!.selected, false);
   });
 
+  test('displays placeholders until photos are present', async () => {
+    // Prepare Google Photos data.
+    const photosCount = 5;
+    const album: GooglePhotosAlbum =
+        {id: '1', title: '', photoCount: photosCount, preview: {url: ''}};
+    const photos: GooglePhotosPhoto[] =
+        Array.from({length: photosCount}, (_, i) => ({
+                                            id: `id-${i}`,
+                                            name: `name-${i}`,
+                                            date: {data: []},
+                                            url: {url: `url-${i}`},
+                                          }));
+
+    // Initialize |googlePhotosPhotosByAlbumIdElement|.
+    googlePhotosPhotosByAlbumIdElement =
+        initElement(GooglePhotosPhotosByAlbumId, {hidden: false});
+    await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
+
+    // Initially no album id selected. Photos and placeholders should be absent.
+    const selector = 'wallpaper-grid-item:not([hidden]).photo';
+    const photoSelector = `${selector}:not([placeholder])`;
+    const placeholderSelector = `${selector}[placeholder]`;
+    assertEquals(querySelectorAll(photoSelector)!.length, 0);
+    assertEquals(querySelectorAll(placeholderSelector)!.length, 0);
+
+    // Select album id. Only placeholders should be present.
+    googlePhotosPhotosByAlbumIdElement.setAttribute('album-id', album.id);
+    await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
+    assertEquals(querySelectorAll(photoSelector)!.length, 0);
+    assertNotEquals(querySelectorAll(placeholderSelector)!.length, 0);
+
+    // Clicking a placeholder should do nothing.
+    const clickHandler = 'selectGooglePhotosPhoto';
+    (querySelector(placeholderSelector) as HTMLElement).click();
+    await new Promise<void>(resolve => setTimeout(resolve));
+    assertEquals(wallpaperProvider.getCallCount(clickHandler), 0);
+
+    // Provide Google Photos data.
+    personalizationStore.data.wallpaper.googlePhotos.count = photosCount;
+    personalizationStore.data.wallpaper.googlePhotos.albums = [album];
+    personalizationStore.data.wallpaper.googlePhotos.photos = photos;
+    personalizationStore.data.wallpaper.googlePhotos
+        .photosByAlbumId = {[album.id]: photos};
+    personalizationStore.notifyObservers();
+
+    // Only photos should be present.
+    await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
+    assertNotEquals(querySelectorAll(photoSelector)!.length, 0);
+    assertEquals(querySelectorAll(placeholderSelector)!.length, 0);
+
+    // Clicking a photo should do something.
+    (querySelector(photoSelector) as HTMLElement).click();
+    assertEquals(
+        await wallpaperProvider.whenCalled(clickHandler), photos[0]!.id);
+  });
+
   test('incrementally loads photos', async () => {
     personalizationStore.setReducersEnabled(true);
 
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 418c1a7..439d4f16 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -32,25 +32,10 @@
 
 js_modulizer("modulize") {
   input_files = [
-    "fake_settings_search_handler.js",
     "fake_system_display.js",
     "kerberos_accounts_test.js",
     "kerberos_page_test.js",
     "onc_mojo_test.js",
-    "os_files_page_test.js",
-    "os_reset_page_test.js",
-    "os_search_page_test.js",
-    "os_settings_main_test.js",
-    "os_settings_page_test.js",
-    "os_settings_search_box_test.js",
-    "os_settings_menu_test.js",
-    "os_settings_ui_test.js",
-    "os_settings_ui_test_2.js",
-    "os_sync_controls_test.js",
-    "search_subpage_test.js",
-    "search_engine_test.js",
-    "settings_scheduler_slider_test.js",
-    "settings_traffic_counters_test.js",
     "smb_shares_page_tests.js",
     "test_kerberos_accounts_browser_proxy.js",
     "test_os_lifetime_browser_proxy.js",
diff --git a/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js b/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
index 45c0e33..6c839e9 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_settings_search_handler.js
@@ -5,46 +5,41 @@
 /**
  * @fileoverview Fake implementation of SettingsSearchHandler for testing.
  */
-cr.define('settings', function() {
-  /**
-   * Fake implementation of chromeos.settings.mojom.SettingsSearchHandlerRemote.
-   *
-   * @implements {chromeos.settings.mojom.SearchHandlerInterface}
-   */
-  /* #export */ class FakeSettingsSearchHandler {
-    constructor() {
-      /** @private {!Array<chromeos.settings.mojom.SearchResult>} */
-      this.fakeResults_ = [];
+/**
+ * Fake implementation of chromeos.settings.mojom.SettingsSearchHandlerRemote.
+ *
+ * @implements {chromeos.settings.mojom.SearchHandlerInterface}
+ */
+export class FakeSettingsSearchHandler {
+  constructor() {
+    /** @private {!Array<chromeos.settings.mojom.SearchResult>} */
+    this.fakeResults_ = [];
 
-      /** @private {!chromeos.settings.mojom.SearchResultsObserverInterface} */
-      this.observer_;
-    }
-
-    /**
-     * @param {!Array<chromeos.settings.mojom.SearchResult>} results Fake
-     *     results that will be returned when Search() is called.
-     */
-    setFakeResults(results) {
-      this.fakeResults_ = results;
-    }
-
-    /** override */
-    async search(query, maxNumResults, parentResultBehavior) {
-      return {results: this.fakeResults_};
-    }
-
-    /** override */
-    observe(observer) {
-      this.observer_ = observer;
-    }
-
-    simulateSearchResultAvailabilityChanged() {
-      if (this.observer_) {
-        this.observer_.onSearchResultAvailabilityChanged();
-      }
-    }
+    /** @private {!chromeos.settings.mojom.SearchResultsObserverInterface} */
+    this.observer_;
   }
 
-  // #cr_define_end
-  return {FakeSettingsSearchHandler: FakeSettingsSearchHandler};
-});
+  /**
+   * @param {!Array<chromeos.settings.mojom.SearchResult>} results Fake
+   *     results that will be returned when Search() is called.
+   */
+  setFakeResults(results) {
+    this.fakeResults_ = results;
+  }
+
+  /** override */
+  async search(query, maxNumResults, parentResultBehavior) {
+    return {results: this.fakeResults_};
+  }
+
+  /** override */
+  observe(observer) {
+    this.observer_ = observer;
+  }
+
+  simulateSearchResultAvailabilityChanged() {
+    if (this.observer_) {
+      this.observer_.onSearchResultAvailabilityChanged();
+    }
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
index da1402e..2a05c1e 100644
--- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -16,7 +16,7 @@
 suite('AboutPageTest', function() {
   let page = null;
 
-  /** @type {?settings.TestAboutPageBrowserProxyChromeOS} */
+  /** @type {?TestAboutPageBrowserProxyChromeOS} */
   let aboutBrowserProxy = null;
 
   /** @type {?TestLifetimeBrowserProxy} */
diff --git a/chrome/test/data/webui/settings/chromeos/os_files_page_test.js b/chrome/test/data/webui/settings/chromeos/os_files_page_test.js
index 212a6a48..c606830 100644
--- a/chrome/test/data/webui/settings/chromeos/os_files_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_files_page_test.js
@@ -2,17 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/lazy_load.js';
+import 'chrome://os-settings/chromeos/lazy_load.js';
 
-// #import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
 suite('FilesPageTests', function() {
   /** @type {SettingsFilesPageElement} */
@@ -22,12 +20,12 @@
     PolymerTest.clearBody();
     filesPage = document.createElement('os-settings-files-page');
     document.body.appendChild(filesPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
     filesPage.remove();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
   });
 
   test('Disconnect Google Drive account,pref disabled/enabled', async () => {
@@ -37,7 +35,7 @@
     assertFalse(disconnectGoogleDrive.checked);
 
     disconnectGoogleDrive.shadowRoot.querySelector('cr-toggle').click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(disconnectGoogleDrive.checked);
   });
 
@@ -45,22 +43,20 @@
     const smbShares = assert(filesPage.$$('#smbShares'));
 
     smbShares.click();
-    Polymer.dom.flush();
-    assertEquals(
-        settings.Router.getInstance().getCurrentRoute(),
-        settings.routes.SMB_SHARES);
+    flush();
+    assertEquals(Router.getInstance().getCurrentRoute(), routes.SMB_SHARES);
   });
 
   test('Deep link to disconnect Google Drive', async () => {
     const params = new URLSearchParams();
     params.append('settingId', '1300');
-    settings.Router.getInstance().navigateTo(settings.routes.FILES, params);
+    Router.getInstance().navigateTo(routes.FILES, params);
 
-    Polymer.dom.flush();
+    flush();
 
     const deepLinkElement = filesPage.$$('#disconnectGoogleDriveAccount')
                                 .shadowRoot.querySelector('cr-toggle');
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Disconnect Drive toggle should be focused for settingId=1300.');
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index 27ab1d9..c0f5255b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -2,241 +2,231 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
-// #import {OsResetBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
-// #import {LifetimeBrowserProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.m.js';
-// #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
-// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {OsResetBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
+import {LifetimeBrowserProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
-cr.define('settings_reset_page', function() {
-  /** @enum {string} */
-  const TestNames = {
-    PowerwashDialogAction: 'PowerwashDialogAction',
-    PowerwashDialogOpenClose: 'PowerwashDialogOpenClose',
-    PowerwashFocusDeepLink: 'PowerwashFocusDeepLink',
-    PowerwashFocusDeepLinkNoFlag: 'PowerwashFocusDeepLinkNoFlag',
-    PowerwashFocusDeepLinkWrongId: 'PowerwashFocusDeepLinkWrongId',
-  };
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
-  suite('DialogTests', function() {
-    let resetPage = null;
+import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
+import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.m.js';
 
-    /** @type {!settings.ResetPageBrowserProxy} */
-    let resetPageBrowserProxy = null;
+/** @enum {string} */
+const TestNames = {
+  PowerwashDialogAction: 'PowerwashDialogAction',
+  PowerwashDialogOpenClose: 'PowerwashDialogOpenClose',
+  PowerwashFocusDeepLink: 'PowerwashFocusDeepLink',
+  PowerwashFocusDeepLinkNoFlag: 'PowerwashFocusDeepLinkNoFlag',
+  PowerwashFocusDeepLinkWrongId: 'PowerwashFocusDeepLinkWrongId',
+};
 
-    /** @type {!settings.LifetimeBrowserProxy} */
-    let lifetimeBrowserProxy = null;
+suite('DialogTests', function() {
+  let resetPage = null;
 
-    /** @type {!ash.cellularSetup.mojom.ESimManagerRemote|undefined} */
-    let eSimManagerRemote;
+  /** @type {!settings.ResetPageBrowserProxy} */
+  let resetPageBrowserProxy = null;
 
-    setup(function() {
-      lifetimeBrowserProxy = new settings.TestLifetimeBrowserProxy();
-      settings.LifetimeBrowserProxyImpl.setInstance(lifetimeBrowserProxy);
+  /** @type {!LifetimeBrowserProxy} */
+  let lifetimeBrowserProxy = null;
 
-      resetPageBrowserProxy = new reset_page.TestOsResetBrowserProxy();
-      settings.OsResetBrowserProxyImpl.instance_ = resetPageBrowserProxy;
+  /** @type {!ash.cellularSetup.mojom.ESimManagerRemote|undefined} */
+  let eSimManagerRemote;
 
-      eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
-      cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
+  setup(function() {
+    lifetimeBrowserProxy = new TestLifetimeBrowserProxy();
+    LifetimeBrowserProxyImpl.setInstance(lifetimeBrowserProxy);
 
-      PolymerTest.clearBody();
-      resetPage = document.createElement('os-settings-reset-page');
-      document.body.appendChild(resetPage);
-      Polymer.dom.flush();
-    });
+    resetPageBrowserProxy = new TestOsResetBrowserProxy();
+    OsResetBrowserProxyImpl.instance_ = resetPageBrowserProxy;
 
-    teardown(function() {
-      settings.Router.getInstance().resetRouteForTesting();
-      resetPage.remove();
-    });
+    eSimManagerRemote = new FakeESimManagerRemote();
+    setESimManagerRemoteForTesting(eSimManagerRemote);
 
-    function flushAsync() {
-      Polymer.dom.flush();
-      // Use setTimeout to wait for the next macrotask.
-      return new Promise(resolve => setTimeout(resolve));
-    }
-
-    /**
-     * @param {function(SettingsPowerwashDialogElement):!Element}
-     *     closeButtonFn A function that returns the button to be used for
-     *     closing the dialog.
-     * @return {!Promise}
-     */
-    async function testOpenClosePowerwashDialog(closeButtonFn) {
-      // Open powerwash dialog.
-      assertTrue(!!resetPage);
-      resetPage.$$('#powerwash').click();
-      await flushAsync();
-      const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
-      const onDialogClosed = new Promise(function(resolve, reject) {
-        dialog.addEventListener('close', function() {
-          assertFalse(dialog.$.dialog.open);
-          resolve();
-        });
-      });
-
-      closeButtonFn(dialog).click();
-      return Promise.all([
-        onDialogClosed,
-        resetPageBrowserProxy.whenCalled('onPowerwashDialogShow'),
-      ]);
-    }
-
-    async function openDialogWithESimWarning() {
-      eSimManagerRemote.addEuiccForTest(2);
-
-      // Set the first profile's state to kActive.
-      const euicc = (await eSimManagerRemote.getAvailableEuiccs()).euiccs[0];
-      const profile = (await euicc.getProfileList()).profiles[0];
-      profile.properties.state = ash.cellularSetup.mojom.ProfileState.kActive;
-
-      // Click the powerwash button.
-      resetPage.$$('#powerwash').click();
-      await flushAsync();
-
-      // The eSIM warning should be showing.
-      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ true);
-      const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertEquals(dialog.$$('iron-list').items.length, 1);
-
-      // The 'Continue' button should initially be disabled.
-      assertTrue(dialog.$$('#continue').disabled);
-    }
-
-    /**
-     * @param {boolean} shouldBeShowingESimWarning
-     */
-    function assertOpenDialogUIState(shouldBeShowingESimWarning) {
-      const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertTrue(!!dialog);
-      assertTrue(dialog.$.dialog.open);
-
-      assertEquals(
-          !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
-      assertEquals(
-          !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
-      assertEquals(!!dialog.$$('#powerwash'), !shouldBeShowingESimWarning);
-
-      assertEquals(
-          !!dialog.$$('#profilesListContainer'), shouldBeShowingESimWarning);
-      assertEquals(!!dialog.$$('#continue'), shouldBeShowingESimWarning);
-    }
-
-    /**
-     * Navigates to the deep link provided by |settingId| and returns true if
-     * the focused element is |deepLinkElement|.
-     * @param {!Element} deepLinkElement
-     * @param {!string} settingId
-     * @returns {!boolean}
-     */
-    async function isDeepLinkFocusedForSettingId(deepLinkElement, settingId) {
-      const params = new URLSearchParams();
-      params.append('settingId', settingId);
-      settings.Router.getInstance().navigateTo(
-          settings.routes.OS_RESET, params);
-
-      await test_util.waitAfterNextRender(deepLinkElement);
-      return deepLinkElement === getDeepActiveElement();
-    }
-
-    // Tests that the powerwash dialog with no EUICC opens and closes correctly,
-    // and that chrome.send calls are propagated as expected.
-    test(TestNames.PowerwashDialogOpenClose, function() {
-      // Test case where the 'cancel' button is clicked.
-      return testOpenClosePowerwashDialog(function(dialog) {
-        return dialog.$.cancel;
-      });
-    });
-
-    // Tests that when powerwash is requested chrome.send calls are
-    // propagated as expected.
-    test(TestNames.PowerwashDialogAction, async () => {
-      // Open powerwash dialog.
-      resetPage.$$('#powerwash').click();
-      await flushAsync();
-      const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
-      dialog.$$('#powerwash').click();
-      const requestTpmFirmwareUpdate =
-          await lifetimeBrowserProxy.whenCalled('factoryReset');
-      assertFalse(requestTpmFirmwareUpdate);
-    });
-
-    // Tests that when the route changes to one containing a deep link to
-    // powerwash, powerwash is focused.
-    test(TestNames.PowerwashFocusDeepLink, async () => {
-      assertTrue(
-          await isDeepLinkFocusedForSettingId(
-              resetPage.$$('#powerwash'), '1600'),
-          'Powerwash should be focused for settingId=1600.');
-    });
-
-    // Tests that when the route changes to one containing a deep link not equal
-    // to powerwash, no focusing of powerwash occurs.
-    test(TestNames.PowerwashFocusDeepLinkWrongId, async () => {
-      assertFalse(
-          await isDeepLinkFocusedForSettingId(
-              resetPage.$$('#powerwash'), '1234'),
-          'Powerwash should not be focused for settingId=1234.');
-    });
-
-    test(
-        'EUICC with no non-pending profiles shows powerwash dialog',
-        async () => {
-          eSimManagerRemote.addEuiccForTest(2);
-
-          return testOpenClosePowerwashDialog(function(dialog) {
-            return dialog.$.cancel;
-          });
-        });
-
-    test('Non-pending profile shows eSIM warning dialog', async () => {
-      await openDialogWithESimWarning();
-
-      // Clicking the checkbox should enable the 'Continue' button.
-      const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      const continueButton = dialog.$$('#continue');
-      dialog.$$('cr-checkbox').click();
-      assertFalse(continueButton.disabled);
-
-      // Click the 'Continue' button.
-      continueButton.click();
-      await flushAsync();
-      // The powerwash UI should now be showing.
-      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
-    });
-
-    test(
-        'eSIM warning dialog link click goes to mobile data subpage',
-        async () => {
-          await openDialogWithESimWarning();
-
-          const dialog = resetPage.$$('os-settings-powerwash-dialog');
-          const mobileSettingsLink =
-              dialog.$$('localized-link').shadowRoot.querySelector('a');
-          assertTrue(!!mobileSettingsLink);
-
-          mobileSettingsLink.click();
-          await flushAsync();
-
-          assertEquals(
-              settings.routes.INTERNET_NETWORKS,
-              settings.Router.getInstance().getCurrentRoute());
-          assertEquals(
-              'type=Cellular',
-              settings.Router.getInstance().getQueryParameters().toString());
-        });
+    PolymerTest.clearBody();
+    resetPage = document.createElement('os-settings-reset-page');
+    document.body.appendChild(resetPage);
+    flush();
   });
 
-  // #cr_define_end
-  return {};
+  teardown(function() {
+    Router.getInstance().resetRouteForTesting();
+    resetPage.remove();
+  });
+
+  function flushAsync() {
+    flush();
+    // Use setTimeout to wait for the next macrotask.
+    return new Promise(resolve => setTimeout(resolve));
+  }
+
+  /**
+   * @param {function(SettingsPowerwashDialogElement):!Element}
+   *     closeButtonFn A function that returns the button to be used for
+   *     closing the dialog.
+   * @return {!Promise}
+   */
+  async function testOpenClosePowerwashDialog(closeButtonFn) {
+    // Open powerwash dialog.
+    assertTrue(!!resetPage);
+    resetPage.$$('#powerwash').click();
+    await flushAsync();
+    const dialog = resetPage.$$('os-settings-powerwash-dialog');
+    assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
+    const onDialogClosed = new Promise(function(resolve, reject) {
+      dialog.addEventListener('close', function() {
+        assertFalse(dialog.$.dialog.open);
+        resolve();
+      });
+    });
+
+    closeButtonFn(dialog).click();
+    return Promise.all([
+      onDialogClosed,
+      resetPageBrowserProxy.whenCalled('onPowerwashDialogShow'),
+    ]);
+  }
+
+  async function openDialogWithESimWarning() {
+    eSimManagerRemote.addEuiccForTest(2);
+
+    // Set the first profile's state to kActive.
+    const euicc = (await eSimManagerRemote.getAvailableEuiccs()).euiccs[0];
+    const profile = (await euicc.getProfileList()).profiles[0];
+    profile.properties.state = ash.cellularSetup.mojom.ProfileState.kActive;
+
+    // Click the powerwash button.
+    resetPage.$$('#powerwash').click();
+    await flushAsync();
+
+    // The eSIM warning should be showing.
+    assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ true);
+    const dialog = resetPage.$$('os-settings-powerwash-dialog');
+    assertEquals(dialog.$$('iron-list').items.length, 1);
+
+    // The 'Continue' button should initially be disabled.
+    assertTrue(dialog.$$('#continue').disabled);
+  }
+
+  /**
+   * @param {boolean} shouldBeShowingESimWarning
+   */
+  function assertOpenDialogUIState(shouldBeShowingESimWarning) {
+    const dialog = resetPage.$$('os-settings-powerwash-dialog');
+    assertTrue(!!dialog);
+    assertTrue(dialog.$.dialog.open);
+
+    assertEquals(
+        !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
+    assertEquals(
+        !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
+    assertEquals(!!dialog.$$('#powerwash'), !shouldBeShowingESimWarning);
+
+    assertEquals(
+        !!dialog.$$('#profilesListContainer'), shouldBeShowingESimWarning);
+    assertEquals(!!dialog.$$('#continue'), shouldBeShowingESimWarning);
+  }
+
+  /**
+   * Navigates to the deep link provided by |settingId| and returns true if
+   * the focused element is |deepLinkElement|.
+   * @param {!Element} deepLinkElement
+   * @param {!string} settingId
+   * @returns {!boolean}
+   */
+  async function isDeepLinkFocusedForSettingId(deepLinkElement, settingId) {
+    const params = new URLSearchParams();
+    params.append('settingId', settingId);
+    Router.getInstance().navigateTo(routes.OS_RESET, params);
+
+    await waitAfterNextRender(deepLinkElement);
+    return deepLinkElement === getDeepActiveElement();
+  }
+
+  // Tests that the powerwash dialog with no EUICC opens and closes correctly,
+  // and that chrome.send calls are propagated as expected.
+  test(TestNames.PowerwashDialogOpenClose, function() {
+    // Test case where the 'cancel' button is clicked.
+    return testOpenClosePowerwashDialog(function(dialog) {
+      return dialog.$.cancel;
+    });
+  });
+
+  // Tests that when powerwash is requested chrome.send calls are
+  // propagated as expected.
+  test(TestNames.PowerwashDialogAction, async () => {
+    // Open powerwash dialog.
+    resetPage.$$('#powerwash').click();
+    await flushAsync();
+    const dialog = resetPage.$$('os-settings-powerwash-dialog');
+    assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
+    dialog.$$('#powerwash').click();
+    const requestTpmFirmwareUpdate =
+        await lifetimeBrowserProxy.whenCalled('factoryReset');
+    assertFalse(requestTpmFirmwareUpdate);
+  });
+
+  // Tests that when the route changes to one containing a deep link to
+  // powerwash, powerwash is focused.
+  test(TestNames.PowerwashFocusDeepLink, async () => {
+    assertTrue(
+        await isDeepLinkFocusedForSettingId(resetPage.$$('#powerwash'), '1600'),
+        'Powerwash should be focused for settingId=1600.');
+  });
+
+  // Tests that when the route changes to one containing a deep link not equal
+  // to powerwash, no focusing of powerwash occurs.
+  test(TestNames.PowerwashFocusDeepLinkWrongId, async () => {
+    assertFalse(
+        await isDeepLinkFocusedForSettingId(resetPage.$$('#powerwash'), '1234'),
+        'Powerwash should not be focused for settingId=1234.');
+  });
+
+  test(
+      'EUICC with no non-pending profiles shows powerwash dialog', async () => {
+        eSimManagerRemote.addEuiccForTest(2);
+
+        return testOpenClosePowerwashDialog(function(dialog) {
+          return dialog.$.cancel;
+        });
+      });
+
+  test('Non-pending profile shows eSIM warning dialog', async () => {
+    await openDialogWithESimWarning();
+
+    // Clicking the checkbox should enable the 'Continue' button.
+    const dialog = resetPage.$$('os-settings-powerwash-dialog');
+    const continueButton = dialog.$$('#continue');
+    dialog.$$('cr-checkbox').click();
+    assertFalse(continueButton.disabled);
+
+    // Click the 'Continue' button.
+    continueButton.click();
+    await flushAsync();
+    // The powerwash UI should now be showing.
+    assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
+  });
+
+  test(
+      'eSIM warning dialog link click goes to mobile data subpage',
+      async () => {
+        await openDialogWithESimWarning();
+
+        const dialog = resetPage.$$('os-settings-powerwash-dialog');
+        const mobileSettingsLink =
+            dialog.$$('localized-link').shadowRoot.querySelector('a');
+        assertTrue(!!mobileSettingsLink);
+
+        mobileSettingsLink.click();
+        await flushAsync();
+
+        assertEquals(
+            routes.INTERNET_NETWORKS, Router.getInstance().getCurrentRoute());
+        assertEquals(
+            'type=Cellular',
+            Router.getInstance().getQueryParameters().toString());
+      });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_search_page_test.js b/chrome/test/data/webui/settings/chromeos/os_search_page_test.js
index f70d3d3..31dbccd5 100644
--- a/chrome/test/data/webui/settings/chromeos/os_search_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_search_page_test.js
@@ -2,15 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
-// #import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {assertEquals, assertTrue} from '../../chai_assert.js';
 
 suite('OSSearchPageTests', function() {
   /** @type {?SettingsSearchPageElement} */
@@ -22,19 +19,18 @@
     });
     page = document.createElement('os-settings-search-page');
     document.body.appendChild(page);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
     page.remove();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
   });
 
   test('Deep link to preferred search engine', async () => {
     const params = new URLSearchParams();
     params.append('settingId', '600');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.OS_SEARCH, params);
+    Router.getInstance().navigateTo(routes.OS_SEARCH, params);
 
     let deepLinkElement;
     if (loadTimeData.getBoolean('syncSettingsCategorizationEnabled')) {
@@ -47,7 +43,7 @@
           page.$$('settings-search-engine').$$('#searchSelectionDialogButton');
     }
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Preferred search dropdown should be focused for settingId=600.');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_main_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_main_test.js
index 4e66150..34a1b2d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_main_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_main_test.js
@@ -2,142 +2,133 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {CrSettingsPrefs, osPageVisibility, Router, routes, setContactManagerForTesting, setNearbyShareSettingsForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// #import {CrSettingsPrefs, osPageVisibility, Router, routes, setNearbyShareSettingsForTesting, setContactManagerForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
-// #import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
-// #import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
-// clang-format on
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
+import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
 
-cr.define('settings_main_page', function() {
-  let settingsPrefs = null;
-  /** @type {!nearby_share.FakeContactManager} */
-  let fakeContactManager = null;
-  /** @type {!nearby_share.FakeNearbyShareSettings} */
-  let fakeSettings = null;
+let settingsPrefs = null;
+/** @type {!FakeContactManager} */
+let fakeContactManager = null;
+/** @type {!FakeNearbyShareSettings} */
+let fakeSettings = null;
 
-  suiteSetup(function() {
-    settingsPrefs = document.createElement('settings-prefs');
-    return CrSettingsPrefs.initialized;
+suiteSetup(function() {
+  settingsPrefs = document.createElement('settings-prefs');
+  return CrSettingsPrefs.initialized;
+});
+
+suite('MainPageTests', function() {
+  /** @type {?SettingsMainElement} */
+  let settingsMain = null;
+
+  setup(function() {
+    fakeContactManager = new FakeContactManager();
+    setContactManagerForTesting(fakeContactManager);
+    fakeContactManager.setupContactRecords();
+
+    fakeSettings = new FakeNearbyShareSettings();
+    setNearbyShareSettingsForTesting(fakeSettings);
+
+    Router.getInstance().navigateTo(routes.BASIC);
+    PolymerTest.clearBody();
+    settingsMain = document.createElement('os-settings-main');
+    settingsMain.prefs = settingsPrefs.prefs;
+    settingsMain.toolbarSpinnerActive = false;
+    settingsMain.pageVisibility = osPageVisibility;
+    document.body.appendChild(settingsMain);
   });
 
-  suite('MainPageTests', function() {
-    /** @type {?SettingsMainElement} */
-    let settingsMain = null;
-
-    setup(function() {
-      fakeContactManager = new nearby_share.FakeContactManager();
-      nearby_share.setContactManagerForTesting(fakeContactManager);
-      fakeContactManager.setupContactRecords();
-
-      fakeSettings = new nearby_share.FakeNearbyShareSettings();
-      nearby_share.setNearbyShareSettingsForTesting(fakeSettings);
-
-      settings.Router.getInstance().navigateTo(settings.routes.BASIC);
-      PolymerTest.clearBody();
-      settingsMain = document.createElement('os-settings-main');
-      settingsMain.prefs = settingsPrefs.prefs;
-      settingsMain.toolbarSpinnerActive = false;
-      settingsMain.pageVisibility = settings.osPageVisibility;
-      document.body.appendChild(settingsMain);
-    });
-
-    teardown(function() {
-      settingsMain.remove();
-    });
-
-    function showManagedHeader() {
-      return settingsMain.showManagedHeader_(
-          settingsMain.inSearchMode_, settingsMain.showingSubpage_,
-          settingsMain.showPages_.about);
-    }
-
-    test('managed header hides when showing subpage', function() {
-      Polymer.dom.flush();
-
-      assertTrue(showManagedHeader());
-
-      const page = settingsMain.$$('os-settings-page');
-      page.fire('subpage-expand', {});
-
-      assertFalse(showManagedHeader());
-    });
-
-    test('managed header hides when showing about page', function() {
-      Polymer.dom.flush();
-
-      assertTrue(showManagedHeader());
-      settings.Router.getInstance().navigateTo(settings.routes.ABOUT);
-
-      assertFalse(showManagedHeader());
-    });
-
-    /** @return {!HTMLElement} */
-    function getToggleContainer() {
-      const page = settingsMain.$$('os-settings-page');
-      assertTrue(!!page);
-      const toggleContainer = page.$$('#toggleContainer');
-      assertTrue(!!toggleContainer);
-      return toggleContainer;
-    }
-
-    /**
-     * Asserts that the Advanced toggle container exists in the combined
-     * settings page and asserts whether it should be visible.
-     * @param {boolean} expectedVisible
-     */
-    function assertToggleContainerVisible(expectedVisible) {
-      const toggleContainer = getToggleContainer();
-      if (expectedVisible) {
-        assertNotEquals('none', toggleContainer.style.display);
-      } else {
-        assertEquals('none', toggleContainer.style.display);
-      }
-    }
-
-    /**
-     * Asserts the visibility of the basic and advanced pages.
-     * @param {string} Expected 'display' value for the basic page.
-     * @param {string} Expected 'display' value for the advanced page.
-     */
-    async function assertPageVisibility(expectedBasic, expectedAdvanced) {
-      Polymer.dom.flush();
-      const page = settingsMain.$$('os-settings-page');
-      assertEquals(
-          expectedBasic, getComputedStyle(page.$$('#basicPage')).display);
-
-      const advancedPage = await page.$$('#advancedPageTemplate').get();
-      assertEquals(expectedAdvanced, getComputedStyle(advancedPage).display);
-    }
-
-    test('navigating to a basic page does not collapse advanced', async () => {
-      settings.Router.getInstance().navigateTo(settings.routes.DATETIME);
-      Polymer.dom.flush();
-
-      assertToggleContainerVisible(true);
-
-      settings.Router.getInstance().navigateTo(settings.routes.DEVICE);
-      Polymer.dom.flush();
-
-      await assertPageVisibility('block', 'block');
-    });
-
-    test('updates the title based on current route', function() {
-      settings.Router.getInstance().navigateTo(settings.routes.BASIC);
-      assertEquals(document.title, loadTimeData.getString('settings'));
-
-      settings.Router.getInstance().navigateTo(settings.routes.ABOUT);
-      assertEquals(
-          document.title,
-          loadTimeData.getStringF(
-              'settingsAltPageTitle',
-              loadTimeData.getString('aboutPageTitle')));
-    });
+  teardown(function() {
+    settingsMain.remove();
   });
-  // #cr_define_end
+
+  function showManagedHeader() {
+    return settingsMain.showManagedHeader_(
+        settingsMain.inSearchMode_, settingsMain.showingSubpage_,
+        settingsMain.showPages_.about);
+  }
+
+  test('managed header hides when showing subpage', function() {
+    flush();
+
+    assertTrue(showManagedHeader());
+
+    const page = settingsMain.$$('os-settings-page');
+    page.fire('subpage-expand', {});
+
+    assertFalse(showManagedHeader());
+  });
+
+  test('managed header hides when showing about page', function() {
+    flush();
+
+    assertTrue(showManagedHeader());
+    Router.getInstance().navigateTo(routes.ABOUT);
+
+    assertFalse(showManagedHeader());
+  });
+
+  /** @return {!HTMLElement} */
+  function getToggleContainer() {
+    const page = settingsMain.$$('os-settings-page');
+    assertTrue(!!page);
+    const toggleContainer = page.$$('#toggleContainer');
+    assertTrue(!!toggleContainer);
+    return toggleContainer;
+  }
+
+  /**
+   * Asserts that the Advanced toggle container exists in the combined
+   * settings page and asserts whether it should be visible.
+   * @param {boolean} expectedVisible
+   */
+  function assertToggleContainerVisible(expectedVisible) {
+    const toggleContainer = getToggleContainer();
+    if (expectedVisible) {
+      assertNotEquals('none', toggleContainer.style.display);
+    } else {
+      assertEquals('none', toggleContainer.style.display);
+    }
+  }
+
+  /**
+   * Asserts the visibility of the basic and advanced pages.
+   * @param {string} Expected 'display' value for the basic page.
+   * @param {string} Expected 'display' value for the advanced page.
+   */
+  async function assertPageVisibility(expectedBasic, expectedAdvanced) {
+    flush();
+    const page = settingsMain.$$('os-settings-page');
+    assertEquals(
+        expectedBasic, getComputedStyle(page.$$('#basicPage')).display);
+
+    const advancedPage = await page.$$('#advancedPageTemplate').get();
+    assertEquals(expectedAdvanced, getComputedStyle(advancedPage).display);
+  }
+
+  test('navigating to a basic page does not collapse advanced', async () => {
+    Router.getInstance().navigateTo(routes.DATETIME);
+    flush();
+
+    assertToggleContainerVisible(true);
+
+    Router.getInstance().navigateTo(routes.DEVICE);
+    flush();
+
+    await assertPageVisibility('block', 'block');
+  });
+
+  test('updates the title based on current route', function() {
+    Router.getInstance().navigateTo(routes.BASIC);
+    assertEquals(document.title, loadTimeData.getString('settings'));
+
+    Router.getInstance().navigateTo(routes.ABOUT);
+    assertEquals(
+        document.title,
+        loadTimeData.getStringF(
+            'settingsAltPageTitle', loadTimeData.getString('aboutPageTitle')));
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
index 31f19e54..d8fdb21 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_menu_test.js
@@ -2,30 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
-
-// #import {Router, routes, Route, osPageVisibility} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// clang-format on
+import {osPageVisibility, Route, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /** @fileoverview Runs tests for the OS settings menu. */
 
 function setupRouter() {
   const testRoutes = {
-    BASIC: new settings.Route('/'),
-    ADVANCED: new settings.Route('/advanced'),
+    BASIC: new Route('/'),
+    ADVANCED: new Route('/advanced'),
   };
   testRoutes.BLUETOOTH =
       testRoutes.BASIC.createSection('/bluetooth', 'bluetooth');
   testRoutes.RESET = testRoutes.ADVANCED.createSection('/osReset', 'osReset');
 
-  settings.Router.resetInstanceForTesting(new settings.Router(testRoutes));
+  Router.resetInstanceForTesting(new Router(testRoutes));
 
-  settings.routes.RESET = testRoutes.RESET;
-  settings.routes.BLUETOOTH = testRoutes.BLUETOOTH;
-  settings.routes.ADVANCED = testRoutes.ADVANCED;
-  settings.routes.BASIC = testRoutes.BASIC;
+  routes.RESET = testRoutes.RESET;
+  routes.BLUETOOTH = testRoutes.BLUETOOTH;
+  routes.ADVANCED = testRoutes.ADVANCED;
+  routes.BASIC = testRoutes.BASIC;
 }
 
 suite('OSSettingsMenu', function() {
@@ -35,7 +31,7 @@
     setupRouter();
     PolymerTest.clearBody();
     settingsMenu = document.createElement('os-settings-menu');
-    settingsMenu.pageVisibility = settings.osPageVisibility;
+    settingsMenu.pageVisibility = osPageVisibility;
     document.body.appendChild(settingsMenu);
   });
 
@@ -46,11 +42,11 @@
   test('advancedOpenedBinding', function() {
     assertFalse(settingsMenu.advancedOpened);
     settingsMenu.advancedOpened = true;
-    Polymer.dom.flush();
+    flush();
     assertTrue(settingsMenu.isAdvancedSubmenuOpenedForTest());
 
     settingsMenu.advancedOpened = false;
-    Polymer.dom.flush();
+    flush();
     assertFalse(settingsMenu.isAdvancedSubmenuOpenedForTest());
   });
 
@@ -61,11 +57,11 @@
     assertTrue(!!advancedToggle);
 
     advancedToggle.click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(settingsMenu.isAdvancedSubmenuOpenedForTest());
 
     advancedToggle.click();
-    Polymer.dom.flush();
+    flush();
     assertFalse(settingsMenu.isAdvancedSubmenuOpenedForTest());
   });
 
@@ -76,25 +72,25 @@
     assertTrue(!!ironIconElement);
 
     settingsMenu.advancedOpened = true;
-    Polymer.dom.flush();
+    flush();
     const openIcon = ironIconElement.icon;
     assertTrue(!!openIcon);
 
     settingsMenu.advancedOpened = false;
-    Polymer.dom.flush();
+    flush();
     assertNotEquals(openIcon, ironIconElement.icon);
   });
 
   test('Advanced menu expands on navigating to an advanced setting', () => {
     assertFalse(settingsMenu.advancedOpened);
-    settings.Router.getInstance().navigateTo(settings.routes.RESET);
+    Router.getInstance().navigateTo(routes.RESET);
     assertFalse(settingsMenu.advancedOpened);
 
     // If there are search params and the current route is a descendant of
     // the Advanced route, then ensure that the advanced menu expands.
     const params = new URLSearchParams('search=test');
-    settings.Router.getInstance().navigateTo(settings.routes.RESET, params);
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.RESET, params);
+    flush();
     assertTrue(settingsMenu.advancedOpened);
   });
 });
@@ -105,10 +101,10 @@
   setup(function() {
     setupRouter();
     PolymerTest.clearBody();
-    settings.Router.getInstance().navigateTo(settings.routes.RESET, '');
+    Router.getInstance().navigateTo(routes.RESET, '');
     settingsMenu = document.createElement('os-settings-menu');
     document.body.appendChild(settingsMenu);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
@@ -126,8 +122,8 @@
     let path = new window.URL(selector.selected).pathname;
     assertEquals('/osReset', path);
 
-    settings.Router.getInstance().navigateTo(settings.routes.BLUETOOTH, '');
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.BLUETOOTH, '');
+    flush();
 
     path = new window.URL(selector.selected).pathname;
     assertEquals('/bluetooth', path);
@@ -138,8 +134,8 @@
     const path = new window.URL(selector.selected).pathname;
     assertEquals('/osReset', path);
 
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC, '');
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.BASIC, '');
+    flush();
 
     // BASIC has no sub page selected.
     assertFalse(!!selector.selected);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
index 98478d66..d924bd6 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
@@ -2,19 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/lazy_load.js';
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import 'chrome://os-settings/chromeos/lazy_load.js';
 
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {CrSettingsPrefs, Router, routes, setNearbyShareSettingsForTesting, setContactManagerForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {FakeBluetoothConfig} from 'chrome://test/cr_components/chromeos/bluetooth/fake_bluetooth_config.js';
-// #import {setBluetoothConfigForTesting} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
-// #import {flushTasks} from 'chrome://test/test_util.js';
-// #import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
-// #import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
-// clang-format on
+import {CrSettingsPrefs, Router, routes, setContactManagerForTesting, setNearbyShareSettingsForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+import {setBluetoothConfigForTesting} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {FakeBluetoothConfig} from 'chrome://test/cr_components/chromeos/bluetooth/fake_bluetooth_config.js';
+
+import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
+import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
 
 suite('OsSettingsPageTests', function() {
   /** @type {?OsSettingsPageElement} */
@@ -23,9 +20,9 @@
   /** @type {?SettingsPrefsElement} */
   let prefElement = null;
 
-  /** @type {!nearby_share.FakeContactManager} */
+  /** @type {!FakeContactManager} */
   let fakeContactManager = null;
-  /** @type {!nearby_share.FakeNearbyShareSettings} */
+  /** @type {!FakeNearbyShareSettings} */
   let fakeSettings = null;
 
   suiteSetup(async function() {
@@ -33,14 +30,14 @@
       enableBluetoothRevamp: false,
     });
 
-    fakeContactManager = new nearby_share.FakeContactManager();
-    nearby_share.setContactManagerForTesting(fakeContactManager);
+    fakeContactManager = new FakeContactManager();
+    setContactManagerForTesting(fakeContactManager);
     fakeContactManager.setupContactRecords();
 
-    fakeSettings = new nearby_share.FakeNearbyShareSettings();
-    nearby_share.setNearbyShareSettingsForTesting(fakeSettings);
+    fakeSettings = new FakeNearbyShareSettings();
+    setNearbyShareSettingsForTesting(fakeSettings);
 
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC);
+    Router.getInstance().navigateTo(routes.BASIC);
     PolymerTest.clearBody();
 
     prefElement = document.createElement('settings-prefs');
@@ -52,14 +49,14 @@
   teardown(function() {
     settingsPage.remove();
     CrSettingsPrefs.resetForTesting();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
   });
 
   function init() {
     settingsPage = document.createElement('os-settings-page');
     settingsPage.prefs = prefElement.prefs;
     document.body.appendChild(settingsPage);
-    Polymer.dom.flush();
+    flush();
   }
 
   test('Os Settings Page created', async () => {
@@ -77,7 +74,7 @@
     init();
     const idleRender = settingsPage.$$('settings-idle-load');
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const osSettingsPrintingPage = settingsPage.$$('os-settings-printing-page');
     assert(!!osSettingsPrintingPage);
   });
@@ -123,7 +120,7 @@
     init();
     const osSettingsPrivacyPage = settingsPage.$$('os-settings-privacy-page');
     assert(!!osSettingsPrivacyPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Check settings-multidevice-page exists', async () => {
@@ -143,7 +140,7 @@
     init();
     const idleRender = settingsPage.$$('settings-idle-load');
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const settingsDateTimePage = settingsPage.$$('settings-date-time-page');
     assert(!!settingsDateTimePage);
   });
@@ -153,7 +150,7 @@
     const idleRender = settingsPage.$$('settings-idle-load');
     assert(!!idleRender);
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const osSettingsLangagesSection =
         settingsPage.$$('os-settings-languages-section');
     assert(!!osSettingsLangagesSection);
@@ -164,7 +161,7 @@
     const idleRender = settingsPage.$$('settings-idle-load');
     assert(!!idleRender);
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const osSettingsA11yPage = settingsPage.$$('os-settings-a11y-page');
     assert(!!osSettingsA11yPage);
   });
@@ -175,11 +172,11 @@
     const idleRender = settingsPage.$$('settings-idle-load');
     assert(!!idleRender);
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
 
     const settingsKerberosPage = settingsPage.$$('settings-kerberos-page');
     assert(!!settingsKerberosPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Check settings-device-page exists', async () => {
@@ -188,7 +185,7 @@
     settingsPage.allowCrostini_ = true;
     const settingsDevicePage = settingsPage.$$('settings-device-page');
     assert(!!settingsDevicePage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Check os-settings-files-page exists', async () => {
@@ -196,7 +193,7 @@
     settingsPage.isGuestMode_ = false;
     const idleRender = settingsPage.$$('settings-idle-load');
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const settingsFilesPage = settingsPage.$$('os-settings-files-page');
     assert(!!settingsFilesPage);
   });
@@ -219,7 +216,7 @@
     settingsPage.showAndroidApps = true;
     settingsPage.showPluginVm = true;
     settingsPage.havePlayStoreApp = true;
-    Polymer.dom.flush();
+    flush();
     const osSettingsAppsPage = settingsPage.$$('os-settings-apps-page');
     assert(!!osSettingsAppsPage);
   });
@@ -229,7 +226,7 @@
     settingsPage.showCrostini = true;
     const idleRender = settingsPage.$$('settings-idle-load');
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
     const osSettingsCrostiniPage = settingsPage.$$('settings-crostini-page');
     assert(!!osSettingsCrostiniPage);
   });
@@ -241,7 +238,7 @@
     await idleRender.get();
 
     settingsPage.showReset = true;
-    Polymer.dom.flush();
+    flush();
     const osSettingsResetPage = settingsPage.$$('os-settings-reset-page');
     assert(!!osSettingsResetPage);
   });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
index d8b112a..d87add2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
@@ -2,24 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {OpenWindowProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {setSearchHandlerForTesting, setUserActionRecorderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {eventToPromise} from 'chrome://test/test_util.js';
 
-// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
-// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {OpenWindowProxyImpl, Router, Route, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {eventToPromise} from 'chrome://test/test_util.js';
-// #import {FakeUserActionRecorder} from './fake_user_action_recorder.js';
-// #import {FakeSettingsSearchHandler} from './fake_settings_search_handler.m.js';
-// #import {setSearchHandlerForTesting, setUserActionRecorderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
-// clang-format on
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
+
+import {FakeSettingsSearchHandler} from './fake_settings_search_handler.js';
+import {FakeUserActionRecorder} from './fake_user_action_recorder.js';
 
 /** @fileoverview Runs tests for the OS settings search box. */
 
 /**
- * @implements {settings.OpenWindowProxy}
+ * @implements {OpenWindowProxy}
  */
 class TestOpenWindowProxy extends TestBrowserProxy {
   constructor() {
@@ -92,19 +90,19 @@
     field.onSearchTermInput();
     field.onSearchTermSearch();
     await settingsSearchHandler.search;
-    Polymer.dom.flush();
+    flush();
   }
 
   async function waitForListUpdate() {
     // Wait for iron-list to complete resizing.
-    await test_util.eventToPromise('iron-resize', resultList);
-    Polymer.dom.flush();
+    await eventToPromise('iron-resize', resultList);
+    flush();
   }
 
   async function waitForResultsFetched() {
     // Wait for search results to be fetched.
-    await test_util.eventToPromise('search-results-fetched', searchBox);
-    Polymer.dom.flush();
+    await eventToPromise('search-results-fetched', searchBox);
+    flush();
   }
 
   /**
@@ -139,16 +137,16 @@
 
   function setupSearchBox() {
     chrome.metricsPrivate = new MockMetricsPrivate();
-    settingsSearchHandler = new settings.FakeSettingsSearchHandler();
-    settings.setSearchHandlerForTesting(settingsSearchHandler);
+    settingsSearchHandler = new FakeSettingsSearchHandler();
+    setSearchHandlerForTesting(settingsSearchHandler);
 
-    userActionRecorder = new settings.FakeUserActionRecorder();
-    settings.setUserActionRecorderForTesting(userActionRecorder);
+    userActionRecorder = new FakeUserActionRecorder();
+    setUserActionRecorderForTesting(userActionRecorder);
 
     toolbar = document.createElement('os-toolbar');
     assertTrue(!!toolbar);
     document.body.appendChild(toolbar);
-    Polymer.dom.flush();
+    flush();
 
     searchBox = toolbar.$$('os-settings-search-box');
     assertTrue(!!searchBox);
@@ -164,7 +162,7 @@
 
   setup(function() {
     setupSearchBox();
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC);
+    Router.getInstance().navigateTo(routes.BASIC);
 
     openWindowProxy = new TestOpenWindowProxy();
     OpenWindowProxyImpl.setInstance(openWindowProxy);
@@ -173,8 +171,8 @@
   teardown(async () => {
     // Clear search field for next test.
     await simulateSearch('');
-    settings.setUserActionRecorderForTesting(null);
-    settings.setSearchHandlerForTesting(null);
+    setUserActionRecorderForTesting(null);
+    setSearchHandlerForTesting(null);
   });
 
   test('Search availability changed', async () => {
@@ -359,9 +357,9 @@
     // Keydown with Enter key on the searchBox causes navigation to selected
     // row's route.
     searchBox.dispatchEvent(enterEvent);
-    Polymer.dom.flush();
+    flush();
     assertFalse(dropDown.opened);
-    const router = settings.Router.getInstance();
+    const router = Router.getInstance();
     assertEquals(router.getQueryParameters().get('search'), 'fake query');
     assertEquals(router.getCurrentRoute().path, '/networks');
     assertEquals(router.getQueryParameters().get('type'), 'WiFi');
@@ -390,7 +388,7 @@
         selectedOsRow.$.searchResultContainer.click();
         assertFalse(dropDown.opened);
         assertEquals(0, openWindowProxy.getCallCount('openURL'));
-        const router = settings.Router.getInstance();
+        const router = Router.getInstance();
         assertEquals(router.getQueryParameters().get('search'), 'fake query 2');
         assertEquals(router.getCurrentRoute().path, '/personalization');
         assertEquals(router.getQueryParameters().get('settingId'), '500');
@@ -411,7 +409,7 @@
         'keypress', {cancelable: true, key: 'Enter', keyCode: 13});
     selectedOsRow.$.searchResultContainer.dispatchEvent(enterEvent);
     assertFalse(dropDown.opened);
-    const router = settings.Router.getInstance();
+    const router = Router.getInstance();
     assertEquals(router.getQueryParameters().get('search'), 'fake query 1');
     assertEquals(router.getCurrentRoute().path, '/networks');
     assertEquals(router.getQueryParameters().get('type'), 'WiFi');
@@ -430,7 +428,7 @@
     searchResultRow.$.searchResultContainer.click();
 
     assertFalse(dropDown.opened);
-    const router = settings.Router.getInstance();
+    const router = Router.getInstance();
     assertEquals(router.getQueryParameters().get('search'), 'fake query 2');
     assertEquals(router.getCurrentRoute().path, '/networks');
     assertEquals(router.getQueryParameters().get('type'), 'WiFi');
@@ -604,12 +602,12 @@
 
   test('Focus search input behavior on attached', async () => {
     PolymerTest.clearBody();
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC);
+    Router.getInstance().navigateTo(routes.BASIC);
     setupSearchBox();
     assertEquals(field.root.activeElement, field.$.searchInput);
 
     PolymerTest.clearBody();
-    settings.Router.getInstance().navigateTo(settings.routes.KEYBOARD);
+    Router.getInstance().navigateTo(routes.KEYBOARD);
     assertEquals(field.root.activeElement, null);
   });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_test.js
index a83b45e..7e01d22 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_test.js
@@ -2,15 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {CrSettingsPrefs} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
 
-// #import {CrSettingsPrefs} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
-// clang-format on
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
 /** @fileoverview Suite of tests for the OS Settings ui and main page. */
 
@@ -23,7 +20,7 @@
     document.body.innerHTML = '';
     ui = document.createElement('os-settings-ui');
     document.body.appendChild(ui);
-    Polymer.dom.flush();
+    flush();
 
     await CrSettingsPrefs.initialized;
     settingsMain =
@@ -43,7 +40,7 @@
         settingsMain.$$('os-settings-page').$$('settings-idle-load');
     assert(!!idleRender);
     await idleRender.get();
-    Polymer.dom.flush();
+    flush();
   });
 
   /**
@@ -105,8 +102,8 @@
   test('AdvancedSections', async function() {
     // Open the Advanced section.
     settingsMain.advancedToggleExpanded = true;
-    Polymer.dom.flush();
-    await test_util.flushTasks();
+    flush();
+    await flushTasks();
 
     const sectionNames = [
       'osPrivacy', 'osLanguages', 'files', 'osReset', 'dateTime',
@@ -127,8 +124,8 @@
 
     // Ensure Advanced is open.
     settingsMain.advancedToggleExpanded = true;
-    Polymer.dom.flush();
-    await test_util.flushTasks();
+    flush();
+    await flushTasks();
 
     const hiddenSections = ['multidevice', 'osPeople', 'personalization'];
     for (const name of hiddenSections) {
@@ -150,24 +147,24 @@
   });
 
   test('Update required end of life banner visibility', function() {
-    Polymer.dom.flush();
+    flush();
     assertFalse(settingsPage.showUpdateRequiredEolBanner_);
     assertFalse(!!settingsPage.$$('#updateRequiredEolBanner'));
 
     settingsPage.showUpdateRequiredEolBanner_ = true;
-    Polymer.dom.flush();
+    flush();
     assertTrue(!!settingsPage.$$('#updateRequiredEolBanner'));
   });
 
   test('Update required end of life banner close button click', function() {
     settingsPage.showUpdateRequiredEolBanner_ = true;
-    Polymer.dom.flush();
+    flush();
     const banner = settingsPage.$$('#updateRequiredEolBanner');
     assertTrue(!!banner);
 
     const closeButton = assert(settingsPage.$$('#closeUpdateRequiredEol'));
     closeButton.click();
-    Polymer.dom.flush();
+    flush();
     assertFalse(settingsPage.showUpdateRequiredEolBanner_);
     assertEquals('none', banner.style.display);
   });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_test_2.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_test_2.js
index 9e39d622..531942d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_test_2.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_test_2.js
@@ -2,18 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {CrSettingsPrefs, Router, routes, setContactManagerForTesting, setNearbyShareSettingsForTesting, setUserActionRecorderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// #import {CrSettingsPrefs, Router, routes, setUserActionRecorderForTesting, setNearbyShareSettingsForTesting, setContactManagerForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {FakeUserActionRecorder} from './fake_user_action_recorder.js';
-// #import {eventToPromise, waitBeforeNextRender} from '../../../test_util.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
-// #import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
-// clang-format on
+import {eventToPromise, waitBeforeNextRender} from '../../../test_util.js';
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {FakeContactManager} from '../../nearby_share/shared/fake_nearby_contact_manager.m.js';
+import {FakeNearbyShareSettings} from '../../nearby_share/shared/fake_nearby_share_settings.m.js';
+
+import {FakeUserActionRecorder} from './fake_user_action_recorder.js';
 
 /**
  * Checks whether a given element is visible to the user.
@@ -27,34 +25,34 @@
 suite('os-settings-ui', () => {
   let ui;
   let userActionRecorder;
-  /** @type {!nearby_share.FakeContactManager} */
+  /** @type {!FakeContactManager} */
   let fakeContactManager = null;
-  /** @type {!nearby_share.FakeNearbyShareSettings} */
+  /** @type {!FakeNearbyShareSettings} */
   let fakeSettings = null;
 
   setup(async () => {
     PolymerTest.clearBody();
 
-    fakeContactManager = new nearby_share.FakeContactManager();
-    nearby_share.setContactManagerForTesting(fakeContactManager);
+    fakeContactManager = new FakeContactManager();
+    setContactManagerForTesting(fakeContactManager);
     fakeContactManager.setupContactRecords();
 
-    fakeSettings = new nearby_share.FakeNearbyShareSettings();
-    nearby_share.setNearbyShareSettingsForTesting(fakeSettings);
+    fakeSettings = new FakeNearbyShareSettings();
+    setNearbyShareSettingsForTesting(fakeSettings);
 
     ui = document.createElement('os-settings-ui');
     document.body.appendChild(ui);
     await CrSettingsPrefs.initialized;
-    userActionRecorder = new settings.FakeUserActionRecorder();
-    settings.setUserActionRecorderForTesting(userActionRecorder);
+    userActionRecorder = new FakeUserActionRecorder();
+    setUserActionRecorderForTesting(userActionRecorder);
     ui.$$('#drawerTemplate').if = false;
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(() => {
     ui.remove();
-    settings.setUserActionRecorderForTesting(null);
-    settings.Router.getInstance().resetRouteForTesting();
+    setUserActionRecorderForTesting(null);
+    Router.getInstance().resetRouteForTesting();
   });
 
   test('top container shadow always shows for sub-pages', () => {
@@ -65,8 +63,8 @@
         element.classList.contains('has-shadow'),
         'Main page should not show shadow ' + element.className);
 
-    settings.Router.getInstance().navigateTo(settings.routes.POWER);
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.POWER);
+    flush();
     assertTrue(
         element.classList.contains('has-shadow'),
         'Sub-page should show shadow ' + element.className);
@@ -87,8 +85,8 @@
     assertFalse(drawer.open);
 
     drawer.openDrawer();
-    Polymer.dom.flush();
-    await test_util.eventToPromise('cr-drawer-opened', drawer);
+    flush();
+    await eventToPromise('cr-drawer-opened', drawer);
 
     // Validate that dialog is open and menu is shown so it will animate.
     assertTrue(drawer.open);
@@ -107,12 +105,12 @@
     // Mimic narrow mode and open the drawer.
     ui.isNarrow = true;
     drawer.openDrawer();
-    Polymer.dom.flush();
-    await test_util.eventToPromise('cr-drawer-opened', drawer);
+    flush();
+    await eventToPromise('cr-drawer-opened', drawer);
 
     ui.isNarrow = false;
-    Polymer.dom.flush();
-    await test_util.eventToPromise('close', drawer);
+    flush();
+    await eventToPromise('close', drawer);
     assertFalse(drawer.open);
   });
 
@@ -129,7 +127,7 @@
     assertFalse(main.advancedToggleExpanded);
 
     main.advancedToggleExpanded = true;
-    Polymer.dom.flush();
+    flush();
 
     assertFalse(!!ui.$$('cr-drawer os-settings-menu'));
     assertTrue(ui.advancedOpenedInMain_);
@@ -138,7 +136,7 @@
     assertTrue(main.advancedToggleExpanded);
 
     ui.$$('#drawerTemplate').if = true;
-    Polymer.dom.flush();
+    flush();
 
     const drawerMenu = ui.$$('cr-drawer os-settings-menu');
     assertTrue(!!drawerMenu);
@@ -147,7 +145,7 @@
 
     // Collapse 'Advanced' in the menu.
     drawerMenu.$.advancedButton.click();
-    Polymer.dom.flush();
+    flush();
 
     // Collapsing it in the menu should not collapse it in the main area.
     assertFalse(drawerMenu.advancedOpened);
@@ -161,7 +159,7 @@
 
     // Collapse 'Advanced' in the main area.
     main.advancedToggleExpanded = false;
-    Polymer.dom.flush();
+    flush();
 
     // Collapsing it in the main area should not collapse it in the menu.
     assertFalse(ui.advancedOpenedInMain_);
@@ -181,17 +179,16 @@
     ironSelector.forceSynchronousItemUpdate();
 
     const urlParams = new URLSearchParams('search=foo');
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC, urlParams);
+    Router.getInstance().navigateTo(routes.BASIC, urlParams);
     assertEquals(
         urlParams.toString(),
-        settings.Router.getInstance().getQueryParameters().toString());
+        Router.getInstance().getQueryParameters().toString());
     settingsMenu.$.osPeople.click();
-    assertEquals(
-        '', settings.Router.getInstance().getQueryParameters().toString());
+    assertEquals('', Router.getInstance().getQueryParameters().toString());
   });
 
   test('Clicking About menu item should focus About section', async () => {
-    const router = settings.Router.getInstance();
+    const router = Router.getInstance();
     const settingsMenu = ui.$$('os-settings-menu');
 
     // As of iron-selector 2.x, need to force iron-selector to update before
@@ -202,8 +199,8 @@
     const {aboutItem} = settingsMenu.$;
     aboutItem.click();
 
-    Polymer.dom.flush();
-    assertEquals(settings.routes.ABOUT_ABOUT, router.getCurrentRoute());
+    flush();
+    assertEquals(routes.ABOUT_ABOUT, router.getCurrentRoute());
     assertNotEquals(aboutItem, settingsMenu.shadowRoot.activeElement);
 
     const settingsMain = ui.$$('os-settings-main');
@@ -215,11 +212,11 @@
 
   test('userActionRouteChange', function() {
     assertEquals(userActionRecorder.navigationCount, 0);
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC);
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.BASIC);
+    flush();
     assertEquals(userActionRecorder.navigationCount, 1);
-    settings.Router.getInstance().navigateTo(settings.routes.BASIC);
-    Polymer.dom.flush();
+    Router.getInstance().navigateTo(routes.BASIC);
+    flush();
     assertEquals(userActionRecorder.navigationCount, 1);
   });
 
@@ -255,11 +252,11 @@
     });
 
     ui.remove();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
     PolymerTest.clearBody();
     ui = document.createElement('os-settings-ui');
     document.body.appendChild(ui);
-    Polymer.dom.flush();
+    flush();
 
     // Toolbar should be hidden.
     assertFalse(isVisible(ui.$$('os-toolbar')));
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index ecbf6b6f..0fcaa73 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -119,7 +119,7 @@
 var OSSettingsPeoplePageOsSyncV3Test = class extends OSSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/os_sync_controls_test.m.js';
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/os_sync_controls_test.js';
   }
 
   /** @override */
@@ -140,7 +140,7 @@
 var OSSettingsOsSettingsPageV3Test = class extends OSSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/os_settings_page_test.m.js';
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/os_settings_page_test.js';
   }
 
   /** @override */
@@ -251,7 +251,7 @@
 var OSSettingsSearchEngineV3Test = class extends OSSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/search_engine_test.m.js';
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/search_engine_test.js';
   }
 
   /** @override */
@@ -377,7 +377,7 @@
  ['EsimInstallErrorDialog', 'esim_install_error_dialog_test.js'],
  ['EsimRemoveProfileDialog', 'esim_remove_profile_dialog_test.js'],
  ['EsimRenameDialog', 'esim_rename_dialog_test.js'],
- ['FilesPage', 'os_files_page_test.m.js'],
+ ['FilesPage', 'os_files_page_test.js'],
  ['FingerprintPage', 'fingerprint_browsertest_chromeos.js'],
  ['GoogleAssistantPage', 'google_assistant_page_test.js'],
  ['GuestOsSharedPaths', 'guest_os_shared_paths_test.js'],
@@ -441,12 +441,12 @@
  ['OsEditDictionaryPage', 'os_edit_dictionary_page_test.js'],
  ['OsLanguagesPageV2', 'os_languages_page_v2_tests.js'],
  ['OsPairedBluetoothList', 'os_paired_bluetooth_list_tests.js'],
- ['OsSettingsUi', 'os_settings_ui_test.m.js'],
- ['OsSettingsUi2', 'os_settings_ui_test_2.m.js'],
- ['OsSettingsMain', 'os_settings_main_test.m.js'],
- ['OsSearchPage', 'os_search_page_test.m.js'],
- ['OsSettingsSearchBox', 'os_settings_search_box_test.m.js'],
- ['OSSettingsMenu', 'os_settings_menu_test.m.js'],
+ ['OsSettingsUi', 'os_settings_ui_test.js'],
+ ['OsSettingsUi2', 'os_settings_ui_test_2.js'],
+ ['OsSettingsMain', 'os_settings_main_test.js'],
+ ['OsSearchPage', 'os_search_page_test.js'],
+ ['OsSettingsSearchBox', 'os_settings_search_box_test.js'],
+ ['OSSettingsMenu', 'os_settings_menu_test.js'],
  ['ParentalControlsPage', 'parental_controls_page_test.js'],
  ['PeoplePage', 'os_people_page_test.js'],
  ['PeoplePageChangePicture', 'people_page_change_picture_test.js'],
@@ -454,10 +454,10 @@
  ['PersonalizationPage', 'personalization_page_test.js'],
  ['PrintingPage', 'os_printing_page_tests.js'],
  ['PrivacyPage', 'os_privacy_page_test.js'],
- ['ResetPage', 'os_reset_page_test.m.js'],
- ['SettingsSchedulerSlider', 'settings_scheduler_slider_test.m.js'],
- ['SearchSubpage', 'search_subpage_test.m.js'],
- ['SettingsTrafficCounters', 'settings_traffic_counters_test.m.js'],
+ ['ResetPage', 'os_reset_page_test.js'],
+ ['SettingsSchedulerSlider', 'settings_scheduler_slider_test.js'],
+ ['SearchSubpage', 'search_subpage_test.js'],
+ ['SettingsTrafficCounters', 'settings_traffic_counters_test.js'],
  ['SmartInputsPage', 'smart_inputs_page_test.js'],
  ['SmbPage', 'smb_shares_page_tests.m.js'],
  ['SmartPrivacySubpage', 'smart_privacy_subpage_tests.js'],
diff --git a/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js b/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
index 52a8de9..107c8f5 100644
--- a/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
@@ -2,18 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {OsSyncBrowserProxyImpl, Router, StatusAction, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
-// #import {OsSyncBrowserProxyImpl, Router, StatusAction, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
-// clang-format on
-
-/** @implements {settings.OsSyncBrowserProxy} */
+/** @implements {OsSyncBrowserProxy} */
 class TestOsSyncBrowserProxy extends TestBrowserProxy {
   constructor() {
     super([
@@ -42,7 +36,7 @@
 /**
  * Returns a sync prefs dictionary with either all or nothing syncing.
  * @param {boolean} syncAll
- * @return {!settings.OsSyncPrefs}
+ * @return {!OsSyncPrefs}
  */
 function getOsSyncPrefs(syncAll) {
   return {
@@ -72,13 +66,13 @@
     hasError: false,
     hasUnrecoverableError: false,
     signedIn: true,
-    statusAction: settings.StatusAction.NO_ACTION,
+    statusAction: StatusAction.NO_ACTION,
   };
 }
 
 function setupSync() {
   cr.webUIListenerCallback('os-sync-prefs-changed', getSyncAllPrefs());
-  Polymer.dom.flush();
+  flush();
 }
 
 suite('OsSyncControlsTest', function() {
@@ -89,7 +83,7 @@
 
   setup(function() {
     browserProxy = new TestOsSyncBrowserProxy();
-    settings.OsSyncBrowserProxyImpl.instance_ = browserProxy;
+    OsSyncBrowserProxyImpl.instance_ = browserProxy;
 
     PolymerTest.clearBody();
     syncControls = document.createElement('os-sync-controls');
@@ -105,7 +99,7 @@
 
   teardown(function() {
     syncControls.remove();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
   });
 
   test('ControlsHiddenUntilInitialUpdateSent', function() {
@@ -176,12 +170,12 @@
 suite('OsSyncControlsNavigationTest', function() {
   test('DidNavigateEvents', async function() {
     const browserProxy = new TestOsSyncBrowserProxy();
-    settings.OsSyncBrowserProxyImpl.instance_ = browserProxy;
+    OsSyncBrowserProxyImpl.instance_ = browserProxy;
 
-    settings.Router.getInstance().navigateTo(settings.routes.OS_SYNC);
+    Router.getInstance().navigateTo(routes.OS_SYNC);
     await browserProxy.methodCalled('didNavigateToOsSyncPage');
 
-    settings.Router.getInstance().navigateTo(settings.routes.OS_PEOPLE);
+    Router.getInstance().navigateTo(routes.OS_PEOPLE);
     await browserProxy.methodCalled('didNavigateAwayFromOsSyncPage');
   });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/search_engine_test.js b/chrome/test/data/webui/settings/chromeos/search_engine_test.js
index 1b8e733..0005e56a 100644
--- a/chrome/test/data/webui/settings/chromeos/search_engine_test.js
+++ b/chrome/test/data/webui/settings/chromeos/search_engine_test.js
@@ -2,19 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {Router, SearchEnginesBrowserProxy, SearchEnginesBrowserProxyImpl, SearchEnginesInfo} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// #import {CrSettingsPrefs} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {GoogleAssistantBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {SearchEnginesBrowserProxy, SearchEnginesBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {SearchEnginesInfo, SearchEngine} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
 suite('SearchEngine', function() {
   /** @type {?SettingsSearchEngineElement} */
@@ -29,7 +21,7 @@
    * for allowing tests to know when a method was called, as well as
    * specifying mock responses.
    *
-   * @implements {settings.SearchEnginesBrowserProxy}
+   * @implements {SearchEnginesBrowserProxy}
    */
   class TestSearchEnginesBrowserProxy extends TestBrowserProxy {
     constructor() {
@@ -138,7 +130,7 @@
     browserProxy = new TestSearchEnginesBrowserProxy();
     searchEngineInfo = generateSearchEngineInfo();
     browserProxy.setSearchEnginesInfo(searchEngineInfo);
-    settings.SearchEnginesBrowserProxyImpl.instance_ = browserProxy;
+    SearchEnginesBrowserProxyImpl.instance_ = browserProxy;
     PolymerTest.clearBody();
     page = document.createElement('settings-search-engine');
     page.prefs = {
@@ -151,7 +143,7 @@
 
   teardown(function() {
     page.remove();
-    settings.Router.getInstance().resetRouteForTesting();
+    Router.getInstance().resetRouteForTesting();
   });
 
   // Tests that the page is querying and displaying search engine info on
@@ -161,7 +153,7 @@
     assertFalse(!!page.$$('os-settings-search-selection-dialog'));
 
     await browserProxy.whenCalled('getSearchEnginesList');
-    Polymer.dom.flush();
+    flush();
 
     // Sublabel should initially display the first search engine's name.
     const searchEngineSubLabel = page.$$('#currentSearchEngine');
@@ -174,10 +166,10 @@
     const dialogButton = page.$$('#searchSelectionDialogButton');
     assertTrue(!!dialogButton);
     dialogButton.click();
-    Polymer.dom.flush();
+    flush();
 
     await browserProxy.whenCalled('getSearchEnginesList');
-    Polymer.dom.flush();
+    flush();
 
     // Dialog should now be showing.
     const dialog = page.$$('os-settings-search-selection-dialog');
@@ -199,7 +191,7 @@
     searchEngineInfo.defaults[2].default = false;
     await browserProxy.whenCalled('setDefaultSearchEngine');
     cr.webUIListenerCallback('search-engines-changed', searchEngineInfo);
-    Polymer.dom.flush();
+    flush();
 
     // The sublabel should now be updated to the new search engine.
     assertEquals(
@@ -213,7 +205,7 @@
 
     browserProxy.resetResolver('setDefaultSearchEngine');
     cr.webUIListenerCallback('search-engines-changed', searchEngineInfo);
-    Polymer.dom.flush();
+    flush();
     assertEquals(
         searchEngineInfo.defaults[2].name,
         searchEngineSubLabel.innerHTML.trim());
@@ -239,7 +231,7 @@
       extensionCanBeDisabled: true,
       value: {},
     });
-    Polymer.dom.flush();
+    flush();
 
     assertTrue(dialogButton.disabled);
     assertTrue(!!page.$$('extension-controlled-indicator'));
@@ -257,7 +249,7 @@
       enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
       value: {},
     });
-    Polymer.dom.flush();
+    flush();
 
     assertTrue(dialogButton.disabled);
     assertFalse(!!page.$$('extension-controlled-indicator'));
diff --git a/chrome/test/data/webui/settings/chromeos/search_subpage_test.js b/chrome/test/data/webui/settings/chromeos/search_subpage_test.js
index b62e13a..2a63af44 100644
--- a/chrome/test/data/webui/settings/chromeos/search_subpage_test.js
+++ b/chrome/test/data/webui/settings/chromeos/search_subpage_test.js
@@ -2,16 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
-// #import {CrSettingsPrefs} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
 suite('SearchSubpage', function() {
   /** @type {SearchSubpageElement} */
@@ -34,7 +30,7 @@
       page = document.createElement('settings-search-subpage');
       page.prefs = prefElement.prefs;
       document.body.appendChild(page);
-      Polymer.dom.flush();
+      flush();
     });
   });
 
@@ -48,7 +44,7 @@
     assertFalse(!!button);
 
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-definition-enable');
     assertTrue(!!button);
@@ -59,7 +55,7 @@
     assertFalse(!!button);
 
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-translation-enable');
     assertTrue(!!button);
@@ -70,14 +66,14 @@
     assertFalse(!!button);
 
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-unit-conversion-enable');
     assertTrue(!!button);
   });
 
   test('toggleQuickAnswers', function() {
-    Polymer.dom.flush();
+    flush();
     const button = page.$$('#quick-answers-enable');
     assertTrue(!!button);
     assertFalse(button.disabled);
@@ -93,7 +89,7 @@
 
     // Tap the enable toggle button and ensure the state becomes enabled.
     button.click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(button.checked);
 
     definition_button = page.$$('#quick-answers-definition-enable');
@@ -109,7 +105,7 @@
     assertFalse(!!button);
     page.setPrefValue('settings.quick_answers.enabled', true);
     page.setPrefValue('settings.quick_answers.definition.enabled', false);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-definition-enable');
     assertTrue(!!button);
@@ -117,7 +113,7 @@
     assertFalse(button.checked);
 
     button.click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(button.checked);
     assertTrue(page.getPref('settings.quick_answers.definition.enabled.value'));
   });
@@ -127,7 +123,7 @@
     assertFalse(!!button);
     page.setPrefValue('settings.quick_answers.enabled', true);
     page.setPrefValue('settings.quick_answers.translation.enabled', false);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-translation-enable');
     assertTrue(!!button);
@@ -135,7 +131,7 @@
     assertFalse(button.checked);
 
     button.click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(button.checked);
     assertTrue(
         page.getPref('settings.quick_answers.translation.enabled.value'));
@@ -165,8 +161,7 @@
         page.getPref('settings.quick_answers.translation.enabled.value'));
 
     assertEquals(
-        settings.routes.OS_LANGUAGES_LANGUAGES,
-        settings.Router.getInstance().getCurrentRoute());
+        routes.OS_LANGUAGES_LANGUAGES, Router.getInstance().getCurrentRoute());
   });
 
   test('toggleQuickAnswersUnitConversion', function() {
@@ -174,7 +169,7 @@
     assertFalse(!!button);
     page.setPrefValue('settings.quick_answers.enabled', true);
     page.setPrefValue('settings.quick_answers.unit_conversion.enabled', false);
-    Polymer.dom.flush();
+    flush();
 
     button = page.$$('#quick-answers-unit-conversion-enable');
     assertTrue(!!button);
@@ -182,7 +177,7 @@
     assertFalse(button.checked);
 
     button.click();
-    Polymer.dom.flush();
+    flush();
     assertTrue(button.checked);
     assertTrue(
         page.getPref('settings.quick_answers.unit_conversion.enabled.value'));
@@ -191,8 +186,7 @@
   test('Deep link to Preferred Search Engine', async () => {
     const params = new URLSearchParams();
     params.append('settingId', '600');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.SEARCH_SUBPAGE, params);
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
 
     let deepLinkElement;
     if (loadTimeData.getBoolean('syncSettingsCategorizationEnabled')) {
@@ -205,7 +199,7 @@
           page.$$('settings-search-engine').$$('#searchSelectionDialogButton');
     }
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Preferred Search Engine button should be focused for settingId=600.');
@@ -214,13 +208,12 @@
   test('Deep link to Quick Answers On/Off', async () => {
     const params = new URLSearchParams();
     params.append('settingId', '608');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.SEARCH_SUBPAGE, params);
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
 
     const deepLinkElement =
         page.$$('#quick-answers-enable').shadowRoot.querySelector('cr-toggle');
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Quick Answer On/Off toggle should be focused for settingId=608.');
@@ -228,17 +221,16 @@
 
   test('Deep link to Quick Answers Definition', async () => {
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     const params = new URLSearchParams();
     params.append('settingId', '609');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.SEARCH_SUBPAGE, params);
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
 
     const deepLinkElement = page.$$('#quick-answers-definition-enable')
                                 .shadowRoot.querySelector('cr-toggle');
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Quick Answer definition toggle should be focused for settingId=609.');
@@ -246,17 +238,16 @@
 
   test('Deep link to Quick Answers Translation', async () => {
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     const params = new URLSearchParams();
     params.append('settingId', '610');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.SEARCH_SUBPAGE, params);
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
 
     const deepLinkElement = page.$$('#quick-answers-translation-enable')
                                 .shadowRoot.querySelector('cr-toggle');
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Quick Answer translation toggle should be focused for settingId=610.');
@@ -264,17 +255,16 @@
 
   test('Deep link to Quick Answers Unit Conversion', async () => {
     page.setPrefValue('settings.quick_answers.enabled', true);
-    Polymer.dom.flush();
+    flush();
 
     const params = new URLSearchParams();
     params.append('settingId', '611');
-    settings.Router.getInstance().navigateTo(
-        settings.routes.SEARCH_SUBPAGE, params);
+    Router.getInstance().navigateTo(routes.SEARCH_SUBPAGE, params);
 
     const deepLinkElement = page.$$('#quick-answers-unit-conversion-enable')
                                 .shadowRoot.querySelector('cr-toggle');
     assertTrue(!!deepLinkElement);
-    await test_util.waitAfterNextRender(deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
         'Quick Answer unit conversion toggle should be focused for settingId=611.');
diff --git a/chrome/test/data/webui/settings/chromeos/settings_scheduler_slider_test.js b/chrome/test/data/webui/settings/chromeos/settings_scheduler_slider_test.js
index 8a76cb5..5d65dc1 100644
--- a/chrome/test/data/webui/settings/chromeos/settings_scheduler_slider_test.js
+++ b/chrome/test/data/webui/settings/chromeos/settings_scheduler_slider_test.js
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {keyDownOn, keyUpOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-// clang-format on
+import 'chrome://os-settings/chromeos/os_settings.js';
+
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertEquals, assertTrue} from '../../chai_assert.js';
 
 /** @fileoverview Suite of tests for settings-scheduler-slider. */
 suite('SettingsSchedulerSlider', function() {
diff --git a/chromecast/browser/exo/cast_wm_helper.cc b/chromecast/browser/exo/cast_wm_helper.cc
index cd019988..9acf15e 100644
--- a/chromecast/browser/exo/cast_wm_helper.cc
+++ b/chromecast/browser/exo/cast_wm_helper.cc
@@ -113,13 +113,6 @@
 
 void CastWMHelper::OnDragExited() {}
 
-ui::mojom::DragOperation CastWMHelper::OnPerformDrop(
-    const ui::DropTargetEvent& event,
-    std::unique_ptr<ui::OSExchangeData> data) {
-  NOTIMPLEMENTED();
-  return ui::mojom::DragOperation::kMove;
-}
-
 WMHelper::DropCallback CastWMHelper::GetDropCallback(
     const ui::DropTargetEvent& event) {
   NOTIMPLEMENTED();
diff --git a/chromecast/browser/exo/cast_wm_helper.h b/chromecast/browser/exo/cast_wm_helper.h
index 27801d3..762ac93 100644
--- a/chromecast/browser/exo/cast_wm_helper.h
+++ b/chromecast/browser/exo/cast_wm_helper.h
@@ -109,9 +109,6 @@
   aura::client::DragUpdateInfo OnDragUpdated(
       const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
-  ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event,
-      std::unique_ptr<ui::OSExchangeData> data) override;
   WMHelper::DropCallback GetDropCallback(
       const ui::DropTargetEvent& event) override;
 
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 503e7ffb..ae853706 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -111,6 +111,7 @@
   if (is_chromeos_ash) {
     deps += [
       ":test_support",
+      "//chromeos/ash/components/dbus:unit_tests",
       "//chromeos/crosapi/cpp:unit_tests",
       "//chromeos/crosapi/mojom:unit_tests",
       "//chromeos/dbus:test_support",
diff --git a/chromeos/ash/components/dbus/BUILD.gn b/chromeos/ash/components/dbus/BUILD.gn
new file mode 100644
index 0000000..2db1e404
--- /dev/null
+++ b/chromeos/ash/components/dbus/BUILD.gn
@@ -0,0 +1,23 @@
+# 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("//build/config/ui.gni")
+
+assert(is_chromeos_ash, "Non ChromeOS builds must not depend on //chromeos/ash")
+
+source_set("unit_tests") {
+  configs += [ "//build/config/linux/dbus" ]
+  testonly = true
+  deps = [
+    "//ash/components/tpm:test_support",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
+    "//chromeos/dbus:test_support",
+    "//chromeos/dbus/session_manager",
+    "//components/account_id",
+  ]
+  sources = [ "authpolicy/fake_authpolicy_client_unittest.cc" ]
+}
diff --git a/chromeos/ash/components/dbus/DEPS b/chromeos/ash/components/dbus/DEPS
new file mode 100644
index 0000000..ca7d39f
--- /dev/null
+++ b/chromeos/ash/components/dbus/DEPS
@@ -0,0 +1,15 @@
+noparent = True
+
+include_rules = [
+  "+base",
+  # Please do not add any chromeos/ dependencies here: https://crbug.com/863439
+  "+components/account_id/account_id.h",
+  "+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.
+  "+chromeos/dbus"
+]
diff --git a/chromeos/ash/components/dbus/DIR_METADATA b/chromeos/ash/components/dbus/DIR_METADATA
new file mode 100644
index 0000000..40779e6
--- /dev/null
+++ b/chromeos/ash/components/dbus/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chromeos/dbus/COMMON_METADATA"
diff --git a/chromeos/ash/components/dbus/OWNERS b/chromeos/ash/components/dbus/OWNERS
new file mode 100644
index 0000000..8637323
--- /dev/null
+++ b/chromeos/ash/components/dbus/OWNERS
@@ -0,0 +1 @@
+file://chromeos/dbus/OWNERS
diff --git a/chromeos/dbus/authpolicy/BUILD.gn b/chromeos/ash/components/dbus/authpolicy/BUILD.gn
similarity index 84%
rename from chromeos/dbus/authpolicy/BUILD.gn
rename to chromeos/ash/components/dbus/authpolicy/BUILD.gn
index 5736394..9b4dcd5 100644
--- a/chromeos/dbus/authpolicy/BUILD.gn
+++ b/chromeos/ash/components/dbus/authpolicy/BUILD.gn
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/ui.gni")
 import("//third_party/protobuf/proto_library.gni")
 
-assert(is_chromeos, "Non-Chrome-OS builds cannot depend on //chromeos")
+assert(is_chromeos_ash, "Non Chrome OS builds cannot depend on //chromeos/ash")
 
 component("authpolicy") {
   defines = [ "IS_AUTHPOLICY_IMPL" ]
@@ -38,5 +39,5 @@
     "//third_party/cros_system_api/dbus/authpolicy/active_directory_info.proto",
   ]
 
-  proto_out_dir = "chromeos/dbus/authpolicy"
+  proto_out_dir = "chromeos/ash/components/dbus/authpolicy"
 }
diff --git a/chromeos/dbus/authpolicy/DEPS b/chromeos/ash/components/dbus/authpolicy/DEPS
similarity index 100%
rename from chromeos/dbus/authpolicy/DEPS
rename to chromeos/ash/components/dbus/authpolicy/DEPS
diff --git a/chromeos/dbus/authpolicy/OWNERS b/chromeos/ash/components/dbus/authpolicy/OWNERS
similarity index 100%
rename from chromeos/dbus/authpolicy/OWNERS
rename to chromeos/ash/components/dbus/authpolicy/OWNERS
diff --git a/chromeos/dbus/authpolicy/authpolicy_client.cc b/chromeos/ash/components/dbus/authpolicy/authpolicy_client.cc
similarity index 98%
rename from chromeos/dbus/authpolicy/authpolicy_client.cc
rename to chromeos/ash/components/dbus/authpolicy/authpolicy_client.cc
index 15bda36..2b483d0 100644
--- a/chromeos/dbus/authpolicy/authpolicy_client.cc
+++ b/chromeos/ash/components/dbus/authpolicy/authpolicy_client.cc
@@ -1,7 +1,8 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
 
 #include <utility>
 
@@ -11,7 +12,7 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "components/account_id/account_id.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
diff --git a/chromeos/dbus/authpolicy/authpolicy_client.h b/chromeos/ash/components/dbus/authpolicy/authpolicy_client.h
similarity index 93%
rename from chromeos/dbus/authpolicy/authpolicy_client.h
rename to chromeos/ash/components/dbus/authpolicy/authpolicy_client.h
index 12b6472..c877de9 100644
--- a/chromeos/dbus/authpolicy/authpolicy_client.h
+++ b/chromeos/ash/components/dbus/authpolicy/authpolicy_client.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 CHROMEOS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
-#define CHROMEOS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
 
 #include <string>
 
 #include "base/callback.h"
 #include "base/component_export.h"
-#include "chromeos/dbus/authpolicy/active_directory_info.pb.h"
+#include "chromeos/ash/components/dbus/authpolicy/active_directory_info.pb.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -111,4 +111,4 @@
 using ::chromeos::AuthPolicyClient;
 }  // namespace ash
 
-#endif  // CHROMEOS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_AUTHPOLICY_CLIENT_H_
diff --git a/chromeos/dbus/authpolicy/fake_authpolicy_client.cc b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.cc
similarity index 99%
rename from chromeos/dbus/authpolicy/fake_authpolicy_client.cc
rename to chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.cc
index d9b1e2c..de2f8c2 100644
--- a/chromeos/dbus/authpolicy/fake_authpolicy_client.cc
+++ b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 
 #include <memory>
 #include <vector>
diff --git a/chromeos/dbus/authpolicy/fake_authpolicy_client.h b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h
similarity index 95%
rename from chromeos/dbus/authpolicy/fake_authpolicy_client.h
rename to chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h
index e4c81ba..ac191d9e 100644
--- a/chromeos/dbus/authpolicy/fake_authpolicy_client.h
+++ b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
-#define CHROMEOS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
 
 #include <set>
 #include <string>
@@ -12,7 +12,7 @@
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "chromeos/dbus/authpolicy/authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -196,4 +196,4 @@
 using ::chromeos::FakeAuthPolicyClient;
 }
 
-#endif  // CHROMEOS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_AUTHPOLICY_FAKE_AUTHPOLICY_CLIENT_H_
diff --git a/chromeos/dbus/authpolicy/fake_authpolicy_client_unittest.cc b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client_unittest.cc
similarity index 99%
rename from chromeos/dbus/authpolicy/fake_authpolicy_client_unittest.cc
rename to chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client_unittest.cc
index fc72535..6b74684 100644
--- a/chromeos/dbus/authpolicy/fake_authpolicy_client_unittest.cc
+++ b/chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client_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 "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 
 #include "ash/components/tpm/stub_install_attributes.h"
 #include "base/bind.h"
diff --git a/chromeos/dbus/upstart/BUILD.gn b/chromeos/ash/components/dbus/upstart/BUILD.gn
similarity index 68%
rename from chromeos/dbus/upstart/BUILD.gn
rename to chromeos/ash/components/dbus/upstart/BUILD.gn
index 0c5e282..45cc302 100644
--- a/chromeos/dbus/upstart/BUILD.gn
+++ b/chromeos/ash/components/dbus/upstart/BUILD.gn
@@ -2,15 +2,17 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-assert(is_chromeos, "Non-Chrome-OS builds cannot depend on //chromeos")
+import("//build/config/ui.gni")
+
+assert(is_chromeos_ash, "Non Chrome OS builds cannot depend on //chromeos/ash")
 
 component("upstart") {
   defines = [ "IS_UPSTART_CLIENT_IMPL" ]
 
   deps = [
     "//base",
-    "//chromeos/dbus/authpolicy",
-    "//chromeos/dbus/authpolicy:authpolicy_proto",
+    "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
     "//chromeos/dbus/common",
     "//chromeos/dbus/kerberos",
     "//chromeos/dbus/media_analytics",
diff --git a/chromeos/dbus/upstart/fake_upstart_client.cc b/chromeos/ash/components/dbus/upstart/fake_upstart_client.cc
similarity index 96%
rename from chromeos/dbus/upstart/fake_upstart_client.cc
rename to chromeos/ash/components/dbus/upstart/fake_upstart_client.cc
index 9e93252..56b2c1a 100644
--- a/chromeos/dbus/upstart/fake_upstart_client.cc
+++ b/chromeos/ash/components/dbus/upstart/fake_upstart_client.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/dbus/authpolicy/fake_authpolicy_client.h"
+#include "chromeos/ash/components/dbus/authpolicy/fake_authpolicy_client.h"
 #include "chromeos/dbus/kerberos/fake_kerberos_client.h"
 #include "chromeos/dbus/kerberos/kerberos_client.h"
 #include "chromeos/dbus/media_analytics/fake_media_analytics_client.h"
diff --git a/chromeos/dbus/upstart/fake_upstart_client.h b/chromeos/ash/components/dbus/upstart/fake_upstart_client.h
similarity index 90%
rename from chromeos/dbus/upstart/fake_upstart_client.h
rename to chromeos/ash/components/dbus/upstart/fake_upstart_client.h
index dc99b3a..01a704a 100644
--- a/chromeos/dbus/upstart/fake_upstart_client.h
+++ b/chromeos/ash/components/dbus/upstart/fake_upstart_client.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
-#define CHROMEOS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
 
 #include "base/callback.h"
 #include "base/component_export.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 
 namespace chromeos {
 
@@ -66,4 +66,4 @@
 using ::chromeos::FakeUpstartClient;
 }  // namespace ash
 
-#endif  // CHROMEOS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_FAKE_UPSTART_CLIENT_H_
diff --git a/chromeos/dbus/upstart/upstart_client.cc b/chromeos/ash/components/dbus/upstart/upstart_client.cc
similarity index 97%
rename from chromeos/dbus/upstart/upstart_client.cc
rename to chromeos/ash/components/dbus/upstart/upstart_client.cc
index 90c03a2..64c8767 100644
--- a/chromeos/dbus/upstart/upstart_client.cc
+++ b/chromeos/ash/components/dbus/upstart/upstart_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
diff --git a/chromeos/dbus/upstart/upstart_client.h b/chromeos/ash/components/dbus/upstart/upstart_client.h
similarity index 94%
rename from chromeos/dbus/upstart/upstart_client.h
rename to chromeos/ash/components/dbus/upstart/upstart_client.h
index a9040d9..085c9753 100644
--- a/chromeos/dbus/upstart/upstart_client.h
+++ b/chromeos/ash/components/dbus/upstart/upstart_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_UPSTART_UPSTART_CLIENT_H_
-#define CHROMEOS_DBUS_UPSTART_UPSTART_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_UPSTART_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_UPSTART_CLIENT_H_
 
 #include <string>
 #include <vector>
@@ -111,4 +111,4 @@
 using ::chromeos::UpstartClient;
 }  // namespace ash
 
-#endif  // CHROMEOS_DBUS_UPSTART_UPSTART_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_UPSTART_UPSTART_CLIENT_H_
diff --git a/chromeos/crosapi/mojom/sharesheet.mojom b/chromeos/crosapi/mojom/sharesheet.mojom
index dc18864..4a48148c 100644
--- a/chromeos/crosapi/mojom/sharesheet.mojom
+++ b/chromeos/crosapi/mojom/sharesheet.mojom
@@ -25,6 +25,8 @@
 };
 
 // Interacts with the Sharesheet service. Implemented in ash-chrome.
+// Next version: 2
+// Next method id: 3
 [Stable, Uuid="be0b8049-1fdd-4ba7-a980-997b8c59420e"]
 interface Sharesheet {
   // Displays the dialog (aka bubble) for sharing content (or files) with
@@ -34,4 +36,14 @@
       string window_id,
       SharesheetLaunchSource source,
       Intent intent) => (SharesheetResult sharesheet_result);
+
+  // Functions like ShowBubble(), except this invokes a callback when the bubble
+  // is closed instead of SharesheetResult.
+  [MinVersion=1] ShowBubbleWithOnClosed@1(
+      string window_id,
+      SharesheetLaunchSource source,
+      Intent intent) => ();
+
+  // Closes the Sharesheet dialog associated with the window ID.
+  [MinVersion=1] CloseBubble@2(string window_id);
 };
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index 56dbe3a..05dd8c03 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -97,12 +97,9 @@
   deps = [
     ":dbus",
     ":test_support",
-    "//ash/components/tpm:test_support",
     "//base",
     "//base/test:test_support",
     "//chromeos/dbus/audio",
-    "//chromeos/dbus/authpolicy",
-    "//chromeos/dbus/authpolicy:authpolicy_proto",
     "//chromeos/dbus/biod:test_support",
     "//chromeos/dbus/cec_service:unit_tests",
     "//chromeos/dbus/cros_disks:unit_tests",
@@ -121,7 +118,6 @@
     "//chromeos/dbus/power:test_support",
     "//chromeos/dbus/rmad:rmad_proto",
     "//chromeos/dbus/rmad:test_support",
-    "//chromeos/dbus/session_manager",
     "//chromeos/dbus/shill:test_support",
     "//chromeos/dbus/system_clock:unit_tests",
     "//chromeos/dbus/tpm_manager",
@@ -132,7 +128,6 @@
     "//chromeos/dbus/userdataauth",
     "//chromeos/dbus/userdataauth:userdataauth_proto",
     "//chromeos/dbus/util:unit_tests",
-    "//components/account_id",
     "//dbus",
     "//dbus:test_support",
     "//testing/gmock",
@@ -144,7 +139,6 @@
   }
   sources = [
     "audio/cras_audio_client_unittest.cc",
-    "authpolicy/fake_authpolicy_client_unittest.cc",
     "biod/biod_client_unittest.cc",
     "biod/fake_biod_client_unittest.cc",
     "blocking_method_caller_unittest.cc",
diff --git a/chromeos/dbus/DEPS b/chromeos/dbus/DEPS
index 09949ce..1712b665 100644
--- a/chromeos/dbus/DEPS
+++ b/chromeos/dbus/DEPS
@@ -3,7 +3,6 @@
 include_rules = [
   "+base",
   # Please do not add any chromeos/ dependencies here: https://crbug.com/863439
-  "+components/account_id/account_id.h",
   "+components/device_event_log",
   "+components/policy/proto",
   "+crypto",
diff --git a/chromeos/dbus/userdataauth/fake_userdataauth_client.cc b/chromeos/dbus/userdataauth/fake_userdataauth_client.cc
index 70c8e5c..163bd1f7 100644
--- a/chromeos/dbus/userdataauth/fake_userdataauth_client.cc
+++ b/chromeos/dbus/userdataauth/fake_userdataauth_client.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/containers/contains.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
@@ -634,6 +635,11 @@
   return keys.find(label);
 }
 
+void FakeUserDataAuthClient::CreateUserProfileDir(
+    const cryptohome::AccountIdentifier& account_id) {
+  base::CreateDirectory(GetUserProfileDir(account_id));
+}
+
 base::FilePath FakeUserDataAuthClient::GetUserProfileDir(
     const cryptohome::AccountIdentifier& account_id) const {
   DCHECK(!user_data_dir_.empty());
diff --git a/chromeos/dbus/userdataauth/fake_userdataauth_client.h b/chromeos/dbus/userdataauth/fake_userdataauth_client.h
index 52a102de..bb702fdf 100644
--- a/chromeos/dbus/userdataauth/fake_userdataauth_client.h
+++ b/chromeos/dbus/userdataauth/fake_userdataauth_client.h
@@ -279,6 +279,8 @@
 
   void set_user_data_dir(base::FilePath path) { user_data_dir_ = path; }
 
+  void CreateUserProfileDir(const cryptohome::AccountIdentifier& account_id);
+
  private:
   // Helper that returns the protobuf reply.
   template <typename ReplyType>
diff --git a/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml b/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
index 41e3970..8dadd63b 100644
--- a/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
+++ b/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
@@ -3,6 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <resources>
+    <dimen name="autofill_assistant_backgroundless_privacy_notice_padding_start">40dp</dimen>
     <dimen name="autofill_assistant_bottombar_horizontal_spacing">24dp</dimen>
     <dimen name="autofill_assistant_bottombar_vertical_spacing">20dp</dimen>
     <dimen name="autofill_assistant_details_image_size">60dp</dimen>
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantCollectUserDataBinder.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
index 9efef048..3d4f407 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
@@ -341,6 +341,10 @@
             view.mDataOriginNotice.setAccountEmail(
                     model.get(AssistantCollectUserDataModel.ACCOUNT_EMAIL));
             return true;
+        } else if (model.get(AssistantCollectUserDataModel.USE_GMS_CORE_EDIT_DIALOGS)) {
+            view.mTermsAsCheckboxSection.useBackgroundlessPrivacyNotice();
+            view.mTermsSection.useBackgroundlessPrivacyNotice();
+            return true;
         }
 
         return false;
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantTermsSection.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantTermsSection.java
index 1f75914..530b2365f 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantTermsSection.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantTermsSection.java
@@ -30,6 +30,7 @@
         void onLinkClicked(int link);
     }
 
+    private final Context mContext;
     private final View mView;
     private final AssistantChoiceList mTermsList;
     private final TextView mTermsAgree;
@@ -40,6 +41,7 @@
     private Delegate mDelegate;
 
     AssistantTermsSection(Context context, ViewGroup parent, boolean showAsSingleCheckbox) {
+        mContext = context;
         mView = LayoutUtils.createInflater(context).inflate(
                 R.layout.autofill_assistant_payment_request_terms_and_conditions, parent, false);
         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@@ -137,6 +139,15 @@
         mPrivacyNotice.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
     }
 
+    void useBackgroundlessPrivacyNotice() {
+        int paddingStart = mContext.getResources().getDimensionPixelSize(
+                R.dimen.autofill_assistant_backgroundless_privacy_notice_padding_start);
+        mPrivacyNotice.setPaddingRelative(paddingStart, mPrivacyNotice.getPaddingTop(),
+                mPrivacyNotice.getPaddingEnd(), mPrivacyNotice.getPaddingBottom());
+        mPrivacyNotice.setTextAppearance(R.style.TextAppearance_TextSmall_Secondary);
+        mPrivacyNotice.setBackgroundResource(0);
+    }
+
     View getView() {
         return mView;
     }
diff --git a/components/bookmarks/browser/BUILD.gn b/components/bookmarks/browser/BUILD.gn
index a4d632e..bbed618f 100644
--- a/components/bookmarks/browser/BUILD.gn
+++ b/components/bookmarks/browser/BUILD.gn
@@ -21,7 +21,6 @@
     "bookmark_undo_delegate.h",
     "bookmark_undo_provider.h",
     "bookmark_utils.h",
-    "features.h",
     "history_bookmark_model.h",
     "model_loader.h",
     "scoped_group_bookmark_actions.h",
@@ -44,7 +43,6 @@
     "bookmark_node_data.cc",
     "bookmark_storage.cc",
     "bookmark_utils.cc",
-    "features.cc",
     "model_loader.cc",
     "scoped_group_bookmark_actions.cc",
     "titled_url_index.cc",
diff --git a/components/bookmarks/browser/bookmark_utils.cc b/components/bookmarks/browser/bookmark_utils.cc
index a54c0c3a..651dc39 100644
--- a/components/bookmarks/browser/bookmark_utils.cc
+++ b/components/bookmarks/browser/bookmark_utils.cc
@@ -24,7 +24,6 @@
 #include "build/build_config.h"
 #include "components/bookmarks/browser/bookmark_client.h"
 #include "components/bookmarks/browser/bookmark_model.h"
-#include "components/bookmarks/browser/features.h"
 #include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -437,14 +436,11 @@
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(
-      prefs::kShowBookmarkBar,
-      false,
+      prefs::kShowBookmarkBar, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(prefs::kEditBookmarksEnabled, true);
   registry->RegisterBooleanPref(
-      prefs::kShowAppsShortcutInBookmarkBar,
-      base::FeatureList::IsEnabled(features::kAppsShortcutDefaultOff) ? false
-                                                                      : true,
+      prefs::kShowAppsShortcutInBookmarkBar, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(
       prefs::kShowManagedBookmarksInBookmarkBar, true,
diff --git a/components/bookmarks/browser/features.cc b/components/bookmarks/browser/features.cc
deleted file mode 100644
index 5909404..0000000
--- a/components/bookmarks/browser/features.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/bookmarks/browser/features.h"
-
-namespace bookmarks {
-namespace features {
-
-// Changes the apps shortcut on the bookmarks bar to default to off.
-// https://crbug.com/1236793
-const base::Feature kAppsShortcutDefaultOff{"AppsShortcutDefaultOff",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-
-}  // namespace features
-}  // namespace bookmarks
diff --git a/components/bookmarks/browser/features.h b/components/bookmarks/browser/features.h
deleted file mode 100644
index 5aa1e1ffc..0000000
--- a/components/bookmarks/browser/features.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BOOKMARKS_BROWSER_FEATURES_H_
-#define COMPONENTS_BOOKMARKS_BROWSER_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace bookmarks {
-namespace features {
-
-extern const base::Feature kAppsShortcutDefaultOff;
-
-}  // namespace features
-}  // namespace bookmarks
-
-#endif  // COMPONENTS_BOOKMARKS_BROWSER_FEATURES_H_
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/ImageDecoderTest.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/ImageDecoderTest.java
index d5a3da2..98d9a2d 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/ImageDecoderTest.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/ImageDecoderTest.java
@@ -23,6 +23,7 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.UrlUtils;
 
 import java.io.File;
@@ -126,6 +127,7 @@
 
     @Test
     @LargeTest
+    @DisabledTest(message = "https://crbug.com/1313903")
     public void testServiceDecodeSimple() throws Exception {
         startDecoderService();
 
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
index 0290f0f..af11c5d 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
@@ -31,6 +31,7 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.UrlUtils;
@@ -650,6 +651,7 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
+    @DisabledTest(message = "https://crbug.com/1313904")
     public void testBorderPersistence() throws Exception {
         setupTestFilesWith80ColoredSquares();
         createDialog(false, Arrays.asList("image/*")); // Multi-select = false.
diff --git a/components/nacl/loader/BUILD.gn b/components/nacl/loader/BUILD.gn
index 8deff67..6b48bd9 100644
--- a/components/nacl/loader/BUILD.gn
+++ b/components/nacl/loader/BUILD.gn
@@ -11,101 +11,103 @@
 
 assert(enable_nacl)
 
-# This is separate so it can be used by ../broker:nacl64.
-source_set("minimal") {
-  sources = [
-    "nacl_ipc_adapter.cc",
-    "nacl_ipc_adapter.h",
-    "nacl_listener.cc",
-    "nacl_listener.h",
-    "nacl_main.cc",
-    "nacl_main_platform_delegate.h",
-    "nacl_trusted_listener.cc",
-    "nacl_trusted_listener.h",
-    "nacl_validation_db.h",
-    "nacl_validation_query.cc",
-    "nacl_validation_query.h",
-  ]
+if (current_cpu != "arm64") {
+  # This is separate so it can be used by ../broker:nacl64.
+  source_set("minimal") {
+    sources = [
+      "nacl_ipc_adapter.cc",
+      "nacl_ipc_adapter.h",
+      "nacl_listener.cc",
+      "nacl_listener.h",
+      "nacl_main.cc",
+      "nacl_main_platform_delegate.h",
+      "nacl_trusted_listener.cc",
+      "nacl_trusted_listener.h",
+      "nacl_validation_db.h",
+      "nacl_validation_query.cc",
+      "nacl_validation_query.h",
+    ]
 
-  deps = [
-    ":minimal_content_dummy",
-    "//base",
-    "//components/nacl/common:minimal",
-    "//components/nacl/common:mojo_bindings",
-    "//crypto",
-    "//ipc",
-    "//mojo/core/embedder",
-    "//native_client/src/trusted/service_runtime:sel_main_chrome",
-    "//ppapi/c",
-    "//ppapi/proxy:ipc",
-    "//sandbox",
-    "//services/service_manager/public/cpp",
-  ]
+    deps = [
+      ":minimal_content_dummy",
+      "//base",
+      "//components/nacl/common:minimal",
+      "//components/nacl/common:mojo_bindings",
+      "//crypto",
+      "//ipc",
+      "//mojo/core/embedder",
+      "//native_client/src/trusted/service_runtime:sel_main_chrome",
+      "//ppapi/c",
+      "//ppapi/proxy:ipc",
+      "//sandbox",
+      "//services/service_manager/public/cpp",
+    ]
 
-  if (is_win) {
-    sources += [ "nacl_main_platform_delegate_win.cc" ]
+    if (is_win) {
+      sources += [ "nacl_main_platform_delegate_win.cc" ]
+    }
+
+    if (is_mac) {
+      sources += [ "nacl_main_platform_delegate_mac.mm" ]
+    }
+
+    if (is_linux || is_chromeos) {
+      sources += [ "nacl_main_platform_delegate_linux.cc" ]
+    }
   }
 
-  if (is_mac) {
-    sources += [ "nacl_main_platform_delegate_mac.mm" ]
+  # This exists just to make 'gn check' happy with :minimal and
+  # :nacl_helper_win_64 (below).  They can't depend on //content/public/common
+  # or anything like that, because that would bring in lots more than counts
+  # as "minimal" (stuff that should not be in the nacl64.exe build).
+  source_set("minimal_content_dummy") {
+    check_includes = false
+    sources = [ "//content/public/common/main_function_params.h" ]
+
+    # Deps required by the above headers.
+    deps = [ "//media:media_buildflags" ]
+
+    if (is_win) {
+      sources += [ "//content/public/common/sandbox_init_win.h" ]
+    }
+    if (is_linux || is_chromeos) {
+      sources += [ "//content/public/common/zygote/sandbox_support_linux.h" ]
+    }
   }
 
-  if (is_linux || is_chromeos) {
-    sources += [ "nacl_main_platform_delegate_linux.cc" ]
+  source_set("loader") {
+    public_deps = [ ":minimal" ]
+    deps = [
+      "//components/nacl/common",
+      "//content/public/common",
+      "//ppapi/shared_impl",
+      "//services/service_manager/public/cpp",
+    ]
+
+    data_deps = [
+      "//ppapi/native_client:irt",
+      "//ppapi/native_client/src/untrusted/pnacl_support_extension",
+    ]
+  }
+
+  test("nacl_loader_unittests") {
+    sources = [
+      "nacl_ipc_adapter_unittest.cc",
+      "nacl_validation_query_unittest.cc",
+      "run_all_unittests.cc",
+    ]
+
+    deps = [
+      ":loader",
+      "//base/test:test_support",
+      "//ipc:test_support",
+      "//ppapi/c",
+      "//testing/gtest",
+    ]
   }
 }
 
-# This exists just to make 'gn check' happy with :minimal and
-# :nacl_helper_win_64 (below).  They can't depend on //content/public/common
-# or anything like that, because that would bring in lots more than counts
-# as "minimal" (stuff that should not be in the nacl64.exe build).
-source_set("minimal_content_dummy") {
-  check_includes = false
-  sources = [ "//content/public/common/main_function_params.h" ]
-
-  # Deps required by the above headers.
-  deps = [ "//media:media_buildflags" ]
-
-  if (is_win) {
-    sources += [ "//content/public/common/sandbox_init_win.h" ]
-  }
-  if (is_linux || is_chromeos) {
-    sources += [ "//content/public/common/zygote/sandbox_support_linux.h" ]
-  }
-}
-
-source_set("loader") {
-  public_deps = [ ":minimal" ]
-  deps = [
-    "//components/nacl/common",
-    "//content/public/common",
-    "//ppapi/shared_impl",
-    "//services/service_manager/public/cpp",
-  ]
-
-  data_deps = [
-    "//ppapi/native_client:irt",
-    "//ppapi/native_client/src/untrusted/pnacl_support_extension",
-  ]
-}
-
-test("nacl_loader_unittests") {
-  sources = [
-    "nacl_ipc_adapter_unittest.cc",
-    "nacl_validation_query_unittest.cc",
-    "run_all_unittests.cc",
-  ]
-
-  deps = [
-    ":loader",
-    "//base/test:test_support",
-    "//ipc:test_support",
-    "//ppapi/c",
-    "//testing/gtest",
-  ]
-}
-
-if (is_linux || is_chromeos) {
+if ((is_linux || is_chromeos) && current_cpu != "arm64") {
   executable("nacl_helper") {
     sources = [
       "nacl_helper_linux.cc",
@@ -161,6 +163,31 @@
   }
 }
 
+# In ARM64 Chrome, use the ARM32 versions of nacl_helper and
+# nacl_helper_bootstrap.  Copy them from the build directory for the
+# ARM32 toolchain.
+if ((is_linux || is_chromeos) && current_cpu == "arm64") {
+  copy("nacl_helper") {
+    label = ":nacl_helper(//build/toolchain/linux:clang_arm)"
+    out_dir = get_label_info(label, "root_out_dir")
+    sources = [ "${out_dir}/nacl_helper" ]
+    outputs = [ "${root_out_dir}/nacl_helper" ]
+    deps = [ label ]
+    data_deps = [
+      ":nacl_helper_bootstrap",
+      "//ppapi/native_client/src/untrusted/pnacl_support_extension",
+    ]
+  }
+
+  copy("nacl_helper_bootstrap") {
+    label = "//native_client/src/trusted/service_runtime/linux:bootstrap(//build/toolchain/linux:clang_arm)"
+    out_dir = get_label_info(label, "root_out_dir")
+    sources = [ "${out_dir}/nacl_helper_bootstrap" ]
+    outputs = [ "${root_out_dir}/nacl_helper_bootstrap" ]
+    deps = [ label ]
+  }
+}
+
 if (is_win && target_cpu == "x86" && current_cpu == "x64") {
   source_set("nacl_helper_win_64") {
     sources = [
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 888b8bb7..743eff9 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -100,13 +100,6 @@
   return false;
 }
 
-// Returns whether reparsing server predictions following a form change is
-// enabled.
-bool IsReparsingServerPredictionsEnabled() {
-  return base::FeatureList::IsEnabled(
-      features::kReparseServerPredictionsFollowingFormChange);
-}
-
 bool IsUsernameFirstFlowFeatureEnabled() {
   return base::FeatureList::IsEnabled(features::kUsernameFirstFlow);
 }
@@ -883,8 +876,7 @@
   bool new_predictions_available = false;
   if (differences_bitmask) {
     UpdateFormManagerWithFormChanges(observed_form_data, predictions);
-    new_predictions_available =
-        parser_.predictions() && IsReparsingServerPredictionsEnabled();
+    new_predictions_available = parser_.predictions().has_value();
   }
   // Fill the form if relevant form predictions were found or if the
   // manager is not waiting for new server predictions.
@@ -1108,8 +1100,6 @@
     const FormData& observed_form_data,
     const std::map<FormSignature, FormPredictions>& predictions) {
   *mutable_observed_form() = observed_form_data;
-  if (!IsReparsingServerPredictionsEnabled())
-    return;
 
   // If the observed form has changed, it might be autofilled again.
   autofills_left_ = kMaxTimesAutofill;
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 2ea3744..95b5453 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -3902,12 +3902,6 @@
 }
 
 TEST_P(PasswordManagerTest, GenerationOnChangedForm) {
-  const bool kIsReparsingEnabled = GetParam();
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatureState(
-      features::kReparseServerPredictionsFollowingFormChange,
-      kIsReparsingEnabled);
-
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
       .WillRepeatedly(Return(true));
   EXPECT_CALL(*store_, GetLogins(_, _))
@@ -3950,16 +3944,12 @@
   manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
 
   autofill::PasswordFormGenerationData form_generation_data;
-  if (kIsReparsingEnabled) {
-    EXPECT_CALL(driver_, FormEligibleForGenerationFound)
-        .WillOnce(SaveArg<0>(&form_generation_data));
-    // The change is discovered by PasswordManager.
-    manager()->OnPasswordFormsParsed(&driver_, {form_data});
-    EXPECT_EQ(new_password_field.unique_renderer_id,
-              form_generation_data.new_password_renderer_id);
-  } else {
-    EXPECT_CALL(driver_, FormEligibleForGenerationFound).Times(0);
-  }
+  EXPECT_CALL(driver_, FormEligibleForGenerationFound)
+      .WillOnce(SaveArg<0>(&form_generation_data));
+  // The change is discovered by PasswordManager.
+  manager()->OnPasswordFormsParsed(&driver_, {form_data});
+  EXPECT_EQ(new_password_field.unique_renderer_id,
+            form_generation_data.new_password_renderer_id);
 }
 
 TEST_P(PasswordManagerTest, SubmissionDetectedOnClearedForm) {
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 26dbbbf..7ca73f2 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -141,12 +141,6 @@
 const base::Feature kRecoverFromNeverSaveAndroid = {
     "RecoverFromNeverSaveAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables reparsing server predictions once the password form manager notices a
-// dynamic form change.
-const base::Feature kReparseServerPredictionsFollowingFormChange = {
-    "ReparseServerPredictionsFollowingFormChange",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables considering secondary server field predictions during form parsing.
 const base::Feature kSecondaryServerFieldPredictions = {
     "SecondaryServerFieldPredictions", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 77b665e..27aaa10 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -48,7 +48,6 @@
 extern const base::Feature kPasswordsAccountStorageRevisedOptInFlow;
 extern const base::Feature kPasswordScriptsFetching;
 extern const base::Feature kRecoverFromNeverSaveAndroid;
-extern const base::Feature kReparseServerPredictionsFollowingFormChange;
 extern const base::Feature kSecondaryServerFieldPredictions;
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 extern const base::Feature kSkipUndecryptablePasswords;
diff --git a/components/services/app_service/public/cpp/features.cc b/components/services/app_service/public/cpp/features.cc
index 15e6b28..89415285 100644
--- a/components/services/app_service/public/cpp/features.cc
+++ b/components/services/app_service/public/cpp/features.cc
@@ -13,4 +13,7 @@
 const base::Feature kAppServiceOnAppUpdateWithoutMojom{
     "AppServiceOnAppUpdateWithoutMojom", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature AppServiceCrosApiOnAppsWithoutMojom{
+    "AppServiceCrosApiOnAppsWithoutMojom", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/features.h b/components/services/app_service/public/cpp/features.h
index 2d8f0183..76aff34 100644
--- a/components/services/app_service/public/cpp/features.h
+++ b/components/services/app_service/public/cpp/features.h
@@ -14,6 +14,8 @@
 extern const base::Feature kAppServiceOnAppTypeInitializedWithoutMojom;
 COMPONENT_EXPORT(APP_UPDATE)
 extern const base::Feature kAppServiceOnAppUpdateWithoutMojom;
+COMPONENT_EXPORT(APP_UPDATE)
+extern const base::Feature AppServiceCrosApiOnAppsWithoutMojom;
 
 }  // namespace apps
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 54a46aa..60cf1de 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -16,6 +16,9 @@
   if (!is_android) {
     public_deps += [ "//components/sync/trusted_vault" ]
   }
+  if (is_chromeos) {
+    public_deps += [ "//components/sync/chromeos" ]
+  }
 }
 
 group("test_support") {
@@ -219,6 +222,10 @@
     ]
   }
 
+  if (is_chromeos) {
+    sources += [ "chromeos/explicit_passphrase_mojo_utils_unittest.cc" ]
+  }
+
   configs += [ "//build/config:precompiled_headers" ]
 
   data = [
diff --git a/components/sync/chromeos/BUILD.gn b/components/sync/chromeos/BUILD.gn
new file mode 100644
index 0000000..57863b5
--- /dev/null
+++ b/components/sync/chromeos/BUILD.gn
@@ -0,0 +1,12 @@
+# 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("chromeos") {
+  sources = [
+    "explicit_passphrase_mojo_utils.cc",
+    "explicit_passphrase_mojo_utils.h",
+  ]
+  public_deps = [ "//chromeos/crosapi/mojom" ]
+  deps = [ "//components/sync/engine" ]
+}
diff --git a/components/sync/chromeos/DEPS b/components/sync/chromeos/DEPS
new file mode 100644
index 0000000..3194d520
--- /dev/null
+++ b/components/sync/chromeos/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+chromeos/crosapi/mojom",
+  "+components/sync/engine",
+]
diff --git a/components/sync/chromeos/explicit_passphrase_mojo_utils.cc b/components/sync/chromeos/explicit_passphrase_mojo_utils.cc
new file mode 100644
index 0000000..1008f229
--- /dev/null
+++ b/components/sync/chromeos/explicit_passphrase_mojo_utils.cc
@@ -0,0 +1,38 @@
+// 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/sync/chromeos/explicit_passphrase_mojo_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "components/sync/engine/nigori/nigori.h"
+
+namespace syncer {
+
+crosapi::mojom::NigoriKeyPtr NigoriToMojo(const Nigori& nigori) {
+  std::string deprecated_user_key;
+  std::string encryption_key;
+  std::string mac_key;
+  nigori.ExportKeys(&deprecated_user_key, &encryption_key, &mac_key);
+
+  crosapi::mojom::NigoriKeyPtr mojo_result = crosapi::mojom::NigoriKey::New();
+  mojo_result->encryption_key =
+      std::vector<uint8_t>(encryption_key.begin(), encryption_key.end());
+  mojo_result->mac_key = std::vector<uint8_t>(mac_key.begin(), mac_key.end());
+  return mojo_result;
+}
+
+std::unique_ptr<Nigori> NigoriFromMojo(
+    const crosapi::mojom::NigoriKey& mojo_nigori_key) {
+  const std::string encryption_key(mojo_nigori_key.encryption_key.begin(),
+                                   mojo_nigori_key.encryption_key.end());
+  const std::string mac_key(mojo_nigori_key.mac_key.begin(),
+                            mojo_nigori_key.mac_key.end());
+  // |user_key| is deprecated, it's safe to pass empty string.
+  return Nigori::CreateByImport(
+      /*user_key=*/std::string(), encryption_key, mac_key);
+}
+
+}  // namespace syncer
diff --git a/components/sync/chromeos/explicit_passphrase_mojo_utils.h b/components/sync/chromeos/explicit_passphrase_mojo_utils.h
new file mode 100644
index 0000000..d38b9cc
--- /dev/null
+++ b/components/sync/chromeos/explicit_passphrase_mojo_utils.h
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_CHROMEOS_EXPLICIT_PASSPHRASE_MOJO_UTILS_H_
+#define COMPONENTS_SYNC_CHROMEOS_EXPLICIT_PASSPHRASE_MOJO_UTILS_H_
+
+#include <memory>
+
+#include "chromeos/crosapi/mojom/sync.mojom.h"
+
+namespace syncer {
+
+class Nigori;
+
+// Converts |nigori| into its mojo representation.
+crosapi::mojom::NigoriKeyPtr NigoriToMojo(const Nigori& nigori);
+
+// Creates Nigori from its mojo representation. Returns nullptr if
+// |mojo_nigori_key| doesn't represent a valid Nigori.
+std::unique_ptr<Nigori> NigoriFromMojo(
+    const crosapi::mojom::NigoriKey& mojo_nigori_key);
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_CHROMEOS_EXPLICIT_PASSPHRASE_MOJO_UTILS_H_
diff --git a/components/sync/chromeos/explicit_passphrase_mojo_utils_unittest.cc b/components/sync/chromeos/explicit_passphrase_mojo_utils_unittest.cc
new file mode 100644
index 0000000..6ca0a7c5
--- /dev/null
+++ b/components/sync/chromeos/explicit_passphrase_mojo_utils_unittest.cc
@@ -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.
+
+#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
+
+#include <string>
+
+#include "chromeos/crosapi/mojom/sync.mojom.h"
+#include "components/sync/engine/nigori/key_derivation_params.h"
+#include "components/sync/engine/nigori/nigori.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using testing::Eq;
+using testing::IsNull;
+using testing::NotNull;
+
+TEST(ExplicitPassphraseMojoUtilsTest, ShouldConvertNigoriToMojoAndBack) {
+  auto nigori1 = Nigori::CreateByDerivation(
+      KeyDerivationParams::CreateForPbkdf2(), "password");
+  ASSERT_THAT(nigori1, NotNull());
+
+  auto mojo_nigori_key = NigoriToMojo(*nigori1);
+  ASSERT_FALSE(mojo_nigori_key.is_null());
+
+  auto nigori2 = NigoriFromMojo(*mojo_nigori_key);
+  ASSERT_THAT(nigori2, NotNull());
+
+  std::string deprecated_user_key1;
+  std::string encryption_key1;
+  std::string mac_key1;
+  nigori1->ExportKeys(&deprecated_user_key1, &encryption_key1, &mac_key1);
+
+  std::string deprecated_user_key2;
+  std::string encryption_key2;
+  std::string mac_key2;
+  nigori2->ExportKeys(&deprecated_user_key2, &encryption_key2, &mac_key2);
+  // Don't check user key, because it's deprecated and safe to ignore.
+  EXPECT_THAT(encryption_key1, Eq(encryption_key2));
+  EXPECT_THAT(mac_key1, Eq(mac_key2));
+}
+
+TEST(ExplicitPassphraseMojoUtilsTest, ShouldFailMojoToNigoriIfMojoEmpty) {
+  auto empty_mojo_nigori_key = crosapi::mojom::NigoriKey::New();
+  EXPECT_THAT(NigoriFromMojo(*empty_mojo_nigori_key), IsNull());
+}
+
+TEST(ExplicitPassphraseMojoUtilsTest, ShouldFailMojoToNigoriIfMojoNotValid) {
+  auto invalid_mojo_nigori_key = crosapi::mojom::NigoriKey::New();
+  invalid_mojo_nigori_key->encryption_key = {1, 2, 3};
+  invalid_mojo_nigori_key->mac_key = {1, 2, 3};
+  EXPECT_THAT(NigoriFromMojo(*invalid_mojo_nigori_key), IsNull());
+}
+
+}  // namespace
+
+}  // namespace syncer
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn
index c506406..5929ea1 100644
--- a/components/vector_icons/BUILD.gn
+++ b/components/vector_icons/BUILD.gn
@@ -45,6 +45,7 @@
     "extension.icon",
     "feed.icon",
     "file_download.icon",
+    "file_download_off.icon",
     "folder.icon",
     "folder_managed.icon",
     "folder_managed_touch.icon",
diff --git a/components/vector_icons/file_download_off.icon b/components/vector_icons/file_download_off.icon
new file mode 100644
index 0000000..b1706b3
--- /dev/null
+++ b/components/vector_icons/file_download_off.icon
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 18, 15.17f,
+V_LINE_TO, 15,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, 2.17f,
+R_LINE_TO, -2, -2,
+CLOSE,
+R_MOVE_TO, -2.59f, -2.58f,
+LINE_TO, 17, 11,
+R_LINE_TO, -1.41f, -1.41f,
+LINE_TO, 14, 11.17f,
+R_LINE_TO, 1.41f, 1.42f,
+CLOSE,
+MOVE_TO, 13, 10.17f,
+V_LINE_TO, 4,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, 4.17f,
+R_LINE_TO, 2, 2,
+CLOSE,
+R_MOVE_TO, 8.19f, 11.02f,
+R_LINE_TO, -1.78f, -1.78f,
+R_LINE_TO, -16.6f, -16.6f,
+R_LINE_TO, -1.42f, 1.41f,
+R_LINE_TO, 6.19f, 6.19f,
+LINE_TO, 7, 11,
+R_LINE_TO, 5, 5,
+R_LINE_TO, 0.59f, -0.59f,
+LINE_TO, 15.17f, 18,
+H_LINE_TO, 6,
+R_V_LINE_TO, -3,
+H_LINE_TO, 4,
+R_V_LINE_TO, 3,
+R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2,
+R_H_LINE_TO, 11.17f,
+R_LINE_TO, 2.61f, 2.61f,
+R_LINE_TO, 1.41f, -1.42f,
+CLOSE
\ No newline at end of file
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index 9943ac5..52dc3be 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -604,6 +604,8 @@
       return wgpu::TextureFormat::RGBA16Float;
     case RGBA_1010102:
       return wgpu::TextureFormat::RGB10A2Unorm;
+    case YUV_420_BIPLANAR:
+      return wgpu::TextureFormat::R8BG8Biplanar420Unorm;
     case RGBA_4444:
     case RGB_565:
     case BGR_565:
@@ -611,7 +613,6 @@
     case RG16_EXT:
     case BGRA_1010102:
     case YVU_420:
-    case YUV_420_BIPLANAR:
     case ETC1:
     case LUMINANCE_F16:
     case P010:
diff --git a/components/viz/service/display/overlay_processor_ozone_unittest.cc b/components/viz/service/display/overlay_processor_ozone_unittest.cc
index 7f785d3..b8b5d5d9 100644
--- a/components/viz/service/display/overlay_processor_ozone_unittest.cc
+++ b/components/viz/service/display/overlay_processor_ozone_unittest.cc
@@ -68,6 +68,7 @@
   uint64_t GetBufferFormatModifier() const override { return 0; }
   gfx::BufferFormat GetBufferFormat() const override { return format_; }
   size_t GetNumberOfPlanes() const override { return 0; }
+  bool SupportsZeroCopyWebGPUImport() const override { return false; }
   gfx::Size GetBufferSize() const override { return size_; }
   uint32_t GetUniqueId() const override { return 0; }
   bool ScheduleOverlayPlane(
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 0387819..9d89c802 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -215,6 +215,18 @@
     SendCommand("Input.dispatchMouseEvent", std::move(params), wait);
   }
 
+  void InitMouseDownLog() {
+    ASSERT_TRUE(
+        content::ExecJs(shell()->web_contents(),
+                        "logs = []; window.addEventListener('mousedown', e => "
+                        "logs.push(`${e.type},${e.clientX},${e.clientY}`));"));
+  }
+
+  std::string GetMouseDownLog() {
+    return content::EvalJs(shell()->web_contents(), "window.logs.join(';')")
+        .ExtractString();
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -313,56 +325,42 @@
   EXPECT_EQ(3u, result_ids_.size());
 }
 
-// Event dispatch appears to be flaky on Android and Cast Audio Linux bots.
-// SendMouseEvent succeeds but event is not consumed by anything.
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMECAST)
-#define MAYBE_MouseEventCoordinates DISABLED_MouseEventCoordinates
-#else
-#define MAYBE_MouseEventCoordinates MouseEventCoordinates
-#endif
-IN_PROC_BROWSER_TEST_F(SyntheticMouseEventTest, MAYBE_MouseEventCoordinates) {
+IN_PROC_BROWSER_TEST_F(SyntheticMouseEventTest, MouseEventCoordinates) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_url = embedded_test_server()->GetURL("/devtools/zoom.html");
   NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
   Attach();
-  ASSERT_TRUE(
-      content::ExecJs(shell()->web_contents(),
-                      "logs = []; window.addEventListener('mousedown', e => "
-                      "logs.push(`${e.type},${e.clientX},${e.clientY}`));"));
-
-  SendMouseEvent("mousePressed", 15, 15, "left", true);
-
-  ASSERT_EQ("mousedown,15,15",
-            content::EvalJs(shell()->web_contents(), "window.logs.join(';')")
-                .ExtractString());
+  InitMouseDownLog();
+  // In about 1 out of 1000 runs, the event gets lost on the way to the
+  // renderer. We repeat the event dispatch until it succeeds since we want to
+  // test event coordinates.
+  while (GetMouseDownLog() == "") {
+    SendMouseEvent("mousePressed", 15, 15, "left", true);
+  }
+  ASSERT_EQ("mousedown,15,15", GetMouseDownLog());
 }
 
-// Event dispatch appears to be flaky on Android and Cast Audio Linux bots.
-// SendMouseEvent succeeds but event is not consumed by anything.
-// TODO(https://crbug.com/1313348): This is also flaky on other bots.
-IN_PROC_BROWSER_TEST_F(SyntheticMouseEventTest,
-                       DISABLED_MouseEventCoordinatesWithZoom) {
+IN_PROC_BROWSER_TEST_F(SyntheticMouseEventTest, MouseEventCoordinatesWithZoom) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_url = embedded_test_server()->GetURL("/devtools/zoom.html");
   NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
   Attach();
+  SendCommand("Page.enable", nullptr, true);
+  InitMouseDownLog();
 
-  SendCommand("Page.enable", nullptr);
-
-  ASSERT_TRUE(
-      content::ExecJs(shell()->web_contents(),
-                      "logs = []; window.addEventListener('mousedown', e => "
-                      "logs.push(`${e.type},${e.clientX},${e.clientY}`));"));
   HostZoomMap* host_zoom_map =
       HostZoomMap::GetForWebContents(shell()->web_contents());
   host_zoom_map->SetZoomLevelForHost(test_url.host(),
                                      blink::PageZoomFactorToZoomLevel(2.5));
   ASSERT_TRUE(WaitForNotification("Page.frameResized", true));
-  SendMouseEvent("mousePressed", 15, 15, "left", true);
 
-  ASSERT_EQ("mousedown,15,15",
-            content::EvalJs(shell()->web_contents(), "window.logs.join(';')")
-                .ExtractString());
+  // In about 1 out of 1000 runs, the event gets lost on the way to the
+  // renderer. We repeat the event dispatch until it succeeds since we want to
+  // test event coordinates.
+  while (GetMouseDownLog() == "") {
+    SendMouseEvent("mousePressed", 15, 15, "left", true);
+  }
+  ASSERT_EQ("mousedown,15,15", GetMouseDownLog());
 }
 
 namespace {
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index a94306a..7ad945f 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -19315,15 +19315,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_TRUE(capturer.did_replace_entry());
 
-    // We keep the same history.state value (except when RenderDocument
-    // subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 
   {
@@ -19349,15 +19342,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_FALSE(capturer.did_replace_entry());
 
-    // We keep the same history.state value (except when RenderDocument
-    // subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 
   // 3) Test navigating the subframe to the same URL, but it ends up in an error
@@ -19419,15 +19405,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_FALSE(capturer.did_replace_entry());
 
-    // We keep the history.state value from before the failed navigation (except
-    // when RenderDocument subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 }
 
@@ -19485,15 +19464,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_EQ(2, controller.GetEntryCount());
 
-    // We keep the same history.state value (except when RenderDocument
-    // subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 
   {
@@ -19567,15 +19539,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_EQ(1, controller.GetEntryCount());
 
-    // We keep the same history.state value (except when RenderDocument
-    // subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 
   {
@@ -19602,15 +19567,8 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_EQ(1, controller.GetEntryCount());
 
-    // We keep the same history.state value (except when RenderDocument
-    // subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
-    if (ShouldCreateNewHostForSameSiteSubframe()) {
-      EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
-    } else {
-      EXPECT_EQ("foo", EvalJs(child, "history.state"));
-    }
+    // We keep the same history.state value.
+    EXPECT_EQ("foo", EvalJs(child, "history.state"));
   }
 
   {
@@ -19676,14 +19634,10 @@
               controller.GetLastCommittedEntry()->GetFrameEntry(child));
     EXPECT_EQ(1, controller.GetEntryCount());
 
-    // We keep the history.state value from before the failed navigation (except
-    // when RenderDocument subframe is on).
-    // TODO(http://crbug.com/1068965): Keep the history.state even with
-    // RenderDocument.
+    // We keep the history.state value from before the failed navigation.
     // TODO(http://crbug.com/1188956): Ensure error page isolation correctly
     // maintains history.state as well.
-    if (ShouldCreateNewHostForSameSiteSubframe() ||
-        SiteIsolationPolicy::IsErrorPageIsolationEnabled(false)) {
+    if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(false)) {
       EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
     } else {
       EXPECT_EQ("foo", EvalJs(child, "history.state"));
@@ -20496,10 +20450,6 @@
   EXPECT_EQ(network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin,
             frame_entry_2->referrer().policy);
 
-  // TODO(http://crbug.com/1068965): Remove this when test passes.
-  if (ShouldCreateNewHostForSameSiteSubframe())
-    return;
-
   int item_sequence_number_2 = frame_entry_1->item_sequence_number();
   int document_sequence_number_2 = frame_entry_1->document_sequence_number();
   EXPECT_EQ(item_sequence_number_1, item_sequence_number_2);
@@ -21267,6 +21217,57 @@
   EXPECT_EQ(0, EvalJs(root, "navigationTiming[0].unloadEventEnd"));
 }
 
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       SameURLNavigationRetainsHistoryState) {
+  GURL main_frame_url(embedded_test_server()->GetURL("/page_with_iframe.html"));
+  GURL subframe_url(embedded_test_server()->GetURL("/title1.html"));
+  GURL other_url(embedded_test_server()->GetURL("/title2.html"));
+  // Navigate to a page with an iframe.
+  EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1u, root->child_count());
+  FrameTreeNode* child = root->child_at(0u);
+  EXPECT_EQ(main_frame_url, root->current_url());
+  EXPECT_EQ(subframe_url, child->current_url());
+
+  // Do a replaceState on the subframe to set the history.state to "foo".
+  ReplaceState(child, "foo");
+
+  // Navigate the subframe to the same URL it is currently on.
+  {
+    FrameNavigateParamsCapturer capturer(child);
+    ASSERT_TRUE(NavigateFrameToURL(child, subframe_url));
+    capturer.Wait();
+    EXPECT_EQ(subframe_url, child->current_url());
+  }
+  // Check that the history.state is retained after the navigation.
+  EXPECT_EQ("foo", EvalJs(child, "history.state"));
+
+  // Navigate the subframe to a different URL.
+  {
+    FrameNavigateParamsCapturer capturer(child);
+    ASSERT_TRUE(NavigateFrameToURL(child, other_url));
+    capturer.Wait();
+    EXPECT_EQ(other_url, child->current_url());
+  }
+  // Check that the history.state is not retained after the navigation.
+  EXPECT_EQ(nullptr, EvalJs(child, "history.state"));
+
+  // Do a replaceState on the main frame to set the history.state to "bar".
+  ReplaceState(root, "bar");
+  EXPECT_EQ("bar", EvalJs(root, "history.state"));
+
+  // Navigate the main frame to the same URL it's currently on.
+  ASSERT_TRUE(NavigateToURL(shell(), root->current_url()));
+  // Check that the history.state is retained after the navigation.
+  EXPECT_EQ("bar", EvalJs(root, "history.state"));
+
+  // Navigate the main frame to a different URL.
+  ASSERT_TRUE(NavigateToURL(shell(), other_url));
+  // Check that the history.state is not retained after the navigation.
+  EXPECT_EQ(nullptr, EvalJs(root, "history.state"));
+}
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     NavigationControllerAlertDialogBrowserTest,
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index ea6acd7b5..ef678d80 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -6159,9 +6159,6 @@
   // to make network request on behalf of the real origin.
   DCHECK(!IsMhtmlOrSubframe() || origin.opaque());
 
-  // https://crbug.com/1041376) of the origin that will be committed because of
-  // |this| NavigationRequest.
-
   // Note that GetRenderFrameHost() only allows to retrieve the RenderFrameHost
   // once it has been set for this navigation.  This will happens either at
   // WillProcessResponse time for regular navigations or at WillFailRequest time
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e1ae956a..55e3ea0a 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1406,14 +1406,6 @@
 // TODO(creis): Make the net error page show in the correct process as well,
 // per https://crbug.com/588314.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProcessTransferAfterError) {
-  // This test doesn't yet work properly with RenderDocument for subframes,
-  // because it does not correctly identify the repeated navigation to the same
-  // URL as same-page, and fails to reuse the same NavigationEntry in this
-  // case. See https://crbug.com/1068965.
-  // TODO(fergal,alexmos): Re-enable once this is fixed.
-  if (ShouldCreateNewHostForSameSiteSubframe())
-    return;
-
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index 845ed5c..e451a614 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -18,6 +18,7 @@
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_run_loop_timeout.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -881,14 +882,13 @@
 // When an iframe is detached, check that unload handlers execute in all of its
 // child frames. Start from A(B1(C(B2))) and delete B1 from A.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-// TODO(crbug.com/1311985): Re-enable this test
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_DetachedIframeUnloadHandlerABCB \
-  DISABLED_DetachedIframeUnloadHandlerABCB
-#else
-#define MAYBE_DetachedIframeUnloadHandlerABCB DetachedIframeUnloadHandlerABCB
-#endif
-                       MAYBE_DetachedIframeUnloadHandlerABCB) {
+                       DetachedIframeUnloadHandlerABCB) {
+  // This test takes longer to run, because multiple processes are waiting on
+  // each other's documents to execute unload handler before destroying their
+  // documents. https://crbug.com/1311985
+  base::test::ScopedRunLoopTimeout increase_timeout(
+      FROM_HERE, TestTimeouts::action_max_timeout());
+
   GURL initial_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c(b)))"));
 
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index 9c1943a..793e355 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -122,8 +122,8 @@
                            DragDropVirtualFilesOriginateFromRenderer);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropUrlData);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropOnOopif);
-  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, OnPerformDrop_DeepScanOK);
-  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, OnPerformDrop_DeepScanBad);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, Drop_DeepScanOK);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, Drop_DeepScanBad);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, StartDragging);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, GetDropCallback_Run);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, GetDropCallback_Cancelled);
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index ec35580..2381e27 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -633,7 +633,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OnPerformDrop_DeepScanOK) {
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, Drop_DeepScanOK) {
   StartTestWithPage("/simple_page.html");
   WebContentsImpl* contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
@@ -680,7 +680,9 @@
 
   // The user can drag something in and drop it.
   view->OnDragEntered(new_event);
-  view->OnPerformDrop(new_event, std::move(new_data));
+  drop_cb = view->GetDropCallback(new_event);
+  output_drag_op = ui::mojom::DragOperation::kNone;
+  std::move(drop_cb).Run(std::move(new_data), output_drag_op);
   EXPECT_FALSE(drag_dest_delegate_.GetOnDropCalled());
 
   delegate->FinishScan();
@@ -690,7 +692,7 @@
   EXPECT_FALSE(drag_dest_delegate_.GetOnDragLeaveCalled());
 }
 
-IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OnPerformDrop_DeepScanBad) {
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, Drop_DeepScanBad) {
   StartTestWithPage("/simple_page.html");
   WebContentsImpl* contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
@@ -737,7 +739,9 @@
 
   // The user can drag something in and drop it.
   view->OnDragEntered(new_event);
-  view->OnPerformDrop(new_event, std::move(new_data));
+  drop_cb = view->GetDropCallback(new_event);
+  output_drag_op = ui::mojom::DragOperation::kNone;
+  std::move(drop_cb).Run(std::move(new_data), output_drag_op);
   EXPECT_FALSE(drag_dest_delegate_.GetOnDropCalled());
 
   delegate->FinishScan();
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 88f08f7..f50c2487 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -334,8 +334,6 @@
           {"AutofillShadowDOM", blink::features::kAutofillShadowDOM},
           {"AndroidDownloadableFontsMatching",
            features::kAndroidDownloadableFontsMatching},
-          {"BiddingAndScoringDebugReportingAPI",
-           blink::features::kBiddingAndScoringDebugReportingAPI},
           {"ClipboardCustomFormats", blink::features::kClipboardCustomFormats},
           {"CSSContainerQueries", blink::features::kCSSContainerQueries},
           {"ComputePressure", blink::features::kComputePressure,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index e6103bf..7d5859fe 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1058,7 +1058,7 @@
 
 // Enable WebAssembly dynamic tiering (only tier up hot functions).
 const base::Feature kWebAssemblyDynamicTiering{
-    "WebAssemblyDynamicTiering", base::FEATURE_DISABLED_BY_DEFAULT};
+    "WebAssemblyDynamicTiering", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable WebAssembly lazy compilation (JIT on first call).
 const base::Feature kWebAssemblyLazyCompilation{
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 475f3e1..2d7e80c 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -226,6 +226,8 @@
 
   SetV8FlagIfFeature(features::kWebAssemblyDynamicTiering,
                      "--wasm-dynamic-tiering");
+  SetV8FlagIfNotFeature(features::kWebAssemblyDynamicTiering,
+                        "--no-wasm-dynamic-tiering");
 
 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(ARCH_CPU_X86_64)
   if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md
index c072bf8..a9d2b0d9 100644
--- a/docs/android_build_instructions.md
+++ b/docs/android_build_instructions.md
@@ -397,7 +397,7 @@
 Alternatively, you can set up the server as a Linux service, so it runs on the
 background and starts on boot. If you're using systemd:
 
-Save the following as /etc/systemd/system/fast-local-dev-server.service.
+Save the following as /etc/systemd/user/fast-local-dev-server.service.
 ```
 [Unit]
 Description=Chrome server for android build static analysis
@@ -408,19 +408,19 @@
 Restart=always
 
 [Install]
-WantedBy=multi-user.target
+WantedBy=default.target
 ```
 
 Then
 ```bash
-sudo systemctl daemon-reload
-sudo systemctl enable fast-local-dev-server
-sudo systemctl start fast-local-dev-server
+systemctl --user daemon-reload
+systemctl --user enable fast-local-dev-server
+systemctl --user start fast-local-dev-server
 ```
 
 The output can be inspected with
 ```
-journalctl -e -u fast-local-dev-server
+journalctl --user -e -u fast-local-dev-server
 ```
 
 **Note**: Since the build completes before the analysis checks finish, the build
diff --git a/docs/enterprise/active_directory_native_integration.md b/docs/enterprise/active_directory_native_integration.md
index 88126556..76d3e2f 100644
--- a/docs/enterprise/active_directory_native_integration.md
+++ b/docs/enterprise/active_directory_native_integration.md
@@ -52,12 +52,12 @@
 
 ## Chrome Architecture
 The following Chrome classes are most relevant for the AD integration:
-[AuthPolicyClient](https://cs.chromium.org/chromium/src/chromeos/dbus/authpolicy/authpolicy_client.h)
+[AuthPolicyClient](https://cs.chromium.org/chromium/src/chromeos/ash/components/dbus/authpolicy/authpolicy_client.h)
 is the D-Bus client for the authpolicy daemon. All authpolicy D-Bus calls are
 routed through it. The
 [AuthPolicyHelper](https://cs.chromium.org/chromium/src/chrome/browser/ash/authpolicy/authpolicy_helper.h)
 is a thin abstraction layer on top of the
-[AuthPolicyClient](https://cs.chromium.org/chromium/src/chromeos/dbus/authpolicy/authpolicy_client.h)
+[AuthPolicyClient](https://cs.chromium.org/chromium/src/chromeos/ash/components/dbus/authpolicy/authpolicy_client.h)
 to handle cancellation and other stuff. The
 [AuthPolicyCredentialsManager](https://cs.chromium.org/chromium/src/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.h)
 keeps track of user credential status, shows notifications if the Kerberos
diff --git a/docs/profiling.md b/docs/profiling.md
index 6b85f69..c293aa35 100644
--- a/docs/profiling.md
+++ b/docs/profiling.md
@@ -32,50 +32,6 @@
     blink_symbol_level = 2
     symbol_level = 2
 
-    # Needed for built-in profiling only
-    enable_profiling = true
-
-#### Preparing your environment
-
-By default, the profiler will take a sample 100 times per second. You can adjust this rate by setting the `CPUPROFILE_FREQUENCY` environment variable before launching chromium:
-
-    $ export CPUPROFILE_FREQUENCY=1000
-    
-The maximum supported rate is 4000 samples per second.
-
-#### Profiling a process over its entire lifetime
-
-To profile the main browser process, add the following argument to your chrome invocation:
-
-    --enable-profiling --profiling-at-start
-
-To profile, e.g., every renderer process, add the following argument to your chrome invocation:
-
-    --enable-profiling --profiling-at-start=renderer --no-sandbox
-
-To profile the gpu process, add the following argument to your chrome invocation:
-
-    --enable-profiling --profiling-at-start=gpu-process --no-sandbox --profiling-flush
-
-The gpu process does not shut down cleanly and so requires periodic flushing to
-write the profile to disk.
-
-*** promo
-The --no-sandbox argument is required to allow the renderer process to write the profiling output to the file system.
-***
-
-When the process being profiled ends, you should see one or more `chrome-profile-{process type}-{process ID}` files in your `$PWD`. Run `pprof` to view the results, e.g.:
-
-    $ pprof -web chrome-profile-renderer-12345
-    
-*** promo
-`pprof` is packed with useful features for visualizing profiling data. Try `pprof --help` for more info.
-***
-
-*** promo
-Tip for Googlers: running `prodaccess` first will make `pprof` run faster, and eliminate some useless spew to the terminal.
-***
-
 ## Profiling a process or thread for a defined period of time using perf
 
 First, make sure you have the `linux-perf` package installed:
@@ -89,7 +45,7 @@
     $ perf record -g -p <Process ID> -o <output file>
     
 *** promo
-`perf` does not honor the `CPUPROFILE_FREQUENCY` env var. To adjust the sampling frequency, use the `-F` argument, e.g., `-F 1000`.
+To adjust the sampling frequency, use the `-F` argument, e.g., `-F 1000`.
 ***
 
 To stop profiling, press `Control-c` in the terminal window where `perf` is running. Run `pprof` to view the results, providing the path to the browser executable; e.g.:
@@ -100,6 +56,10 @@
 `pprof` is packed with useful features for visualizing profiling data. Try `pprof --help` for more info.
 ***
 
+*** promo
+Tip for Googlers: running `gcert` first will make `pprof` run faster, and eliminate some useless spew to the terminal.
+***
+
 If you want to limit the profile to a single thread, run:
 
     $ ps -T -p <Process ID> 
diff --git a/docs/speed/metrics_changelog/2022_03_lcp_fcp.md b/docs/speed/metrics_changelog/2022_03_lcp_fcp.md
new file mode 100644
index 0000000..a258440
--- /dev/null
+++ b/docs/speed/metrics_changelog/2022_03_lcp_fcp.md
@@ -0,0 +1,33 @@
+# Loading metrics changes in Chrome 99
+
+## Navigation optimizations
+
+A few changes are being rolled out that may impact loading metrics, such as
+FCP and LCP. Most of these changes are
+[browser optimizations](https://blog.chromium.org/2022/03/a-new-speed-milestone-for-chrome.html)
+that should improve those loading metrics for everyone.
+
+## `timeOrigin` to take pre-`beforeUnload` IPC into account
+
+We also improved the accuracy of the `timeOrigin` value, on which all loading
+metrics are based. It now [takes into account an extra IPC call that happens
+before `beforeUnload` event handlers](https://bugs.chromium.org/p/chromium/issues/detail?id=1288485).
+In scenarios where traffic to your site is coming from a site with a
+`beforeUnload` handler, your timeline metrics are likely to slightly change:
+because the `timeOrigin` value is now earlier, durations relative to
+`timeOrigin` will appear to be larger.
+
+Note that the duration of the `beforeUnload` event itself is not included in
+the `timeOrigin`, as its semantic meaning is not changed.
+
+## How does this affect a site's metrics?
+On the whole, users will see significant improvements to both FCP and LCP due
+to this effort.
+
+The pre-`beforeUnload` IPC change means that if large parts of the traffic to
+your site are coming from sites with a `beforeUnload` handler, you may see a
+mix of metric regressions and progressions.
+Otherwise, you're likely to see your metrics improve.
+
+## When were users affected?
+This change is being rolled out starting from Chrome 99.
\ No newline at end of file
diff --git a/docs/speed/metrics_changelog/fcp.md b/docs/speed/metrics_changelog/fcp.md
index 4f64ac1..3f2b7517 100644
--- a/docs/speed/metrics_changelog/fcp.md
+++ b/docs/speed/metrics_changelog/fcp.md
@@ -2,6 +2,8 @@
 
 This is a list of changes to [First Contentful Paint](https://web.dev/fcp).
 
+* Chrome 99
+  * Implementation optimizations: [Navigation optimizations and timeOrigin changes](2022_03_lcp_fcp_fid.md)
 * Chrome 94
   * Metric definition improvement: [Paint timing is only reported when the painted content
   is not under opacity:0](2021_07_fcp.md)
diff --git a/docs/speed/metrics_changelog/lcp.md b/docs/speed/metrics_changelog/lcp.md
index a8fefbe..c74d29d3 100644
--- a/docs/speed/metrics_changelog/lcp.md
+++ b/docs/speed/metrics_changelog/lcp.md
@@ -2,6 +2,8 @@
 
 This is a list of changes to [Largest Contentful Paint](https://web.dev/lcp).
 
+* Chrome 99
+  * Implementation optimizations: [Navigation optimizations and timeOrigin changes](2022_03_lcp_fcp_fid.md)
 * Chrome 98
   * Metric bug fix: [Text paints are more accurate](2021_11_lcp.md)
 * Chrome 96
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index efe4f2db..6517eb5 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -740,6 +740,7 @@
 
     deps += [
       "//ash/components/audio",
+      "//chromeos/ash/components/dbus/upstart",
       "//chromeos/dbus:test_support",
       "//chromeos/dbus/audio",
       "//chromeos/dbus/cec_service",
@@ -748,7 +749,6 @@
       "//chromeos/dbus/permission_broker",
       "//chromeos/dbus/power",
       "//chromeos/dbus/power:power_manager_proto",
-      "//chromeos/dbus/upstart",
       "//chromeos/login/login_state",
       "//chromeos/network",
       "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
@@ -984,13 +984,13 @@
       "//chrome/browser/ash/crosapi:browser_util",
       "//chrome/common:constants",
       "//chromeos:test_support",
+      "//chromeos/ash/components/dbus/upstart",
       "//chromeos/dbus:test_support",
       "//chromeos/dbus/audio",
       "//chromeos/dbus/media_analytics",
       "//chromeos/dbus/media_analytics:media_perception_proto",
       "//chromeos/dbus/permission_broker",
       "//chromeos/dbus/power",
-      "//chromeos/dbus/upstart",
       "//chromeos/login/login_state",
       "//chromeos/network:test_support",
       "//components/feedback",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 61e9cc8..20655d4 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -98,10 +98,10 @@
 
     deps += [
       "//chromeos",
+      "//chromeos/ash/components/dbus/upstart",
       "//chromeos/dbus",
       "//chromeos/dbus/media_analytics",
       "//chromeos/dbus/media_analytics:media_perception_proto",
-      "//chromeos/dbus/upstart",
       "//chromeos/login/login_state",
       "//chromeos/services/media_perception/public/mojom",
       "//chromeos/services/media_perception/public/mojom:mojom_js_data_deps",
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
index b0e8a4ca..af8dc5d 100644
--- a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
@@ -13,8 +13,8 @@
 #include "base/lazy_instance.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/api/media_perception_private/conversion_utils.h"
 #include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h"
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager_unittest.cc b/extensions/browser/api/media_perception_private/media_perception_api_manager_unittest.cc
index ecef9321..989e0c9 100644
--- a/extensions/browser/api/media_perception_private/media_perception_api_manager_unittest.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_api_manager_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/bind.h"
 #include "base/containers/queue.h"
 #include "base/run_loop.h"
+#include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
 #include "chromeos/dbus/media_analytics/fake_media_analytics_client.h"
 #include "chromeos/dbus/media_analytics/media_analytics_client.h"
-#include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
index 51ef64f..400323e 100644
--- a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
@@ -6,10 +6,10 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
 #include "chromeos/dbus/media_analytics/fake_media_analytics_client.h"
 #include "chromeos/dbus/media_analytics/media_analytics_client.h"
 #include "chromeos/dbus/media_perception/media_perception.pb.h"
-#include "chromeos/dbus/upstart/upstart_client.h"
 #include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h"
 #include "extensions/browser/api/media_perception_private/media_perception_private_api.h"
 #include "extensions/common/api/media_perception_private.h"
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index 1023715..45684a5 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -39,11 +39,13 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/kiosk/kiosk_delegate.h"
 #include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
 #include "extensions/common/extension_api.h"
 #include "extensions/common/extension_messages.h"
+#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
 #include "extensions/common/mojom/renderer.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-forward.h"
 
@@ -141,9 +143,17 @@
   std::ignore = ExtensionFunctionMemoryDumpProvider::GetInstance();
 }
 
+// Adds Kiosk. prefix to uma histograms if running in a kiosk extension.
+std::string WrapUma(const std::string& uma, bool is_kiosk_enabled) {
+  if (is_kiosk_enabled)
+    return uma + ".Kiosk";
+  return uma;
+}
+
 // Logs UMA about the performance for a given extension function run.
 void LogUma(bool success,
             base::TimeDelta elapsed_time,
+            bool is_kiosk_enabled,
             extensions::functions::HistogramValue histogram_value) {
   // Note: Certain functions perform actions that are inherently slow - such as
   // anything waiting on user action. As such, we can't always assume that a
@@ -178,24 +188,36 @@
       base::UmaHistogramSparse("Extensions.Functions.FailedTime.Over10ms",
                                histogram_value);
     }
-    UMA_HISTOGRAM_TIMES("Extensions.Functions.FailedTotalExecutionTime",
-                        elapsed_time);
+    base::UmaHistogramTimes(
+        WrapUma("Extensions.Functions.FailedTotalExecutionTime",
+                is_kiosk_enabled),
+        elapsed_time);
   }
 }
 
-void LogBadMessage(extensions::functions::HistogramValue histogram_value) {
+void LogBadMessage(bool is_kiosk_enabled,
+                   extensions::functions::HistogramValue histogram_value) {
   base::RecordAction(base::UserMetricsAction("BadMessageTerminate_EFD"));
   // Track the specific function's |histogram_value|, as this may indicate a
   // bug in that API's implementation.
-  base::UmaHistogramSparse("Extensions.BadMessageFunctionName",
-                           histogram_value);
+  base::UmaHistogramSparse(
+      WrapUma("Extensions.BadMessageFunctionName", is_kiosk_enabled),
+      histogram_value);
+}
+
+bool IsKiosk(const extensions::Extension* extension) {
+  extensions::KioskDelegate* const kiosk_delegate =
+      extensions::ExtensionsBrowserClient::Get()->GetKioskDelegate();
+  return kiosk_delegate && extension &&
+         kiosk_delegate->IsAutoLaunchedKioskApp(extension->id());
 }
 
 template <class T>
 void ReceivedBadMessage(T* bad_message_sender,
                         extensions::bad_message::BadMessageReason reason,
+                        bool is_kiosk_enabled,
                         extensions::functions::HistogramValue histogram_value) {
-  LogBadMessage(histogram_value);
+  LogBadMessage(is_kiosk_enabled, histogram_value);
   // The renderer has done validation before sending extension api requests.
   // Therefore, we should never receive a request that is invalid in a way
   // that JSON validation in the renderer should have caught. It could be an
@@ -552,7 +574,7 @@
                        is_from_service_worker()
                            ? extensions::bad_message::EFD_BAD_MESSAGE_WORKER
                            : extensions::bad_message::EFD_BAD_MESSAGE,
-                       histogram_value());
+                       IsKiosk(extension_.get()), histogram_value());
   }
 }
 
@@ -804,7 +826,8 @@
   }
 
   std::move(response_callback_).Run(response, std::move(results), GetError());
-  LogUma(success, timer_.Elapsed(), histogram_value_);
+  LogUma(success, timer_.Elapsed(), IsKiosk(extension_.get()),
+         histogram_value_);
 
   OnResponded();
 }
diff --git a/extensions/common/api/storage.json b/extensions/common/api/storage.json
index b235a594..5fbb386 100644
--- a/extensions/common/api/storage.json
+++ b/extensions/common/api/storage.json
@@ -10,7 +10,6 @@
     "types": [
       {
         "id": "AccessLevel",
-        "nodoc": true,
         "type": "string",
         "enum": ["TRUSTED_CONTEXTS", "TRUSTED_AND_UNTRUSTED_CONTEXTS"],
         "description": "The storage area's access level."
@@ -154,7 +153,6 @@
             }
           }, {
             "name": "setAccessLevel",
-            "nodoc": true,
             "type": "function",
             "description": "Sets the desired access level for the storage area. The default will be only trusted contexts.",
             "parameters": [
@@ -265,7 +263,6 @@
       "session" : {
         "$ref": "StorageArea",
         "description": "Items in the <code>session</code> storage area are stored in-memory and will not be persisted to disk.",
-        "nodoc": true,
         "value": ["session"],
         "properties": {
           "QUOTA_BYTES": {
diff --git a/gpu/command_buffer/service/shared_image_representation_dawn_ozone.cc b/gpu/command_buffer/service/shared_image_representation_dawn_ozone.cc
index 3550e2b..3cddac3 100644
--- a/gpu/command_buffer/service/shared_image_representation_dawn_ozone.cc
+++ b/gpu/command_buffer/service/shared_image_representation_dawn_ozone.cc
@@ -51,11 +51,19 @@
   if (texture_) {
     return nullptr;
   }
+
+  // For multi-planar formats, Mesa is yet to support to allocate and bind
+  // vkmemory for each plane respectively.
+  // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/intel/vulkan/anv_formats.c#L765
+  // For now we assume all plane handles are same, and we don't use the
+  // VK_IMAGE_CREATE_DISJOINT_BIT when creating the vkimage for the pixmap.
+  DCHECK(pixmap_->SupportsZeroCopyWebGPUImport() ||
+         pixmap_->GetNumberOfPlanes() == 1)
+      << "Disjoint Multi-plane importing is not supported.";
+
   if (!ozone_backing()->VaSync()) {
     return nullptr;
   }
-  DCHECK(pixmap_->GetNumberOfPlanes() == 1)
-      << "Multi-plane formats are not supported.";
 
   std::vector<gfx::GpuFenceHandle> fences;
   bool need_end_fence;
@@ -94,6 +102,7 @@
   // closed twice (once by ScopedFD and once by the Vulkan implementation).
   int fd = dup(pixmap_->GetDmaBufFd(0));
   descriptor.memoryFD = fd;
+  // stride is not required for multi-planar formats.
   descriptor.stride = pixmap_->GetDmaBufPitch(0);
   descriptor.drmModifier = pixmap_->GetBufferFormatModifier();
   descriptor.waitFDs = {};
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 6d69469..c14c8832 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1099,6 +1099,14 @@
     // Pass something invalid.
     required_features.push_back(static_cast<WGPUFeatureName>(-1));
   }
+
+  // Always enable "multi-planar-formats" as long as available.
+  if (dawn_adapters_[requested_adapter_index]
+          .GetAdapterProperties()
+          .multiPlanarFormats) {
+    required_features.push_back(WGPUFeatureName_DawnMultiPlanarFormats);
+  }
+
   device_descriptor.requiredFeatures = required_features.data();
   device_descriptor.requiredFeaturesCount = required_features.size();
 
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 75505cb6..ba60aef 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -78,9 +78,19 @@
 const base::Feature kWebViewSurfaceControl{"WebViewSurfaceControl",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Same as kWebViewSurfaceControl, but affects only Android T+, used for
+// targeting pre-release version.
+const base::Feature kWebViewSurfaceControlForT{
+    "WebViewSurfaceControlForT", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Use thread-safe media path on WebView.
 const base::Feature kWebViewThreadSafeMedia{"WebViewThreadSafeMedia",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+// This is used as default state because it's different for webview and chrome.
+// WebView hardcodes this as enabled in AwMainDelegate.
+const base::Feature kWebViewThreadSafeMediaDefault{
+    "WebViewThreadSafeMediaDefault", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Use AImageReader for MediaCodec and MediaPlyer on android.
 const base::Feature kAImageReader{"AImageReader",
@@ -345,7 +355,15 @@
   if (!IsAImageReaderEnabled() || LimitAImageReaderMaxSizeToOne())
     return false;
 
-  return base::FeatureList::IsEnabled(kWebViewThreadSafeMedia);
+  // If the feature is overridden from command line or finch we will use its
+  // value. If not we use kWebViewThreadSafeMediaDefault which is set in
+  // AwMainDelegate for WebView.
+  base::FeatureList* feature_list = base::FeatureList::GetInstance();
+  if (feature_list &&
+      feature_list->IsFeatureOverridden(kWebViewThreadSafeMedia.name))
+    return base::FeatureList::IsEnabled(kWebViewThreadSafeMedia);
+
+  return base::FeatureList::IsEnabled(kWebViewThreadSafeMediaDefault);
 #else
   return false;
 #endif
@@ -391,9 +409,19 @@
 
   // On WebView we also require zero copy or thread-safe media to use
   // SurfaceControl
-  if ((IsWebViewZeroCopyVideoEnabled() || IsUsingThreadSafeMediaForWebView()) &&
-      base::FeatureList::IsEnabled(kWebViewSurfaceControl))
-    return true;
+  if (IsWebViewZeroCopyVideoEnabled() || IsUsingThreadSafeMediaForWebView()) {
+    // If main feature is not overridden from command line and we're running T+
+    // use kWebViewSurfaceControlForT to decide feature status instead so we
+    // can target pre-release android to fish out platform side bugs.
+    base::FeatureList* feature_list = base::FeatureList::GetInstance();
+    if ((!feature_list || !feature_list->IsFeatureOverriddenFromCommandLine(
+                              features::kWebViewSurfaceControl.name)) &&
+        build_info->is_at_least_t()) {
+      return base::FeatureList::IsEnabled(kWebViewSurfaceControlForT);
+    }
+
+    return base::FeatureList::IsEnabled(kWebViewSurfaceControl);
+  }
 
   return base::FeatureList::IsEnabled(kAndroidSurfaceControl);
 }
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index 943273d..91e0c9a 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -24,6 +24,7 @@
 GPU_EXPORT extern const base::Feature kWebViewVulkan;
 GPU_EXPORT extern const base::Feature kLimitAImageReaderMaxSizeToOne;
 GPU_EXPORT extern const base::Feature kWebViewZeroCopyVideo;
+GPU_EXPORT extern const base::Feature kWebViewThreadSafeMediaDefault;
 GPU_EXPORT extern const base::Feature kIncreaseBufferCountForHighFrameRate;
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index 07a9e50..7535d534 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -9,6 +9,10 @@
 import("//testing/test.gni")
 import("features.gni")
 
+if (ozone_platform_wayland) {
+  import("//third_party/wayland/features.gni")
+}
+
 # Generate a buildflag header for compile-time checking of Vulkan support.
 buildflag_header("buildflags") {
   header = "buildflags.h"
@@ -22,6 +26,9 @@
     if (use_vulkan_xcb) {
       defines += [ "USE_VULKAN_XCB" ]
     }
+    if (ozone_platform_wayland && !use_system_libwayland) {
+      configs = [ "//third_party/wayland:wayland_config" ]
+    }
   }
 
   source_set("vulkan_function_pointers") {
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 4c32907..f5a9c02 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -965,12 +965,6 @@
 
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
 
-  NSNumber* credentialProviderExtensionPromoValue =
-      [NSNumber numberWithBool:base::FeatureList::IsEnabled(
-                                   kCredentialProviderExtensionPromo)];
-  NSNumber* credentialProviderExtensionPromoVersion =
-      [NSNumber numberWithInt:kCredentialProviderExtensionPromoFeatureVersion];
-
   NSNumber* passwordManagerBrandingUpdateValue =
       @(base::FeatureList::IsEnabled(kIOSEnablePasswordManagerBrandingUpdate));
   NSNumber* passwordManagerBrandingUpdateVersion =
@@ -990,10 +984,6 @@
   //   }
   // }
   NSDictionary* fieldTrialValues = @{
-    base::SysUTF8ToNSString(kCredentialProviderExtensionPromo.name) : @{
-      kFieldTrialValueKey : credentialProviderExtensionPromoValue,
-      kFieldTrialVersionKey : credentialProviderExtensionPromoVersion,
-    },
     base::SysUTF8ToNSString(kIOSEnablePasswordManagerBrandingUpdate.name) : @{
       kFieldTrialValueKey : passwordManagerBrandingUpdateValue,
       kFieldTrialVersionKey : passwordManagerBrandingUpdateVersion,
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index 985844d..e70f67a 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -412,6 +412,9 @@
       <message name="IDS_IOS_REMOVE_ACCOUNT_CONFIRMATION_MESSAGE" desc="Dialog message to emphasize the effects of removing an account.">
         This account and any unsaved data will be removed from Chromium.
       </message>
+      <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO" desc="Second bullet point under the Safe Browsing enhanced protection mode">
+        Keeps you safe on Chromium and may be used to improve your security in other Google apps when you are signed in.
+      </message>
       <message name="IDS_IOS_SAFE_MODE_NAMED_TWEAKS_FOUND" desc="The message indicating a 3rd party tweak has been found that is known to crash Chromium.  After the ':', we will list the names of the libraries. [Length: 140em]">
         Some add-ons cause Chromium to crash. Please uninstall:
       </message>
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1
new file mode 100644
index 0000000..0b113a52
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1
@@ -0,0 +1 @@
+5684c7ffc921c828c17ed6a69d44b5ffc267ae9d
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index a97edbb..79d18844 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -412,6 +412,9 @@
       <message name="IDS_IOS_REMOVE_ACCOUNT_CONFIRMATION_MESSAGE" desc="Dialog message to emphasize the effects of removing an account.">
       This account and any unsaved data will be removed from Chrome and other Google apps on this device.
       </message>
+      <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO" desc="Second bullet point under the Safe Browsing enhanced protection mode">
+        Keeps you safe on Chrome and may be used to improve your security in other Google apps when you are signed in.
+      </message>
       <message name="IDS_IOS_SAFE_MODE_NAMED_TWEAKS_FOUND" desc="The message indicating a 3rd party tweak has been found that is known to crash Chrome.  After the ':', we will list the names of the libraries. [Length: 140em]">
         Some add-ons cause Chrome to crash. Please uninstall:
       </message>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1
new file mode 100644
index 0000000..9dd6378
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO.png.sha1
@@ -0,0 +1 @@
+6d9bfc75618ffa3e14aa2534e65091f6fb0d1c6d
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index cd4b8e7..37906b6 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -939,6 +939,12 @@
       <message name="IDS_IOS_FIRST_FOLLOW_GOT_IT" desc="The action button to dismiss the first follow surface.">
         Got It
       </message>
+      <message name="IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TEXT" desc="The text of an empty following list.">
+        Visit a site and click Follow in the Chrome menu
+      </message>
+      <message name="IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TITLE" desc="The title of an empty following list.">
+        You'll find your followed sites here
+      </message>
       <message name="IDS_IOS_FOLLOW_MANAGEMENT_TITLE" desc="The title of the following feed management UI.">
         Following
       </message>
@@ -1796,6 +1802,15 @@
       <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_ONE" desc="First bullet point under the Safe Browsing enhanced protection mode">
          Predicts and warns you about dangerous events before they happen.
        </message>
+      <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE" desc="Third bullet point under the Safe Browsing enhanced protection mode">
+        Improves security for you and everyone on the web.
+      </message>
+      <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR" desc="Fourth bullet point under the Safe Browsing enhanced protection mode">
+        Warns you if passwords are exposed in a data breach.
+      </message>
+      <message name="IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE" desc="Fifth bullet point under the Safe Browsing enhanced protection mode">
+        Sends URLs to Safe Browsing to check them. Also sends a small sample of pages, downloads, extension activity, and system information to help discover new threats. Temporarily links this data to your Google Account when you’re signed in, to protect you across Google apps.
+      </message>
       <message name="IDS_IOS_SCANNER_TORCH_BUTTON_ACCESSIBILITY_LABEL" desc="Accessibility label for the button to turn torch on and off in the Scanner. The current state of the button is communicated using the accessibility value. [Length: unlimited] [iOS only]"  meaning="The button to turn on and off the torch. [20em] [Read by Text-to-Speech]">
         Torch
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TEXT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TEXT.png.sha1
new file mode 100644
index 0000000..34733740
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TEXT.png.sha1
@@ -0,0 +1 @@
+d042cdb59c451f9ce45e9a68e90ff45eac60697a
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TITLE.png.sha1
new file mode 100644
index 0000000..34733740
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TITLE.png.sha1
@@ -0,0 +1 @@
+d042cdb59c451f9ce45e9a68e90ff45eac60697a
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE.png.sha1
new file mode 100644
index 0000000..9dd6378
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE.png.sha1
@@ -0,0 +1 @@
+6d9bfc75618ffa3e14aa2534e65091f6fb0d1c6d
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR.png.sha1
new file mode 100644
index 0000000..9dd6378
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR.png.sha1
@@ -0,0 +1 @@
+6d9bfc75618ffa3e14aa2534e65091f6fb0d1c6d
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE.png.sha1
new file mode 100644
index 0000000..9dd6378
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE.png.sha1
@@ -0,0 +1 @@
+6d9bfc75618ffa3e14aa2534e65091f6fb0d1c6d
\ No newline at end of file
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.h b/ios/chrome/browser/discover_feed/discover_feed_service.h
index a943958..cf81d57 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.h
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.h
@@ -66,6 +66,12 @@
   // update all ViewControllers returned by NewFeedViewController.
   virtual void RefreshFeed() = 0;
 
+  // Returns whether the Following feed model has unseen content.
+  virtual BOOL GetFollowingFeedHasUnseenContent();
+
+  // Informs the service that the Following content has been seen.
+  virtual void SetFollowingFeedContentSeen();
+
   // Methods to register or remove observers.
   void AddObserver(DiscoverFeedObserver* observer);
   void RemoveObserver(DiscoverFeedObserver* observer);
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.mm b/ios/chrome/browser/discover_feed/discover_feed_service.mm
index f4e1415..8ac58b0 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.mm
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.mm
@@ -12,6 +12,12 @@
 
 DiscoverFeedService::~DiscoverFeedService() = default;
 
+// TODO(crbug.com/1277974): Remove this when implemented downstream.
+BOOL DiscoverFeedService::GetFollowingFeedHasUnseenContent() {
+  return YES;
+}
+void DiscoverFeedService::SetFollowingFeedContentSeen() {}
+
 void DiscoverFeedService::AddObserver(DiscoverFeedObserver* observer) {
   observer_list_.AddObserver(observer);
 }
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 2c1ab49..b0028aa 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -704,10 +704,6 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(kEnableFREDefaultBrowserScreenTesting,
                                     kFREDefaultPromoTestingVariations,
                                     "EnableFREDefaultBrowserScreenTesting")},
-    {"credential-provider-extension-promo",
-     flag_descriptions::kCredentialProviderExtensionPromoName,
-     flag_descriptions::kCredentialProviderExtensionPromoDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kCredentialProviderExtensionPromo)},
     {"enable-discover-feed-shorter-cache",
      flag_descriptions::kEnableDiscoverFeedShorterCacheName,
      flag_descriptions::kEnableDiscoverFeedShorterCacheDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 9b7ba08a..5d4b831 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -106,13 +106,6 @@
     "When enabled use Crashpad to generate crash reports crash collection. "
     "When disabled use Breakpad. This flag takes two restarts to take effect";
 
-const char kCredentialProviderExtensionPromoName[] =
-    "Enable the credential provider extension promo";
-const char kCredentialProviderExtensionPromoDescription[] =
-    "When enabled, a new item 'Passwords In Other Apps' item will be available "
-    "Chrome passwords settings, containing promotional instructions to enable"
-    "password autofill using Chrome.";
-
 #if defined(DCHECK_IS_CONFIGURABLE)
 const char kDcheckIsFatalName[] = "DCHECKs are fatal";
 const char kDcheckIsFatalDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 0f803c0..90d9bd0 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -92,11 +92,6 @@
 extern const char kCrashpadIOSName[];
 extern const char kCrashpadIOSDescription[];
 
-// Title and description for the flag to trigger credentials provider extension
-// promo.
-extern const char kCredentialProviderExtensionPromoName[];
-extern const char kCredentialProviderExtensionPromoDescription[];
-
 #if defined(DCHECK_IS_CONFIGURABLE)
 // Title and description for the flag to enable configurable DCHECKs.
 extern const char kDcheckIsFatalName[];
diff --git a/ios/chrome/browser/prerender/prerender_egtest.mm b/ios/chrome/browser/prerender/prerender_egtest.mm
index e1e29ab..fe8c610 100644
--- a/ios/chrome/browser/prerender/prerender_egtest.mm
+++ b/ios/chrome/browser/prerender/prerender_egtest.mm
@@ -168,18 +168,20 @@
 
   // Open the suggestion. The suggestion needs to be the first suggestion to
   // have the prerenderer activated.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityLabel(pageString),
-                                   [ChromeEarlGrey isNewOmniboxPopupEnabled]
-                                       ? grey_accessibilityTrait(
-                                             UIAccessibilityTraitStaticText)
-                                       : grey_kindOfClassName(
-                                             @"FadeTruncatingLabel"),
-                                   grey_ancestor(grey_accessibilityID(
-                                       @"omnibox suggestion 0 0")),
-                                   grey_sufficientlyVisible(), nil)]
-      performAction:grey_tap()];
+  id<GREYMatcher> rowMatcher =
+      [ChromeEarlGrey isNewOmniboxPopupEnabled]
+          ? grey_allOf(
+                grey_accessibilityValue(pageString),
+                grey_ancestor(grey_accessibilityID(@"omnibox suggestion 0 0")),
+                chrome_test_util::OmniboxPopupRow(), grey_sufficientlyVisible(),
+                nil)
+          : grey_allOf(grey_descendant(
+                           chrome_test_util::StaticTextWithAccessibilityLabel(
+                               pageString)),
+                       grey_accessibilityID(@"omnibox suggestion 0 0"),
+                       chrome_test_util::OmniboxPopupRow(),
+                       grey_sufficientlyVisible(), nil);
+  [[EarlGrey selectElementWithMatcher:rowMatcher] performAction:grey_tap()];
 
   [ChromeEarlGrey waitForWebStateContainingText:kPageLoadedString];
   GREYAssertEqual(visitCountBeforePrerender + 1, _visitCounter,
diff --git a/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm b/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
index 5bc925c..59cc64d 100644
--- a/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
+++ b/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
@@ -33,6 +33,8 @@
   void UpdateTheme() final {}
   void RefreshFeedIfNeeded() final {}
   void RefreshFeed() final {}
+  BOOL GetFollowingFeedHasUnseenContent() final { return NO; }
+  void SetFollowingFeedContentSeen() final {}
 };
 
 }  // anonymous namespace
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index e18c4b2..ff979db 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -227,6 +227,7 @@
     "content_suggestions_category_wrapper_unittest.mm",
     "content_suggestions_collection_utils_unittest.mm",
     "content_suggestions_header_synchronizer_unittest.mm",
+    "content_suggestions_mediator_unittest.mm",
     "ntp_home_mediator_unittest.mm",
   ]
   deps = [
@@ -235,13 +236,20 @@
     ":content_suggestions_ui_util",
     "//base",
     "//base/test:test_support",
+    "//components/favicon/core",
+    "//components/favicon/core/test:test_support",
     "//components/ntp_snippets",
+    "//components/ntp_tiles",
+    "//components/reading_list/core",
     "//components/signin/public/identity_manager",
+    "//components/sync_preferences:test_support",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ntp_snippets",
+    "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/signin:test_support",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 239ac5c0..d024249c1 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -61,7 +61,7 @@
   CGFloat topMargin = doodleTopMargin(YES, kTopInset, IPadTraitCollection());
 
   // Test.
-  EXPECT_EQ(120, height);
+  EXPECT_EQ(68, height);
   EXPECT_EQ(162, topMargin);
 }
 
@@ -73,9 +73,9 @@
       doodleTopMargin(YES, kTopInset, IPhonePortraitTraitCollection());
 
   // Test.
-  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(68, heightLogo);
   EXPECT_EQ(kDoodleHeightNoLogo, heightNoLogo);
-  EXPECT_EQ(58 + kTopInset, topMargin);
+  EXPECT_EQ(75 + kTopInset, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhoneLandscape) {
@@ -86,9 +86,9 @@
       doodleTopMargin(YES, kTopInset, IPhoneLandscapeTraitCollection());
 
   // Test.
-  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(68, heightLogo);
   EXPECT_EQ(kDoodleHeightNoLogo, heightNoLogo);
-  EXPECT_EQ(kTopInset, topMargin);
+  EXPECT_EQ(78, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPad) {
@@ -103,7 +103,7 @@
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(22, topMargin);
   EXPECT_EQ(432, resultWidth);
   EXPECT_EQ(432, resultWidthLargeIPad);
 }
@@ -118,7 +118,7 @@
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(22, topMargin);
   EXPECT_EQ(343, resultWidth);
 }
 
@@ -132,31 +132,31 @@
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(22, topMargin);
   EXPECT_EQ(343, resultWidth);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPad) {
   // Action, tests.
-  EXPECT_EQ(382,
+  EXPECT_EQ(322,
             heightForLogoHeader(YES, YES, YES, YES, 0, IPadTraitCollection()));
-  EXPECT_EQ(406,
+  EXPECT_EQ(346,
             heightForLogoHeader(YES, YES, NO, YES, 0, IPadTraitCollection()));
-  EXPECT_EQ(382,
+  EXPECT_EQ(322,
             heightForLogoHeader(YES, YES, YES, NO, 0, IPadTraitCollection()));
-  EXPECT_EQ(406,
+  EXPECT_EQ(346,
             heightForLogoHeader(YES, YES, NO, NO, 0, IPadTraitCollection()));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPhone) {
   // Action, tests.
-  EXPECT_EQ(278, heightForLogoHeader(YES, YES, YES, YES, 0,
+  EXPECT_EQ(235, heightForLogoHeader(YES, YES, YES, YES, 0,
                                      IPhonePortraitTraitCollection()));
-  EXPECT_EQ(278, heightForLogoHeader(YES, YES, NO, YES, 0,
+  EXPECT_EQ(235, heightForLogoHeader(YES, YES, NO, YES, 0,
                                      IPhonePortraitTraitCollection()));
-  EXPECT_EQ(278, heightForLogoHeader(YES, YES, YES, NO, 0,
+  EXPECT_EQ(235, heightForLogoHeader(YES, YES, YES, NO, 0,
                                      IPhonePortraitTraitCollection()));
-  EXPECT_EQ(278, heightForLogoHeader(YES, YES, NO, NO, 0,
+  EXPECT_EQ(235, heightForLogoHeader(YES, YES, NO, NO, 0,
                                      IPhonePortraitTraitCollection()));
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
index 1f2cc99..598852e 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -23,15 +23,6 @@
 - (void)hideMostRecentTab;
 // Handles the actions following a tap on the promo.
 - (void)handlePromoTapped;
-// Handles the actions following a tap on the "Manage Activity" item in the
-// Discover feed menu.
-- (void)handleFeedManageActivityTapped;
-// Handles the actions following a tap on the "Manage Interests" item in the
-// Discover feed menu.
-- (void)handleFeedManageInterestsTapped;
-// Handles the actions following a tap on the "Learn More" item in the Discover
-// feed menu.
-- (void)handleFeedLearnMoreTapped;
 
 @end
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 1108fab..a1981d1 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -198,13 +198,18 @@
                     mostVisitedSite:std::move(mostVisitedFactory)
                    readingListModel:readingListModel
                         prefService:prefs
-      isGoogleDefaultSearchProvider:isGoogleDefaultSearchProvider];
-  self.contentSuggestionsMediator.commandHandler = self.ntpMediator;
-  self.contentSuggestionsMediator.headerProvider = self.headerController;
+      isGoogleDefaultSearchProvider:isGoogleDefaultSearchProvider
+                            browser:self.browser];
   self.contentSuggestionsMediator.discoverFeedDelegate =
       self.discoverFeedDelegate;
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
+  self.contentSuggestionsMediator.dispatcher =
+      static_cast<id<ApplicationCommands, BrowserCommands, OmniboxCommands,
+                     SnackbarCommands>>(self.browser->GetCommandDispatcher());
   self.contentSuggestionsMediator.webStateList =
       self.browser->GetWebStateList();
+  self.contentSuggestionsMediator.webState = self.webState;
   [self configureStartSurfaceIfNeeded];
 
   if (!IsContentSuggestionsHeaderMigrationEnabled()) {
@@ -216,14 +221,15 @@
     self.contentSuggestionsViewController =
         [[ContentSuggestionsViewController alloc] init];
     self.contentSuggestionsViewController.suggestionCommandHandler =
-        self.ntpMediator;
+        self.contentSuggestionsMediator;
     self.contentSuggestionsViewController.audience = self;
     self.contentSuggestionsViewController.menuProvider = self;
   } else {
     self.collectionViewController =
         [[ContentSuggestionsCollectionViewController alloc]
             initWithStyle:CollectionViewControllerStyleDefault];
-    self.collectionViewController.suggestionCommandHandler = self.ntpMediator;
+    self.collectionViewController.suggestionCommandHandler =
+        self.contentSuggestionsMediator;
     self.collectionViewController.audience = self;
     self.collectionViewController.contentSuggestionsEnabled =
         self.contentSuggestionsEnabled;
@@ -243,11 +249,6 @@
   if (!IsContentSuggestionsHeaderMigrationEnabled()) {
     self.ntpMediator.consumer = self.headerController;
   }
-  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
-  // clean up.
-  self.ntpMediator.dispatcher =
-      static_cast<id<ApplicationCommands, BrowserCommands, OmniboxCommands,
-                     SnackbarCommands>>(self.browser->GetCommandDispatcher());
   // IsContentSuggestionsUIViewControllerMigrationEnabled() doesn't need to set
   // the suggestionsViewController since it won't be retrieving an item's index
   // from the CollectionView model.
@@ -324,6 +325,13 @@
   return self.collectionViewController;
 }
 
+#pragma mark - Setters
+
+- (void)setWebState:(web::WebState*)webState {
+  _webState = webState;
+  self.contentSuggestionsMediator.webState = webState;
+}
+
 #pragma mark - ContentSuggestionsViewControllerAudience
 
 - (void)promoShown {
@@ -334,6 +342,9 @@
 }
 
 - (void)viewDidDisappear {
+  // Start no longer showing
+  self.contentSuggestionsMediator.showingStartSurface = NO;
+  // Update DiscoverFeedService to NO
   if (ShouldShowReturnToMostRecentTabForStartSurface()) {
     [self.contentSuggestionsMediator hideRecentTabTile];
   }
@@ -444,7 +455,7 @@
                                                     toView:nil];
 
         [menuElements addObject:[actionFactory actionToOpenInNewTabWithBlock:^{
-                        [weakSelf.ntpMediator
+                        [weakSelf.contentSuggestionsMediator
                             openNewTabWithMostVisitedItem:item
                                                 incognito:NO
                                                   atIndex:index
@@ -453,10 +464,11 @@
 
         UIAction* incognitoAction =
             [actionFactory actionToOpenInNewIncognitoTabWithBlock:^{
-              [weakSelf.ntpMediator openNewTabWithMostVisitedItem:item
-                                                        incognito:YES
-                                                          atIndex:index
-                                                        fromPoint:centerPoint];
+              [weakSelf.contentSuggestionsMediator
+                  openNewTabWithMostVisitedItem:item
+                                      incognito:YES
+                                        atIndex:index
+                                      fromPoint:centerPoint];
             }];
 
         if (IsIncognitoModeDisabled(
@@ -485,7 +497,8 @@
                       }]];
 
         [menuElements addObject:[actionFactory actionToRemoveWithBlock:^{
-                        [weakSelf.ntpMediator removeMostVisited:item];
+                        [weakSelf.contentSuggestionsMediator
+                            removeMostVisited:item];
                       }]];
 
         return [UIMenu menuWithTitle:@"" children:menuElements];
@@ -504,6 +517,8 @@
   if (!scene.modifytVisibleNTPForStartSurface)
     return;
 
+  // Update Mediator property to signal the NTP is currently showing Start.
+  self.contentSuggestionsMediator.showingStartSurface = YES;
   if (ShouldShowReturnToMostRecentTabForStartSurface()) {
     base::RecordAction(
         base::UserMetricsAction("IOS.StartSurface.ShowReturnToRecentTabTile"));
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
index 07fd2702..7828c73 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
@@ -10,7 +10,9 @@
 #include <memory>
 
 #include "components/prefs/pref_service.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_gesture_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_consumer.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_consumer.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h"
 
@@ -26,20 +28,23 @@
 class PrefRegistrySyncable;
 }  // namespace user_prefs
 
-@protocol ContentSuggestionsCommands;
+@protocol ApplicationCommands;
+class Browser;
+@protocol BrowserCommands;
 @protocol ContentSuggestionsCollectionConsumer;
-@protocol ContentSuggestionsGestureCommands;
-@protocol ContentSuggestionsHeaderProvider;
 @protocol DiscoverFeedDelegate;
 class GURL;
 class LargeIconCache;
 class NotificationPromoWhatsNew;
 class ReadingListModel;
+@protocol SnackbarCommands;
 class WebStateList;
 
 // Mediator for ContentSuggestions.
 @interface ContentSuggestionsMediator
-    : NSObject <StartSurfaceRecentTabObserving>
+    : NSObject <ContentSuggestionsCommands,
+                ContentSuggestionsGestureCommands,
+                StartSurfaceRecentTabObserving>
 
 // Default initializer.
 - (instancetype)
@@ -50,20 +55,23 @@
                  readingListModel:(ReadingListModel*)readingListModel
                       prefService:(PrefService*)prefService
     isGoogleDefaultSearchProvider:(BOOL)isGoogleDefaultSearchProvider
-    NS_DESIGNATED_INITIALIZER;
+                          browser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
 
 // Registers the feature preferences.
 + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry;
 
+// Dispatcher.
+@property(nonatomic, weak)
+    id<ApplicationCommands, BrowserCommands, SnackbarCommands>
+        dispatcher;
+
 // Command handler for the mediator.
 @property(nonatomic, weak)
     id<ContentSuggestionsCommands, ContentSuggestionsGestureCommands>
         commandHandler;
 
-@property(nonatomic, weak) id<ContentSuggestionsHeaderProvider> headerProvider;
-
 // Delegate used to communicate to communicate events to the DiscoverFeed.
 @property(nonatomic, weak) id<DiscoverFeedDelegate> discoverFeedDelegate;
 
@@ -72,9 +80,15 @@
     collectionConsumer;
 @property(nonatomic, weak) id<ContentSuggestionsConsumer> consumer;
 
+// YES if the Start Surface is being shown.
+@property(nonatomic, assign) BOOL showingStartSurface;
+
 // WebStateList associated with this mediator.
 @property(nonatomic, assign) WebStateList* webStateList;
 
+// The web state associated with this NTP.
+@property(nonatomic, assign) web::WebState* webState;
+
 // Disconnects the mediator.
 - (void)disconnect;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index b5efcd25..d3c102d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -4,8 +4,12 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
 
+#import <MaterialComponents/MaterialSnackbar.h>
+
 #include "base/bind.h"
 #include "base/mac/foundation_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/favicon/ios/web_favicon_driver.h"
 #include "components/ntp_snippets/category.h"
@@ -16,10 +20,16 @@
 #import "components/pref_registry/pref_registry_syncable.h"
 #include "components/reading_list/core/reading_list_model.h"
 #import "components/reading_list/ios/reading_list_model_bridge_observer.h"
+#include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
 #include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h"
 #import "ios/chrome/browser/policy/policy_util.h"
 #import "ios/chrome/browser/pref_names.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_parent_item.h"
@@ -28,19 +38,23 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_category_wrapper.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_provider.h"
 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h"
 #import "ios/chrome/browser/ui/content_suggestions/mediator_util.h"
+#import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
+#import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_delegate.h"
+#import "ios/chrome/browser/ui/ntp/metrics.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h"
 #include "ios/chrome/browser/ui/ntp/ntp_tile_saver.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -134,6 +148,10 @@
 // Whether the user already tapped on the NTP promo and therefore should be
 // hidden.
 @property(nonatomic, assign) BOOL shouldHidePromoAfterTap;
+// Recorder for the metrics related to the NTP.
+@property(nonatomic, strong) NTPHomeMetrics* NTPMetrics;
+// Browser reference.
+@property(nonatomic, assign) Browser* browser;
 
 @end
 
@@ -148,7 +166,8 @@
                                       mostVisitedSites
                  readingListModel:(ReadingListModel*)readingListModel
                       prefService:(PrefService*)prefService
-    isGoogleDefaultSearchProvider:(BOOL)isGoogleDefaultSearchProvider {
+    isGoogleDefaultSearchProvider:(BOOL)isGoogleDefaultSearchProvider
+                          browser:(Browser*)browser {
   self = [super init];
   if (self) {
     _incognitoAvailable = !IsIncognitoModeDisabled(prefService);
@@ -183,6 +202,9 @@
 
     _readingListModelBridge =
         std::make_unique<ReadingListModelBridge>(self, readingListModel);
+    _browser = browser;
+    _NTPMetrics = [[NTPHomeMetrics alloc]
+        initWithBrowserState:_browser->GetBrowserState()];
   }
   return self;
 }
@@ -262,6 +284,11 @@
   [self reloadAllData];
 }
 
+- (void)setWebState:(web::WebState*)webState {
+  _webState = webState;
+  self.NTPMetrics.webState = self.webState;
+}
+
 + (NSUInteger)maxSitesShown {
   return kMaxNumMostVisitedTiles;
 }
@@ -338,6 +365,155 @@
   }
 }
 
+#pragma mark - ContentSuggestionsCommands
+
+- (void)openReadingList {
+  [self.dispatcher showReadingList];
+}
+
+- (void)openMostVisitedItem:(CollectionViewItem*)item
+                    atIndex:(NSInteger)mostVisitedIndex {
+  NewTabPageTabHelper* NTPHelper =
+      NewTabPageTabHelper::FromWebState(self.webState);
+  if (NTPHelper && NTPHelper->IgnoreLoadRequests())
+    return;
+
+  if ([item isKindOfClass:[ContentSuggestionsMostVisitedActionItem class]]) {
+    ContentSuggestionsMostVisitedActionItem* mostVisitedItem =
+        base::mac::ObjCCastStrict<ContentSuggestionsMostVisitedActionItem>(
+            item);
+    switch (mostVisitedItem.collectionShortcutType) {
+      case NTPCollectionShortcutTypeBookmark:
+        base::RecordAction(base::UserMetricsAction("MobileNTPShowBookmarks"));
+        LogLikelyInterestedDefaultBrowserUserActivity(DefaultPromoTypeAllTabs);
+        [self.dispatcher showBookmarksManager];
+        break;
+      case NTPCollectionShortcutTypeReadingList:
+        base::RecordAction(base::UserMetricsAction("MobileNTPShowReadingList"));
+        [self.dispatcher showReadingList];
+        break;
+      case NTPCollectionShortcutTypeRecentTabs:
+        base::RecordAction(base::UserMetricsAction("MobileNTPShowRecentTabs"));
+        [self.dispatcher showRecentTabs];
+        break;
+      case NTPCollectionShortcutTypeHistory:
+        base::RecordAction(base::UserMetricsAction("MobileNTPShowHistory"));
+        [self.dispatcher showHistory];
+        break;
+      case NTPCollectionShortcutTypeCount:
+        NOTREACHED();
+        break;
+    }
+    return;
+  }
+
+  ContentSuggestionsMostVisitedItem* mostVisitedItem =
+      base::mac::ObjCCastStrict<ContentSuggestionsMostVisitedItem>(item);
+
+  [self logMostVisitedOpening:mostVisitedItem atIndex:mostVisitedIndex];
+
+  UrlLoadParams params = UrlLoadParams::InCurrentTab(mostVisitedItem.URL);
+  params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
+  UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
+}
+
+// TODO(crbug.com/761096) : Promo handling should be tested.
+- (void)handlePromoTapped {
+  NotificationPromoWhatsNew* notificationPromo = _notificationPromo.get();
+  DCHECK(notificationPromo);
+  notificationPromo->HandleClosed();
+  [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_PROMO];
+  if (IsSingleCellContentSuggestionsEnabled()) {
+    [self hidePromo];
+  }
+
+  if (notificationPromo->IsURLPromo()) {
+    UrlLoadParams params = UrlLoadParams::InNewTab(notificationPromo->url());
+    params.append_to = kCurrentTab;
+    UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
+    return;
+  }
+
+  if (notificationPromo->IsChromeCommandPromo()) {
+    // "What's New" promo that runs a command can be added here by calling
+    // self.dispatcher.
+    if (notificationPromo->command() == kSetDefaultBrowserCommand) {
+      base::RecordAction(
+          base::UserMetricsAction("IOS.DefaultBrowserNTPPromoTapped"));
+      [[UIApplication sharedApplication]
+                    openURL:
+                        [NSURL URLWithString:UIApplicationOpenSettingsURLString]
+                    options:{}
+          completionHandler:nil];
+      return;
+    }
+
+    DCHECK_EQ(kTestWhatsNewCommand, notificationPromo->command())
+        << "Promo command is not valid.";
+    return;
+  }
+  NOTREACHED() << "Promo type is neither URL or command.";
+}
+
+- (void)openMostRecentTab {
+  base::RecordAction(
+      base::UserMetricsAction("IOS.StartSurface.OpenMostRecentTab"));
+  [self hideRecentTabTile];
+  WebStateList* web_state_list = self.browser->GetWebStateList();
+  web::WebState* web_state =
+      StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser)
+          ->most_recent_tab();
+  DCHECK(web_state);
+  int index = web_state_list->GetIndexOfWebState(web_state);
+  web_state_list->ActivateWebStateAt(index);
+}
+
+- (void)hideMostRecentTab {
+  [self hideRecentTabTile];
+}
+
+#pragma mark - ContentSuggestionsGestureCommands
+
+- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
+                            incognito:(BOOL)incognito
+                              atIndex:(NSInteger)index
+                            fromPoint:(CGPoint)point {
+  if (incognito &&
+      IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) {
+    // This should only happen when the policy changes while the option is
+    // presented.
+    return;
+  }
+  [self logMostVisitedOpening:item atIndex:index];
+  [self openNewTabWithURL:item.URL incognito:incognito originPoint:point];
+}
+
+- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
+                            incognito:(BOOL)incognito
+                              atIndex:(NSInteger)index {
+  if (incognito &&
+      IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) {
+    // This should only happen when the policy changes while the option is
+    // presented.
+    return;
+  }
+  [self logMostVisitedOpening:item atIndex:index];
+  [self openNewTabWithURL:item.URL incognito:incognito originPoint:CGPointZero];
+}
+
+- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
+                            incognito:(BOOL)incognito {
+  [self openNewTabWithMostVisitedItem:item
+                            incognito:incognito
+                              atIndex:item.index];
+}
+
+- (void)removeMostVisited:(ContentSuggestionsMostVisitedItem*)item {
+  base::RecordAction(base::UserMetricsAction("MostVisited_UrlBlacklisted"));
+  [self blockMostVisitedURL:item.URL];
+  [self showMostVisitedUndoForURL:item.URL];
+}
+
 #pragma mark - StartSurfaceRecentTabObserving
 
 - (void)mostRecentTabWasRemoved:(web::WebState*)web_state {
@@ -376,7 +552,7 @@
   for (const ntp_tiles::NTPTile& tile : mostVisited) {
     ContentSuggestionsMostVisitedItem* item =
         ConvertNTPTile(tile, self.mostVisitedSectionInfo);
-    item.commandHandler = self.commandHandler;
+    item.commandHandler = self;
     item.incognitoAvailable = self.incognitoAvailable;
     item.index = index;
     DCHECK(index < kShortcutMinimumIndex);
@@ -523,6 +699,56 @@
   return convertedSuggestions;
 }
 
+// Opens the |URL| in a new tab |incognito| or not. |originPoint| is the origin
+// of the new tab animation if the tab is opened in background, in window
+// coordinates.
+- (void)openNewTabWithURL:(const GURL&)URL
+                incognito:(BOOL)incognito
+              originPoint:(CGPoint)originPoint {
+  // Open the tab in background if it is non-incognito only.
+  UrlLoadParams params = UrlLoadParams::InNewTab(URL);
+  params.SetInBackground(!incognito);
+  params.in_incognito = incognito;
+  params.append_to = kCurrentTab;
+  params.origin_point = originPoint;
+  UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
+}
+
+// Logs a histogram due to a Most Visited item being opened.
+- (void)logMostVisitedOpening:(ContentSuggestionsMostVisitedItem*)item
+                      atIndex:(NSInteger)mostVisitedIndex {
+  [self.NTPMetrics
+      recordAction:new_tab_page_uma::ACTION_OPENED_MOST_VISITED_ENTRY];
+  base::RecordAction(base::UserMetricsAction("MobileNTPMostVisited"));
+  RecordNTPTileClick(mostVisitedIndex, item.source, item.titleSource,
+                     item.attributes, GURL());
+}
+
+// Shows a snackbar with an action to undo the removal of the most visited item
+// with a |URL|.
+- (void)showMostVisitedUndoForURL:(GURL)URL {
+  GURL copiedURL = URL;
+
+  MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init];
+  __weak ContentSuggestionsMediator* weakSelf = self;
+  action.handler = ^{
+    ContentSuggestionsMediator* strongSelf = weakSelf;
+    if (!strongSelf)
+      return;
+    [strongSelf allowMostVisitedURL:copiedURL];
+  };
+  action.title = l10n_util::GetNSString(IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE);
+  action.accessibilityIdentifier = @"Undo";
+
+  TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess);
+  MDCSnackbarMessage* message = [MDCSnackbarMessage
+      messageWithText:l10n_util::GetNSString(
+                          IDS_IOS_NEW_TAB_MOST_VISITED_ITEM_REMOVED)];
+  message.action = action;
+  message.category = @"MostVisitedUndo";
+  [self.dispatcher showSnackbarMessage:message];
+}
+
 #pragma mark - Properties
 
 - (NSArray<ContentSuggestionsMostVisitedActionItem*>*)actionButtonItems {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator_unittest.mm
new file mode 100644
index 0000000..4a1a4e8
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator_unittest.mm
@@ -0,0 +1,153 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
+
+#include "base/time/default_clock.h"
+#include "components/favicon/core/large_icon_service_impl.h"
+#include "components/favicon/core/test/mock_favicon_service.h"
+#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+#include "components/reading_list/core/reading_list_model_impl.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h"
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
+#import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
+#import "ios/chrome/browser/url_loading/fake_url_loading_browser_agent.h"
+#import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h"
+#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#include "ios/web/public/test/web_task_environment.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+std::unique_ptr<KeyedService> BuildReadingListModel(
+    web::BrowserState* context) {
+  ChromeBrowserState* browser_state =
+      ChromeBrowserState::FromBrowserState(context);
+  std::unique_ptr<ReadingListModelImpl> reading_list_model(
+      new ReadingListModelImpl(nullptr, browser_state->GetPrefs(),
+                               base::DefaultClock::GetInstance()));
+  return reading_list_model;
+}
+
+}  // namespace
+
+@protocol
+    ContentSuggestionsMediatorDispatcher <BrowserCommands, SnackbarCommands>
+@end
+
+// Testing Suite for ContentSuggestionsMediator
+class ContentSuggestionsMediatorTest : public PlatformTest {
+ public:
+  ContentSuggestionsMediatorTest() {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    test_cbs_builder.AddTestingFactory(
+        IOSChromeLargeIconServiceFactory::GetInstance(),
+        IOSChromeLargeIconServiceFactory::GetDefaultFactory());
+    chrome_browser_state_ = test_cbs_builder.Build();
+    large_icon_service_.reset(new favicon::LargeIconServiceImpl(
+        &mock_favicon_service_, nullptr, 32, favicon_base::IconType::kTouchIcon,
+        "test_chrome"));
+    browser_ = std::make_unique<TestBrowser>(chrome_browser_state_.get());
+    fake_web_state_ = std::make_unique<web::FakeWebState>();
+    InitializeReadingListModel();
+    dispatcher_ =
+        OCMProtocolMock(@protocol(ContentSuggestionsMediatorDispatcher));
+
+    favicon::LargeIconService* largeIconService =
+        IOSChromeLargeIconServiceFactory::GetForBrowserState(
+            chrome_browser_state_.get());
+    LargeIconCache* cache = IOSChromeLargeIconCacheFactory::GetForBrowserState(
+        chrome_browser_state_.get());
+    std::unique_ptr<ntp_tiles::MostVisitedSites> mostVisitedSites =
+        std::make_unique<ntp_tiles::MostVisitedSites>(
+            &pref_service_, /*top_sites*/ nullptr, /*popular_sites*/ nullptr,
+            /*custom_links*/ nullptr, /*icon_cacher*/ nullptr,
+            /*supervisor=*/nullptr, true);
+    ntp_tiles::MostVisitedSites::RegisterProfilePrefs(pref_service_.registry());
+    ReadingListModel* readingListModel =
+        ReadingListModelFactory::GetForBrowserState(
+            chrome_browser_state_.get());
+    mediator_ = [[ContentSuggestionsMediator alloc]
+             initWithLargeIconService:largeIconService
+                       largeIconCache:cache
+                      mostVisitedSite:std::move(mostVisitedSites)
+                     readingListModel:readingListModel
+                          prefService:chrome_browser_state_.get()->GetPrefs()
+        isGoogleDefaultSearchProvider:NO
+                              browser:browser_.get()];
+    mediator_.dispatcher = dispatcher_;
+    mediator_.webStateList = browser_.get()->GetWebStateList();
+    mediator_.webState = fake_web_state_.get();
+
+    UrlLoadingNotifierBrowserAgent::CreateForBrowser(browser_.get());
+    FakeUrlLoadingBrowserAgent::InjectForBrowser(browser_.get());
+    url_loader_ = FakeUrlLoadingBrowserAgent::FromUrlLoadingBrowserAgent(
+        UrlLoadingBrowserAgent::FromBrowser(browser_.get()));
+  }
+
+ protected:
+  // Initialize reading list model and its required tab helpers.
+  void InitializeReadingListModel() {
+    fake_web_state_->SetBrowserState(chrome_browser_state_.get());
+    ReadingListModelFactory::GetInstance()->SetTestingFactoryAndUse(
+        chrome_browser_state_.get(),
+        base::BindRepeating(&BuildReadingListModel));
+  }
+
+  web::WebTaskEnvironment task_environment_;
+  sync_preferences::TestingPrefServiceSyncable pref_service_;
+  IOSChromeScopedTestingLocalState local_state_;
+  testing::StrictMock<favicon::MockFaviconService> mock_favicon_service_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<Browser> browser_;
+  id dispatcher_;
+  std::unique_ptr<web::FakeWebState> fake_web_state_;
+  std::unique_ptr<favicon::LargeIconServiceImpl> large_icon_service_;
+  ContentSuggestionsMediator* mediator_;
+  FakeUrlLoadingBrowserAgent* url_loader_;
+};
+
+// Tests that the command is sent to the dispatcher when opening the Reading
+// List.
+TEST_F(ContentSuggestionsMediatorTest, TestOpenReadingList) {
+  OCMExpect([dispatcher_ showReadingList]);
+
+  // Action.
+  [mediator_ openReadingList];
+
+  // Test.
+  EXPECT_OCMOCK_VERIFY(dispatcher_);
+}
+
+// Tests that the command is sent to the loader when opening a most visited.
+TEST_F(ContentSuggestionsMediatorTest, TestOpenMostVisited) {
+  GURL url = GURL("http://chromium.org");
+  ContentSuggestionsMostVisitedItem* item =
+      [[ContentSuggestionsMostVisitedItem alloc] initWithType:0];
+  item.URL = url;
+
+  // Action.
+  [mediator_ openMostVisitedItem:item atIndex:0];
+
+  // Test.
+  EXPECT_EQ(url, url_loader_->last_params.web_params.url);
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      ui::PAGE_TRANSITION_AUTO_BOOKMARK,
+      url_loader_->last_params.web_params.transition_type));
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index e788283f..657a73b 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -249,17 +249,20 @@
       performAction:grey_typeText(URL)];
 
   // The first suggestion is a search, the second suggestion is the URL.
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(
-              [ChromeEarlGrey isNewOmniboxPopupEnabled]
-                  ? grey_descendant(
-                        grey_accessibilityID(@"omnibox suggestion 0 1"))
-                  : grey_accessibilityID(@"omnibox suggestion 0 1"),
-              chrome_test_util::OmniboxPopupRow(),
-              grey_descendant(
-                  chrome_test_util::StaticTextWithAccessibilityLabel(URL)),
-              grey_sufficientlyVisible(), nil)] performAction:grey_tap()];
+  id<GREYMatcher> rowMatcher =
+      [ChromeEarlGrey isNewOmniboxPopupEnabled]
+          ? grey_allOf(
+                grey_ancestor(grey_accessibilityID(@"omnibox suggestion 0 1")),
+                chrome_test_util::OmniboxPopupRow(),
+                grey_accessibilityValue(URL), grey_sufficientlyVisible(), nil)
+          : grey_allOf(
+                grey_accessibilityID(@"omnibox suggestion 0 1"),
+                chrome_test_util::OmniboxPopupRow(),
+                grey_descendant(
+                    chrome_test_util::StaticTextWithAccessibilityLabel(URL)),
+                grey_sufficientlyVisible(), nil);
+
+  [[EarlGrey selectElementWithMatcher:rowMatcher] performAction:grey_tap()];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       assertWithMatcher:grey_sufficientlyVisible()];
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
index 42daa347..d7a7e02 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
@@ -7,8 +7,6 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_gesture_commands.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller_delegate.h"
 
 namespace signin {
@@ -19,10 +17,8 @@
 class WebState;
 }
 
-@protocol ApplicationCommands;
 class AuthenticationService;
 class Browser;
-@protocol BrowserCommands;
 class ChromeAccountManagerService;
 @protocol ContentSuggestionsCollectionControlling;
 @class ContentSuggestionsHeaderSynchronizer;
@@ -33,18 +29,14 @@
 @protocol NTPHomeConsumer;
 @class NTPHomeMetrics;
 @class FeedMetricsRecorder;
-@protocol OmniboxCommands;
 class TemplateURLService;
-@protocol SnackbarCommands;
 class UrlLoadingBrowserAgent;
 class VoiceSearchAvailability;
 
 // Mediator for the NTP Home panel, handling the interactions with the
 // suggestions.
 @interface NTPHomeMediator
-    : NSObject<ContentSuggestionsCommands,
-               ContentSuggestionsGestureCommands,
-               ContentSuggestionsHeaderViewControllerDelegate>
+    : NSObject <ContentSuggestionsHeaderViewControllerDelegate>
 
 - (instancetype)
            initWithWebState:(web::WebState*)webState
@@ -59,10 +51,6 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Dispatcher.
-@property(nonatomic, weak)
-    id<ApplicationCommands, BrowserCommands, OmniboxCommands, SnackbarCommands>
-        dispatcher;
 // Recorder for the metrics related to the NTP.
 @property(nonatomic, strong) NTPHomeMetrics* NTPMetrics;
 // Recorder for the metrics related to the feed.
@@ -105,6 +93,18 @@
 // before navigating away.
 - (void)saveContentOffsetForWebState:(web::WebState*)webState;
 
+// Handles the actions following a tap on the "Manage Activity" item in the
+// Discover feed menu.
+- (void)handleFeedManageActivityTapped;
+
+// Handles the actions following a tap on the "Manage Interests" item in the
+// Discover feed menu.
+- (void)handleFeedManageInterestsTapped;
+
+// Handles the actions following a tap on the "Learn More" item in the Discover
+// feed menu.
+- (void)handleFeedLearnMoreTapped;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NTP_HOME_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
index 7e8a536..d744176 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -6,8 +6,6 @@
 
 #include <memory>
 
-#import <MaterialComponents/MaterialSnackbar.h>
-
 #include "base/mac/foundation_util.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
@@ -26,14 +24,8 @@
 #import "ios/chrome/browser/signin/authentication_service.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service_observer_bridge.h"
-#import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/omnibox_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
-#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
-#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h"
-#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_view_controller.h"
@@ -42,16 +34,13 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_consumer.h"
-#import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
 #import "ios/chrome/browser/ui/content_suggestions/user_account_image_update_delegate.h"
-#import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/ntp/logo_vendor.h"
 #include "ios/chrome/browser/ui/ntp/metrics.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
-#import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
@@ -247,6 +236,31 @@
   item->SetPageDisplayState(displayState);
 }
 
+// Opens web page for a menu item in the NTP.
+- (void)openMenuItemWebPage:(GURL)URL {
+  NewTabPageTabHelper* NTPHelper =
+      NewTabPageTabHelper::FromWebState(self.webState);
+  if (NTPHelper && NTPHelper->IgnoreLoadRequests())
+    return;
+  _URLLoader->Load(UrlLoadParams::InCurrentTab(URL));
+  // TODO(crbug.com/1085419): Add metrics.
+}
+
+- (void)handleFeedManageActivityTapped {
+  [self openMenuItemWebPage:GURL(kFeedManageActivityURL)];
+  [self.feedMetricsRecorder recordHeaderMenuManageActivityTapped];
+}
+
+- (void)handleFeedManageInterestsTapped {
+  [self openMenuItemWebPage:GURL(kFeedManageInterestsURL)];
+  [self.feedMetricsRecorder recordHeaderMenuManageInterestsTapped];
+}
+
+- (void)handleFeedLearnMoreTapped {
+  [self openMenuItemWebPage:GURL(kFeedLearnMoreURL)];
+  [self.feedMetricsRecorder recordHeaderMenuLearnMoreTapped];
+}
+
 #pragma mark - Properties.
 
 - (void)setWebState:(web::WebState*)webState {
@@ -254,7 +268,6 @@
     _webState->RemoveObserver(_webStateObserver.get());
   }
   _webState = webState;
-  self.NTPMetrics.webState = webState;
   if (IsSingleNtpEnabled()) {
     [self.logoVendor setWebState:webState];
   }
@@ -283,189 +296,6 @@
   self.webState = nullptr;
 }
 
-#pragma mark - ContentSuggestionsCommands
-
-- (void)openReadingList {
-  [self.dispatcher showReadingList];
-}
-
-- (void)openMostVisitedItem:(CollectionViewItem*)item
-                    atIndex:(NSInteger)mostVisitedIndex {
-  NewTabPageTabHelper* NTPHelper =
-      NewTabPageTabHelper::FromWebState(self.webState);
-  if (NTPHelper && NTPHelper->IgnoreLoadRequests())
-    return;
-
-  if ([item isKindOfClass:[ContentSuggestionsMostVisitedActionItem class]]) {
-    ContentSuggestionsMostVisitedActionItem* mostVisitedItem =
-        base::mac::ObjCCastStrict<ContentSuggestionsMostVisitedActionItem>(
-            item);
-    switch (mostVisitedItem.collectionShortcutType) {
-      case NTPCollectionShortcutTypeBookmark:
-        base::RecordAction(base::UserMetricsAction("MobileNTPShowBookmarks"));
-        LogLikelyInterestedDefaultBrowserUserActivity(DefaultPromoTypeAllTabs);
-        [self.dispatcher showBookmarksManager];
-        break;
-      case NTPCollectionShortcutTypeReadingList:
-        base::RecordAction(base::UserMetricsAction("MobileNTPShowReadingList"));
-        [self.dispatcher showReadingList];
-        break;
-      case NTPCollectionShortcutTypeRecentTabs:
-        base::RecordAction(base::UserMetricsAction("MobileNTPShowRecentTabs"));
-        [self.dispatcher showRecentTabs];
-        break;
-      case NTPCollectionShortcutTypeHistory:
-        base::RecordAction(base::UserMetricsAction("MobileNTPShowHistory"));
-        [self.dispatcher showHistory];
-        break;
-      case NTPCollectionShortcutTypeCount:
-        NOTREACHED();
-        break;
-    }
-    return;
-  }
-
-  ContentSuggestionsMostVisitedItem* mostVisitedItem =
-      base::mac::ObjCCastStrict<ContentSuggestionsMostVisitedItem>(item);
-
-  [self logMostVisitedOpening:mostVisitedItem atIndex:mostVisitedIndex];
-
-  UrlLoadParams params = UrlLoadParams::InCurrentTab(mostVisitedItem.URL);
-  params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  _URLLoader->Load(params);
-}
-
-// TODO(crbug.com/761096) : Promo handling should be tested.
-- (void)handlePromoTapped {
-  NotificationPromoWhatsNew* notificationPromo =
-      [self.suggestionsMediator notificationPromo];
-  DCHECK(notificationPromo);
-  notificationPromo->HandleClosed();
-  [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_PROMO];
-  if (IsSingleCellContentSuggestionsEnabled()) {
-    [self.suggestionsMediator hidePromo];
-  }
-
-  if (notificationPromo->IsURLPromo()) {
-    UrlLoadParams params = UrlLoadParams::InNewTab(notificationPromo->url());
-    params.append_to = kCurrentTab;
-    _URLLoader->Load(params);
-    return;
-  }
-
-  if (notificationPromo->IsChromeCommandPromo()) {
-    // "What's New" promo that runs a command can be added here by calling
-    // self.dispatcher.
-    if (notificationPromo->command() == kSetDefaultBrowserCommand) {
-      base::RecordAction(
-          base::UserMetricsAction("IOS.DefaultBrowserNTPPromoTapped"));
-      [[UIApplication sharedApplication]
-                    openURL:
-                        [NSURL URLWithString:UIApplicationOpenSettingsURLString]
-                    options:{}
-          completionHandler:nil];
-      return;
-    }
-
-    DCHECK_EQ(kTestWhatsNewCommand, notificationPromo->command())
-        << "Promo command is not valid.";
-    return;
-  }
-  NOTREACHED() << "Promo type is neither URL or command.";
-}
-
-// Opens web page for a menu item in the NTP.
-- (void)openMenuItemWebPage:(GURL)URL {
-  NewTabPageTabHelper* NTPHelper =
-      NewTabPageTabHelper::FromWebState(self.webState);
-  if (NTPHelper && NTPHelper->IgnoreLoadRequests())
-    return;
-  _URLLoader->Load(UrlLoadParams::InCurrentTab(URL));
-  // TODO(crbug.com/1085419): Add metrics.
-}
-
-- (void)handleFeedManageActivityTapped {
-  [self openMenuItemWebPage:GURL(kFeedManageActivityURL)];
-  [self.feedMetricsRecorder recordHeaderMenuManageActivityTapped];
-}
-
-- (void)handleFeedManageInterestsTapped {
-  [self openMenuItemWebPage:GURL(kFeedManageInterestsURL)];
-  [self.feedMetricsRecorder recordHeaderMenuManageInterestsTapped];
-}
-
-- (void)handleFeedLearnMoreTapped {
-  [self openMenuItemWebPage:GURL(kFeedLearnMoreURL)];
-  [self.feedMetricsRecorder recordHeaderMenuLearnMoreTapped];
-}
-
-- (void)openMostRecentTab {
-  base::RecordAction(
-      base::UserMetricsAction("IOS.StartSurface.OpenMostRecentTab"));
-  [self.suggestionsMediator hideRecentTabTile];
-  WebStateList* web_state_list = self.browser->GetWebStateList();
-  web::WebState* web_state =
-      StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser)
-          ->most_recent_tab();
-  DCHECK(web_state);
-  int index = web_state_list->GetIndexOfWebState(web_state);
-  web_state_list->ActivateWebStateAt(index);
-}
-
-- (void)hideMostRecentTab {
-  [self.suggestionsMediator hideRecentTabTile];
-}
-
-#pragma mark - ContentSuggestionsGestureCommands
-
-- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
-                            incognito:(BOOL)incognito
-                              atIndex:(NSInteger)index
-                            fromPoint:(CGPoint)point {
-  if (incognito &&
-      IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) {
-    // This should only happen when the policy changes while the option is
-    // presented.
-    return;
-  }
-  [self logMostVisitedOpening:item atIndex:index];
-  [self openNewTabWithURL:item.URL incognito:incognito originPoint:point];
-}
-
-- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
-                            incognito:(BOOL)incognito
-                              atIndex:(NSInteger)index {
-  if (incognito &&
-      IsIncognitoModeDisabled(self.browser->GetBrowserState()->GetPrefs())) {
-    // This should only happen when the policy changes while the option is
-    // presented.
-    return;
-  }
-  [self logMostVisitedOpening:item atIndex:index];
-  // This is called in response to accessibility custom actions which don't
-  // need to animate the new tab from the Most Visited Tile.
-  CGPoint cellCenter = IsContentSuggestionsUIViewControllerMigrationEnabled()
-                           ? CGPointZero
-                           : [self cellCenterForItem:item];
-  [self openNewTabWithURL:item.URL incognito:incognito originPoint:cellCenter];
-}
-
-- (void)openNewTabWithMostVisitedItem:(ContentSuggestionsMostVisitedItem*)item
-                            incognito:(BOOL)incognito {
-  NSInteger index = IsContentSuggestionsUIViewControllerMigrationEnabled()
-                        ? item.index
-                        : [self.suggestionsViewController.collectionViewModel
-                              indexPathForItem:item]
-                              .item;
-  [self openNewTabWithMostVisitedItem:item incognito:incognito atIndex:index];
-}
-
-- (void)removeMostVisited:(ContentSuggestionsMostVisitedItem*)item {
-  base::RecordAction(base::UserMetricsAction("MostVisited_UrlBlacklisted"));
-  [self.suggestionsMediator blockMostVisitedURL:item.URL];
-  [self showMostVisitedUndoForURL:item.URL];
-}
-
 #pragma mark - ChromeAccountManagerServiceObserver
 
 - (void)identityChanged:(ChromeIdentity*)identity {
@@ -529,70 +359,6 @@
 
 #pragma mark - Private
 
-// Returns the center of the cell associated with |item| in the window
-// coordinates. Returns CGPointZero if the cell isn't visible.
-- (CGPoint)cellCenterForItem:(ContentSuggestionsMostVisitedItem*)item {
-  NSIndexPath* indexPath = [self.suggestionsViewController.collectionViewModel
-      indexPathForItem:item];
-  if (!indexPath)
-    return CGPointZero;
-
-  UIView* cell = [self.suggestionsViewController.collectionView
-      cellForItemAtIndexPath:indexPath];
-  return [cell.superview convertPoint:cell.center toView:nil];
-}
-
-// Opens the |URL| in a new tab |incognito| or not. |originPoint| is the origin
-// of the new tab animation if the tab is opened in background, in window
-// coordinates.
-- (void)openNewTabWithURL:(const GURL&)URL
-                incognito:(BOOL)incognito
-              originPoint:(CGPoint)originPoint {
-  // Open the tab in background if it is non-incognito only.
-  UrlLoadParams params = UrlLoadParams::InNewTab(URL);
-  params.SetInBackground(!incognito);
-  params.in_incognito = incognito;
-  params.append_to = kCurrentTab;
-  params.origin_point = originPoint;
-  _URLLoader->Load(params);
-}
-
-// Logs a histogram due to a Most Visited item being opened.
-- (void)logMostVisitedOpening:(ContentSuggestionsMostVisitedItem*)item
-                      atIndex:(NSInteger)mostVisitedIndex {
-  [self.NTPMetrics
-      recordAction:new_tab_page_uma::ACTION_OPENED_MOST_VISITED_ENTRY];
-  base::RecordAction(base::UserMetricsAction("MobileNTPMostVisited"));
-
-  RecordNTPTileClick(mostVisitedIndex, item.source, item.titleSource,
-                     item.attributes, GURL());
-}
-
-// Shows a snackbar with an action to undo the removal of the most visited item
-// with a |URL|.
-- (void)showMostVisitedUndoForURL:(GURL)URL {
-  GURL copiedURL = URL;
-
-  MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init];
-  __weak NTPHomeMediator* weakSelf = self;
-  action.handler = ^{
-    NTPHomeMediator* strongSelf = weakSelf;
-    if (!strongSelf)
-      return;
-    [strongSelf.suggestionsMediator allowMostVisitedURL:copiedURL];
-  };
-  action.title = l10n_util::GetNSString(IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE);
-  action.accessibilityIdentifier = @"Undo";
-
-  TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess);
-  MDCSnackbarMessage* message = [MDCSnackbarMessage
-      messageWithText:l10n_util::GetNSString(
-                          IDS_IOS_NEW_TAB_MOST_VISITED_ITEM_REMOVED)];
-  message.action = action;
-  message.category = @"MostVisitedUndo";
-  [self.dispatcher showSnackbarMessage:message];
-}
-
 // Set the NTP scroll offset for the current navigation item.
 - (void)setContentOffsetForWebState:(web::WebState*)webState {
   if (webState->GetVisibleURL().DeprecatedGetOriginAsURL() !=
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator_unittest.mm
index f1b3c988..f026b33 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator_unittest.mm
@@ -19,8 +19,6 @@
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_consumer.h"
@@ -41,9 +39,6 @@
 #error "This file requires ARC support."
 #endif
 
-@protocol NTPHomeMediatorDispatcher <BrowserCommands, SnackbarCommands>
-@end
-
 class NTPHomeMediatorTest : public PlatformTest {
  public:
   NTPHomeMediatorTest() {
@@ -66,7 +61,6 @@
     navigation_manager_ = navigation_manager.get();
     fake_web_state_ = std::make_unique<web::FakeWebState>();
     logo_vendor_ = OCMProtocolMock(@protocol(LogoVendor));
-    dispatcher_ = OCMProtocolMock(@protocol(NTPHomeMediatorDispatcher));
     suggestions_view_controller_ =
         OCMClassMock([ContentSuggestionsCollectionViewController class]);
     voice_availability_.SetVoiceProviderEnabled(true);
@@ -95,7 +89,6 @@
           accountManagerService:accountManagerService
                      logoVendor:logo_vendor_
         voiceSearchAvailability:&voice_availability_];
-    mediator_.dispatcher = dispatcher_;
     mediator_.suggestionsViewController = suggestions_view_controller_;
     consumer_ = OCMProtocolMock(@protocol(NTPHomeConsumer));
     mediator_.consumer = consumer_;
@@ -111,7 +104,6 @@
   std::unique_ptr<Browser> browser_;
   id consumer_;
   id logo_vendor_;
-  id dispatcher_;
   id suggestions_view_controller_;
   FakeVoiceSearchAvailability voice_availability_;
   NTPHomeMediator* mediator_;
@@ -165,39 +157,6 @@
   EXPECT_OCMOCK_VERIFY(consumer_);
 }
 
-// Tests that the command is sent to the dispatcher when opening the Reading
-// List.
-TEST_F(NTPHomeMediatorTest, TestOpenReadingList) {
-  // Setup.
-  [mediator_ setUp];
-  OCMExpect([dispatcher_ showReadingList]);
-
-  // Action.
-  [mediator_ openReadingList];
-
-  // Test.
-  EXPECT_OCMOCK_VERIFY(dispatcher_);
-}
-
-// Tests that the command is sent to the loader when opening a most visited.
-TEST_F(NTPHomeMediatorTest, TestOpenMostVisited) {
-  // Setup.
-  [mediator_ setUp];
-  GURL url = GURL("http://chromium.org");
-  ContentSuggestionsMostVisitedItem* item =
-      [[ContentSuggestionsMostVisitedItem alloc] initWithType:0];
-  item.URL = url;
-
-  // Action.
-  [mediator_ openMostVisitedItem:item atIndex:0];
-
-  // Test.
-  EXPECT_EQ(url, url_loader_->last_params.web_params.url);
-  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
-      ui::PAGE_TRANSITION_AUTO_BOOKMARK,
-      url_loader_->last_params.web_params.transition_type));
-}
-
 // Tests that the voice search button is disabled when VoiceOver is turned on
 // and off.
 TEST_F(NTPHomeMediatorTest, DisableVoiceSearch) {
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
index e00f4d2..a21fe611 100644
--- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
@@ -78,11 +78,11 @@
 
   self.metricsConsentButton = [self createMetricsConsentButton];
   UIView* footerTopAnchor = nil;
-  BOOL showUMAReportingLink =
+  BOOL showUMAReportingCheckBox =
       fre_field_trial::GetNewMobileIdentityConsistencyFRE() ==
       NewMobileIdentityConsistencyFRE::kOld;
   self.footerTextView =
-      [self createFooterTextViewForOldFRE:showUMAReportingLink];
+      [self createFooterTextViewWithUMAReportingLink:!showUMAReportingCheckBox];
   [self.specificContentView addSubview:self.footerTextView];
 
   [NSLayoutConstraint activateConstraints:@[
@@ -94,7 +94,7 @@
         constraintEqualToAnchor:self.specificContentView.bottomAnchor],
   ]];
 
-  if (!showUMAReportingLink) {
+  if (!showUMAReportingCheckBox) {
     footerTopAnchor = self.footerTextView;
   } else {
     self.metricsConsentButton = [self createMetricsConsentButton];
@@ -242,11 +242,12 @@
 
 // Creates and configures the text view for the terms of service, with a
 // formatted link to the full text of the terms of service.
-- (UITextView*)createFooterTextViewForOldFRE:(BOOL)showUMAReportingLink {
+- (UITextView*)createFooterTextViewWithUMAReportingLink:
+    (BOOL)UMAReportingLink {
   NSAttributedString* termsOfServiceString = [self createTermsOfServiceString];
   NSMutableAttributedString* footerString = [[NSMutableAttributedString alloc]
       initWithAttributedString:termsOfServiceString];
-  if (!showUMAReportingLink) {
+  if (UMAReportingLink) {
     NSAttributedString* manageMetricsReported =
         [self createManageMetricsReportedString];
     [footerString appendAttributedString:manageMetricsReported];
@@ -261,7 +262,7 @@
   textView.linkTextAttributes =
       @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]};
   textView.translatesAutoresizingMaskIntoConstraints = NO;
-  textView.attributedText = [self createTermsOfServiceString];
+  textView.attributedText = footerString;
   return textView;
 }
 
diff --git a/ios/chrome/browser/ui/ntp/feed_header_view_controller.h b/ios/chrome/browser/ui/ntp/feed_header_view_controller.h
index b53f66b..0326a62 100644
--- a/ios/chrome/browser/ui/ntp/feed_header_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/feed_header_view_controller.h
@@ -25,10 +25,15 @@
 // The currently selected sorting for the Following feed.
 @property(nonatomic, assign) FollowingFeedSortType followingFeedSortType;
 
+// Whether the Following segment dot should currently be visible.
+@property(nonatomic, assign) BOOL followingSegmentDotVisible;
+
 // Initializes the header with the currently selected feed and the Following
 // feed's sort type.
 - (instancetype)initWithSelectedFeed:(FeedType)selectedFeed
-               followingFeedSortType:(FollowingFeedSortType)sortType
+               followingFeedSortType:
+                   (FollowingFeedSortType)followingFeedSortType
+          followingSegmentDotVisible:(BOOL)followingSegmentDotVisible
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm b/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
index f324c8d..9e8cb69 100644
--- a/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
@@ -45,8 +45,13 @@
 // The height and width of the header menu button. Based on the default
 // UISegmentedControl height.
 const CGFloat kButtonSize = 28;
-// Duration of the fade animation when the sort button appears/disappears.
-const CGFloat kSortButtonAnimationDuration = 0.3;
+// The radius of the dot indicating that there is new content in the Following
+// feed.
+const CGFloat kFollowingSegmentDotRadius = 3;
+// The distance between the Following segment dot and the Following label.
+const CGFloat kFollowingSegmentDotMargin = 8;
+// Duration of the fade animation for elements that toggle when switching feeds.
+const CGFloat kSegmentAnimationDuration = 0.3;
 
 // Image names for feed header icons.
 NSString* kMenuIcon = @"ellipsis";
@@ -73,9 +78,11 @@
 // Segmented control for toggling between the two feeds.
 @property(nonatomic, strong) UISegmentedControl* segmentedControl;
 
+// The dot in the Following segment indicating new content in the Following
+// feed.
+@property(nonatomic, strong) UIView* followingSegmentDot;
+
 // Currently selected feed.
-// TODO(crbug.com/1277974): Reassign this value instead of recreating feed
-// header when NTP coordinator's restart is improved.
 @property(nonatomic, assign) FeedType selectedFeed;
 
 // The blurred background of the feed header.
@@ -86,11 +93,14 @@
 @implementation FeedHeaderViewController
 
 - (instancetype)initWithSelectedFeed:(FeedType)selectedFeed
-               followingFeedSortType:(FollowingFeedSortType)sortType {
+               followingFeedSortType:
+                   (FollowingFeedSortType)followingFeedSortType
+          followingSegmentDotVisible:(BOOL)followingSegmentDotVisible {
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
     _selectedFeed = selectedFeed;
-    _followingFeedSortType = sortType;
+    _followingFeedSortType = followingFeedSortType;
+    _followingSegmentDotVisible = followingSegmentDotVisible;
 
     // The menu button is created early so that it can be assigned a tap action
     // before the view loads.
@@ -128,6 +138,10 @@
     self.sortButton.menu = [self createSortMenu];
     [self.container addSubview:self.sortButton];
 
+    self.followingSegmentDot = [self createFollowingSegmentDot];
+    self.followingSegmentDot.hidden = !self.followingSegmentDotVisible;
+    [self.segmentedControl addSubview:self.followingSegmentDot];
+
     if (!UIAccessibilityIsReduceTransparencyEnabled()) {
       self.blurBackgroundView = [self createBlurBackground];
       [self.view addSubview:self.blurBackgroundView];
@@ -185,6 +199,18 @@
   }
 }
 
+// Sets |followingSegmentDotVisible| and animates |followingSegmentDot|'s
+// visibility accordingly.
+- (void)setFollowingSegmentDotVisible:(BOOL)followingSegmentDotVisible {
+  DCHECK(IsWebChannelsEnabled());
+  _followingSegmentDotVisible = followingSegmentDotVisible;
+  [UIView animateWithDuration:kSegmentAnimationDuration
+                   animations:^{
+                     self.followingSegmentDot.alpha =
+                         followingSegmentDotVisible ? 1 : 0;
+                   }];
+}
+
 #pragma mark - Private
 
 // Creates sort menu with its content and active sort type.
@@ -315,6 +341,16 @@
   return segmentedControl;
 }
 
+// Configures and returns the dot indicating that there is new content in the
+// Following feed.
+- (UIView*)createFollowingSegmentDot {
+  UIView* followingSegmentDot = [[UIView alloc] init];
+  followingSegmentDot.layer.cornerRadius = kFollowingSegmentDotRadius;
+  followingSegmentDot.backgroundColor = [UIColor colorNamed:kBlue500Color];
+  followingSegmentDot.translatesAutoresizingMaskIntoConstraints = NO;
+  return followingSegmentDot;
+}
+
 // Configures and returns the blurred background of the feed header.
 - (UIVisualEffectView*)createBlurBackground {
   UIBlurEffect* blurEffect =
@@ -371,7 +407,47 @@
                          constant:kButtonHorizontalMargin],
       [self.sortButton.centerYAnchor
           constraintEqualToAnchor:self.container.centerYAnchor],
+      // Set Following segment dot size.
+      [self.followingSegmentDot.heightAnchor
+          constraintEqualToConstant:kFollowingSegmentDotRadius * 2],
+      [self.followingSegmentDot.widthAnchor
+          constraintEqualToConstant:kFollowingSegmentDotRadius * 2],
     ]];
+
+    // Find the "Following" label within the segmented control, since it is not
+    // exposed by UISegmentedControl.
+    UIView* followingSegment = self.segmentedControl.subviews[0];
+    UILabel* followingLabel;
+    for (UIView* view in followingSegment.subviews) {
+      if ([view isKindOfClass:[UILabel class]]) {
+        followingLabel = static_cast<UILabel*>(view);
+      }
+    }
+
+    // If the label was found, anchor the dot to it. Otherwise, anchor the dot
+    // to the top corner of the segmented control.
+    if (followingLabel) {
+      [NSLayoutConstraint activateConstraints:@[
+        // Anchor Following segment dot to label text.
+        [self.followingSegmentDot.leftAnchor
+            constraintEqualToAnchor:followingLabel.rightAnchor
+                           constant:kFollowingSegmentDotMargin],
+        [self.followingSegmentDot.bottomAnchor
+            constraintEqualToAnchor:followingLabel.topAnchor
+                           constant:kFollowingSegmentDotMargin],
+      ]];
+    } else {
+      [NSLayoutConstraint activateConstraints:@[
+        // Anchor Following segment dot to top corner.
+        [self.followingSegmentDot.rightAnchor
+            constraintEqualToAnchor:self.segmentedControl.rightAnchor
+                           constant:-kFollowingSegmentDotMargin],
+        [self.followingSegmentDot.topAnchor
+            constraintEqualToAnchor:self.segmentedControl.topAnchor
+                           constant:kFollowingSegmentDotMargin],
+      ]];
+    }
+
     if (self.blurBackgroundView) {
       AddSameConstraints(self.blurBackgroundView, self.view);
     }
@@ -396,7 +472,7 @@
   switch (segmentedControl.selectedSegmentIndex) {
     case static_cast<NSInteger>(FeedTypeDiscover): {
       [self.feedControlDelegate handleFeedSelected:FeedTypeDiscover];
-      [UIView animateWithDuration:kSortButtonAnimationDuration
+      [UIView animateWithDuration:kSegmentAnimationDuration
                        animations:^{
                          self.sortButton.alpha = 0;
                        }];
@@ -404,7 +480,8 @@
     }
     case static_cast<NSInteger>(FeedTypeFollowing): {
       [self.feedControlDelegate handleFeedSelected:FeedTypeFollowing];
-      [UIView animateWithDuration:kSortButtonAnimationDuration
+      // Only show sorting button for Following feed.
+      [UIView animateWithDuration:kSegmentAnimationDuration
                        animations:^{
                          self.sortButton.alpha = 1;
                        }];
diff --git a/ios/chrome/browser/ui/ntp/feed_management/follow_management_view_controller.mm b/ios/chrome/browser/ui/ntp/feed_management/follow_management_view_controller.mm
index 1ca2e27b6..7ddffdf2 100644
--- a/ios/chrome/browser/ui/ntp/feed_management/follow_management_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/feed_management/follow_management_view_controller.mm
@@ -98,6 +98,7 @@
     item.followedWebChannel = followedWebChannel;
     [model addItem:item toSectionWithIdentifier:DefaultSectionIdentifier];
   }
+  [self showOrHideEmptyTableViewBackground];
 }
 
 #pragma mark - UITableViewDelegate
@@ -189,6 +190,22 @@
 
 #pragma mark - Helpers
 
+- (void)showOrHideEmptyTableViewBackground {
+  TableViewModel* model = self.tableViewModel;
+  NSInteger section =
+      [model sectionForSectionIdentifier:DefaultSectionIdentifier];
+  NSInteger itemCount = [model numberOfItemsInSection:section];
+  if (itemCount == 0) {
+    [self addEmptyTableViewWithImage:nil
+                               title:l10n_util::GetNSString(
+                                         IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TITLE)
+                            subtitle:l10n_util::GetNSString(
+                                         IDS_IOS_FOLLOW_MANAGEMENT_EMPTY_TEXT)];
+  } else {
+    [self removeEmptyTableView];
+  }
+}
+
 // Deletes item at |indexPath| from both model and UI.
 - (void)deleteItemAtIndexPath:(NSIndexPath*)indexPath {
   TableViewModel* model = self.tableViewModel;
@@ -201,6 +218,7 @@
                         atIndex:index];
   [self.tableView deleteRowsAtIndexPaths:@[ indexPath ]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
+  [self showOrHideEmptyTableViewBackground];
 }
 
 // Reinserts last unfollowed item into both model and UI.
@@ -219,6 +237,7 @@
                         withRowAnimation:UITableViewRowAnimationAutomatic];
   self.lastUnfollowedWebChannelItem = nil;
   self.indexPathOfLastUnfollowAttempt = nil;
+  [self showOrHideEmptyTableViewBackground];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 9e7eb3b..ecbe447e 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -503,6 +503,7 @@
   if (_webState == webState) {
     return;
   }
+  self.contentSuggestionsCoordinator.webState = webState;
   self.ntpMediator.webState = webState;
   _webState = webState;
 }
@@ -587,6 +588,12 @@
 
 - (void)handleFeedSelected:(FeedType)feedType {
   DCHECK(IsWebChannelsEnabled());
+  if (feedType == FeedTypeFollowing) {
+    // Clears dot and notifies service that the Following feed content has been
+    // seen.
+    self.feedHeaderViewController.followingSegmentDotVisible = NO;
+    self.discoverFeedService->SetFollowingFeedContentSeen();
+  }
   self.selectedFeed = feedType;
   [self updateNTPForFeed];
 }
@@ -1074,9 +1081,6 @@
   ntpMediator.browser = self.browser;
   ntpMediator.ntpViewController = self.ntpViewController;
   ntpMediator.headerCollectionInteractionHandler = self.headerSynchronizer;
-  ntpMediator.NTPMetrics = [[NTPHomeMetrics alloc]
-      initWithBrowserState:self.browser->GetBrowserState()];
-  ntpMediator.NTPMetrics.webState = self.webState;
   return ntpMediator;
 }
 
@@ -1147,10 +1151,12 @@
   DCHECK(!self.browser->GetBrowserState()->IsOffTheRecord());
   if (!_feedHeaderViewController) {
     _feedHeaderViewController = [[FeedHeaderViewController alloc]
-         initWithSelectedFeed:self.selectedFeed
-        followingFeedSortType:(FollowingFeedSortType)
-                                  self.prefService->GetInteger(
-                                      prefs::kNTPFollowingFeedSortType)];
+              initWithSelectedFeed:self.selectedFeed
+             followingFeedSortType:(FollowingFeedSortType)
+                                       self.prefService->GetInteger(
+                                           prefs::kNTPFollowingFeedSortType)
+        followingSegmentDotVisible:self.discoverFeedService
+                                       ->GetFollowingFeedHasUnseenContent()];
     _feedHeaderViewController.feedControlDelegate = self;
     [_feedHeaderViewController.menuButton
                addTarget:self
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index 449520bd..a9b8c081 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -462,6 +462,9 @@
   if (self.feedHeaderViewController) {
     [self removeFromViewHierarchy:self.feedHeaderViewController];
   }
+  if (self.headerController) {
+    [self removeFromViewHierarchy:self.headerController];
+  }
   self.contentSuggestionsHeightConstraint.active = NO;
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index df8a0bdc..66dd16f 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -577,6 +577,30 @@
 - (void)testEmpty {
 }
 
+- (void)testOmniboxDefocusesOnTabSwitch {
+  [self openPage1];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey waitForMainTabCount:2];
+  [self openPage2];
+
+  [ChromeEarlGreyUI focusOmnibox];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_typeText(@"Obama")];
+
+  // The popup should open.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxPopupList()]
+      assertWithMatcher:grey_notNil()];
+
+  // Switch to the first tab.
+  [ChromeEarlGrey selectTabAtIndex:0];
+  [ChromeEarlGrey waitForWebStateContainingText:kPage1];
+
+  // The omnibox shouldn't be focused and the popup should be closed.
+  [self checkLocationBarSteadyState];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxPopupList()]
+      assertWithMatcher:grey_notVisible()];
+}
+
 @end
 
 #pragma mark - Edit state tests
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm
index 4194da1..be2ca28 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_egtest.mm
@@ -27,11 +27,14 @@
 
 // Returns the popup row containing the |url| as suggestion.
 id<GREYMatcher> PopupRowWithUrl(GURL url) {
-  return grey_allOf(
-      chrome_test_util::OmniboxPopupRow(),
-      grey_descendant(chrome_test_util::StaticTextWithAccessibilityLabel(
-          base::SysUTF8ToNSString(url.GetContent()))),
-      grey_sufficientlyVisible(), nil);
+  NSString* urlString = base::SysUTF8ToNSString(url.GetContent());
+  id<GREYMatcher> URLMatcher =
+      [ChromeEarlGrey isNewOmniboxPopupEnabled]
+          ? grey_descendant(grey_accessibilityValue(urlString))
+          : grey_descendant(
+                chrome_test_util::StaticTextWithAccessibilityLabel(urlString));
+  return grey_allOf(chrome_test_util::OmniboxPopupRow(), URLMatcher,
+                    grey_sufficientlyVisible(), nil);
 }
 
 // Returns the switch to open tab element for the |url|.
@@ -445,11 +448,14 @@
       performAction:grey_typeText(@"hello")];
 
   // Matcher for a URL-what-you-typed suggestion.
-  id<GREYMatcher> row = grey_allOf(
-      chrome_test_util::OmniboxPopupRow(),
-      grey_descendant(
-          chrome_test_util::StaticTextWithAccessibilityLabel(@"hello")),
-      grey_sufficientlyVisible(), nil);
+  id<GREYMatcher> textMatcher =
+      [ChromeEarlGrey isNewOmniboxPopupEnabled]
+          ? grey_accessibilityLabel(@"hello")
+          : grey_descendant(
+                chrome_test_util::StaticTextWithAccessibilityLabel(@"hello"));
+  id<GREYMatcher> row =
+      grey_allOf(chrome_test_util::OmniboxPopupRow(), textMatcher,
+                 grey_sufficientlyVisible(), nil);
 
   // Omnibox can reorder itself in multiple animations, so add an extra wait
   // here.
diff --git a/ios/chrome/browser/ui/omnibox/popup/popup_swift_bridge.h b/ios/chrome/browser/ui/omnibox/popup/popup_swift_bridge.h
index b38f25d..4e84764 100644
--- a/ios/chrome/browser/ui/omnibox/popup/popup_swift_bridge.h
+++ b/ios/chrome/browser/ui/omnibox/popup/popup_swift_bridge.h
@@ -14,5 +14,7 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_pedal.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_accessibility_identifier_constants.h"
 #import "ios/chrome/common/ui/colors/swift_bridge.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util_mac_bridge.h"
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_POPUP_SWIFT_BRIDGE_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match.swift
index 6561e387..662a7ca 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match.swift
@@ -97,7 +97,8 @@
       text: "clear browsing data",
       pedal: OmniboxPedalData(
         title: "Click here", subtitle: "PAR → NYC",
-        accessibilityHint: "a11y hint", imageName: "pedal_dino", action: {})))
+        accessibilityHint: "a11y hint", imageName: "pedal_dino",
+        action: { print("dino pedal clicked") })))
   static let appendable = PopupMatch(
     suggestion: FakeAutocompleteSuggestion(
       text: "is appendable",
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
index 0273f200..fcadf38 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
@@ -125,12 +125,23 @@
         .onPreferenceChange(PressedPreferenceKey.self) { isPressed in
           self.isPressed = isPressed
         }
+        .accessibilityElement()
+        .accessibilityLabel(match.text)
+        .accessibilityValue(match.detailText ?? "")
+        .accessibilityAction(
+          named: match.isTabMatch
+            ? L10NUtils.string(forMessageId: IDS_IOS_OMNIBOX_POPUP_SWITCH_TO_OPEN_TAB)
+            : L10NUtils.string(forMessageId: IDS_IOS_OMNIBOX_POPUP_APPEND), trailingButtonHandler)
+        .accessibilityRemoveTraits(.isButton)
 
       // The content is in front of the button, for proper hit testing.
       HStack(alignment: .center, spacing: 0) {
         HStack(alignment: .center, spacing: 0) {
           Spacer()
-          match.image.map { image in PopupMatchImageView(image: image) }
+          match.image.map { image in
+            PopupMatchImageView(image: image)
+              .accessibilityHidden(true)
+          }
           Spacer()
         }.frame(width: Dimensions.leadingSpacing)
         VStack(alignment: .leading, spacing: 0) {
@@ -138,6 +149,7 @@
             Text(match.text)
               .lineLimit(1)
               .truncatedWithGradient()
+              .accessibilityHidden(true)
 
             if let subtitle = match.detailText, !subtitle.isEmpty {
               Text(subtitle)
@@ -145,6 +157,7 @@
                 .foregroundColor(Color.gray)
                 .lineLimit(1)
                 .truncatedWithGradient()
+                .accessibilityHidden(true)
             }
           }
           .frame(height: Dimensions.textHeight)
@@ -154,6 +167,7 @@
             PopupMatchRowActionButton(pedal: pedal)
               .padding(Dimensions.actionButtonOuterPadding)
               .offset(Dimensions.actionButtonOffset)
+              .accessibilityHidden(true)
           }
         }
         Spacer()
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_trailing_button.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_trailing_button.swift
index 88e0836..68c50d7 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_trailing_button.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_trailing_button.swift
@@ -34,6 +34,15 @@
         )
     }
     .buttonStyle(.plain)
+    // The button shouldn't be an actual accessibility element for
+    // VoiceOver.
+    .accessibilityHidden(true)
+    // TODO(crbug.com/1312110): This should be `children: .contain` so the
+    // new accessibility element isn't accessible. However, EG currently can't
+    // tap on a non-accessible SwiftUI view in a test.
+    // Create a new accessibility element that is non-accessible so tests
+    // can find the button.
+    .accessibilityElement(children: .ignore)
     .accessibilityIdentifier(
       match.isTabMatch
         ? kOmniboxPopupRowSwitchTabAccessibilityIdentifier
diff --git a/ios/chrome/browser/ui/omnibox/popup/swiftui_previews/omnibox_popup/external_defines.swift b/ios/chrome/browser/ui/omnibox/popup/swiftui_previews/omnibox_popup/external_defines.swift
index 21fc51d..549c42f 100644
--- a/ios/chrome/browser/ui/omnibox/popup/swiftui_previews/omnibox_popup/external_defines.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/swiftui_previews/omnibox_popup/external_defines.swift
@@ -23,3 +23,23 @@
     return "omnibox suggestion \(indexPath.section) \(indexPath.row)"
   }
 }
+
+let IDS_IOS_OMNIBOX_POPUP_SWITCH_TO_OPEN_TAB = 1
+let IDS_IOS_OMNIBOX_POPUP_APPEND = 2
+
+public class L10NUtils {
+  public static func string(forMessageId: Int) -> String {
+    switch forMessageId {
+    case IDS_IOS_OMNIBOX_POPUP_SWITCH_TO_OPEN_TAB:
+      return "Open tab"
+
+    case IDS_IOS_OMNIBOX_POPUP_APPEND:
+      return "Append"
+
+    default:
+      return "STRING_NOT_DEFINED"
+
+    }
+  }
+
+}
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index 00b1327..4e09412d 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -34,7 +34,6 @@
     "//ios/chrome/browser/passwords:save_passwords_consumer",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
-    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/settings/password/password_details",
@@ -92,7 +91,6 @@
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
-    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/settings:settings_root",
diff --git a/ios/chrome/browser/ui/settings/password/password_issues_coordinator.mm b/ios/chrome/browser/ui/settings/password/password_issues_coordinator.mm
index ab3484917..6d587d9 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues_coordinator.mm
@@ -18,7 +18,6 @@
 #import "ios/chrome/browser/ui/settings/password/password_issues_presenter.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues_table_view_controller.h"
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 #include "ui/base/l10n/l10n_util.h"
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
index c3dea167..75f995c 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
@@ -29,7 +29,6 @@
 #import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -151,11 +150,9 @@
   self.passwordDetailsCoordinator.delegate = nil;
   self.passwordDetailsCoordinator = nil;
 
-  if (base::FeatureList::IsEnabled(kCredentialProviderExtensionPromo)) {
-    [self.passwordsInOtherAppsCoordinator stop];
-    self.passwordsInOtherAppsCoordinator.delegate = nil;
-    self.passwordsInOtherAppsCoordinator = nil;
-  }
+  [self.passwordsInOtherAppsCoordinator stop];
+  self.passwordsInOtherAppsCoordinator.delegate = nil;
+  self.passwordsInOtherAppsCoordinator = nil;
 
   [self.mediator disconnect];
 }
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_egtest.mm
index bc430ec3..c548cbd4 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_egtest.mm
@@ -103,12 +103,6 @@
   _passwordAutoFillStatusSwizzler.reset();
 }
 
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.features_enabled.push_back(kCredentialProviderExtensionPromo);
-  return config;
-}
-
 #pragma mark - helper functions
 
 // Tests that the banner image, title and subtitle are visible.
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
index d70111f..456d3ea 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
@@ -22,7 +22,6 @@
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/saved_passwords_presenter_observer.h"
 #import "ios/chrome/browser/ui/settings/utils/password_auto_fill_status_manager.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/common/string_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/favicon/favicon_constants.h"
@@ -115,9 +114,7 @@
     _passwordsPresenterObserver =
         std::make_unique<SavedPasswordsPresenterObserverBridge>(
             self, _savedPasswordsPresenter);
-    if (base::FeatureList::IsEnabled(kCredentialProviderExtensionPromo)) {
-      [[PasswordAutoFillStatusManager sharedManager] addObserver:self];
-    }
+    [[PasswordAutoFillStatusManager sharedManager] addObserver:self];
   }
   return self;
 }
@@ -129,9 +126,7 @@
   if (_passwordCheckObserver) {
     _passwordCheckManager->RemoveObserver(_passwordCheckObserver.get());
   }
-  if (base::FeatureList::IsEnabled(kCredentialProviderExtensionPromo)) {
-    [[PasswordAutoFillStatusManager sharedManager] removeObserver:self];
-  }
+  [[PasswordAutoFillStatusManager sharedManager] removeObserver:self];
 }
 
 - (void)setConsumer:(id<PasswordsConsumer>)consumer {
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 8d69ba2b6..1e07fae 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -62,7 +62,6 @@
 #import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
@@ -580,14 +579,12 @@
   }
 
   // Passwords in other apps.
-  if (base::FeatureList::IsEnabled(kCredentialProviderExtensionPromo)) {
-    [model addSectionWithIdentifier:SectionIdentifierPasswordsInOtherApps];
-    if (!_passwordsInOtherAppsItem) {
-      _passwordsInOtherAppsItem = [self passwordsInOtherAppsItem];
-    }
-    [model addItem:_passwordsInOtherAppsItem
-        toSectionWithIdentifier:SectionIdentifierPasswordsInOtherApps];
+  [model addSectionWithIdentifier:SectionIdentifierPasswordsInOtherApps];
+  if (!_passwordsInOtherAppsItem) {
+    _passwordsInOtherAppsItem = [self passwordsInOtherAppsItem];
   }
+  [model addItem:_passwordsInOtherAppsItem
+      toSectionWithIdentifier:SectionIdentifierPasswordsInOtherApps];
 
   // Password check.
   [model addSectionWithIdentifier:SectionIdentifierPasswordCheck];
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_view_controller.mm b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_view_controller.mm
index 46c540c..552cd9b 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_view_controller.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/browser/ui/settings/privacy/privacy_constants.h"
 #import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "net/base/mac/url_conversions.h"
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn
index e8a7d8c..e707b2d 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn
@@ -16,6 +16,11 @@
     "safe_browsing_enhanced_protection_view_controller.mm",
   ]
   deps = [
+    "resources:bar_chart",
+    "resources:g_icon",
+    "resources:globe",
+    "resources:key",
+    "resources:shield",
     "//components/strings:components_strings_grit",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
@@ -26,11 +31,14 @@
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/cells",
+    "//ios/chrome/browser/ui/settings/resources:settings_safe_browsing",
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view:utils",
     "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/common/ui/colors:colors",
     "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/BUILD.gn
new file mode 100644
index 0000000..1498f8e
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/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.
+
+import("//build/config/ios/asset_catalog.gni")
+
+imageset("bar_chart") {
+  sources = [
+    "bar_chart.imageset/Contents.json",
+    "bar_chart.imageset/bar_chart@2x.png",
+    "bar_chart.imageset/bar_chart@3x.png",
+  ]
+}
+
+imageset("g_icon") {
+  sources = [
+    "g_icon.imageset/Contents.json",
+    "g_icon.imageset/g_icon@2x.png",
+    "g_icon.imageset/g_icon@3x.png",
+  ]
+}
+
+imageset("globe") {
+  sources = [
+    "globe.imageset/Contents.json",
+    "globe.imageset/globe@2x.png",
+    "globe.imageset/globe@3x.png",
+  ]
+}
+
+imageset("key") {
+  sources = [
+    "key.imageset/Contents.json",
+    "key.imageset/key@2x.png",
+    "key.imageset/key@3x.png",
+  ]
+}
+
+imageset("shield") {
+  sources = [
+    "shield.imageset/Contents.json",
+    "shield.imageset/shield@2x.png",
+    "shield.imageset/shield@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/Contents.json b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/Contents.json
new file mode 100644
index 0000000..fbe23c1
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "bar_chart@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "bar_chart@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@2x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@2x.png
new file mode 100644
index 0000000..515ed9f3
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@3x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@3x.png
new file mode 100644
index 0000000..feda9cd8
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/bar_chart.imageset/bar_chart@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/Contents.json b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/Contents.json
new file mode 100644
index 0000000..7cdbb8f
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "g_icon@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "g_icon@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@2x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@2x.png
new file mode 100644
index 0000000..51daf70
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@3x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@3x.png
new file mode 100644
index 0000000..9a6c9fd5
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/g_icon.imageset/g_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/Contents.json b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/Contents.json
new file mode 100644
index 0000000..711e2b3
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "globe@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "globe@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@2x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@2x.png
new file mode 100644
index 0000000..f1e5a3b0
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@3x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@3x.png
new file mode 100644
index 0000000..2087144f
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/globe.imageset/globe@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/Contents.json b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/Contents.json
new file mode 100644
index 0000000..37d8769
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "key@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "key@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@2x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@2x.png
new file mode 100644
index 0000000..f01911a7
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@3x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@3x.png
new file mode 100644
index 0000000..4055849
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/key.imageset/key@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/Contents.json b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/Contents.json
new file mode 100644
index 0000000..fbe6fe0
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "shield@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "shield@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@2x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@2x.png
new file mode 100644
index 0000000..32553f982
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@3x.png b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@3x.png
new file mode 100644
index 0000000..fa52be75
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/resources/shield.imageset/shield@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h
index 44ae1f9..ac4c7dc 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h
@@ -15,4 +15,20 @@
 // cell.
 extern NSString* const kSafeBrowsingEnhancedProtectionShieldCellId;
 
+// The accessibility identifier of the Safe Browsing Enhanced Protection shield
+// cell.
+extern NSString* const kSafeBrowsingEnhancedProtectionGIconCellId;
+
+// The accessibility identifier of the Safe Browsing Enhanced Protection G Icon
+// cell.
+extern NSString* const kSafeBrowsingEnhancedProtectionGlobeCellId;
+
+// The accessibility identifier of the Safe Browsing Enhanced Protection key
+// cell.
+extern NSString* const kSafeBrowsingEnhancedProtectionKeyCellId;
+
+// The accessibility identifier of the Safe Browsing Enhanced Protection metric
+// cell.
+extern NSString* const kSafeBrowsingEnhancedProtectionMetricCellId;
+
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_SAFE_BROWSING_SAFE_BROWSING_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.mm b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.mm
index 7013d4e5..c963fd6 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.mm
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.mm
@@ -13,3 +13,15 @@
 
 NSString* const kSafeBrowsingEnhancedProtectionShieldCellId =
     @"kSafeBrowsingEnhancedProtectionShieldCellId";
+
+NSString* const kSafeBrowsingEnhancedProtectionGIconCellId =
+    @"kSafeBrowsingEnhancedProtectionGIconCellId";
+
+NSString* const kSafeBrowsingEnhancedProtectionGlobeCellId =
+    @"kSafeBrowsingEnhancedProtectionGlobeCellId";
+
+NSString* const kSafeBrowsingEnhancedProtectionKeyCellId =
+    @"kSafeBrowsingEnhancedProtectionKeyCellId";
+
+NSString* const kSafeBrowsingEnhancedProtectionMetricCellId =
+    @"kSafeBrowsingEnhancedProtectionMetricCellId";
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_coordinator.mm b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_coordinator.mm
index 6b71790..e9ac0bb 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_coordinator.mm
@@ -14,9 +14,11 @@
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_mediator.h"
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_view_controller.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -51,6 +53,7 @@
 - (void)start {
   self.viewController = [[SafeBrowsingEnhancedProtectionViewController alloc]
       initWithStyle:ChromeTableViewStyle()];
+  self.viewController.styler.cellSeparatorColor = UIColor.clearColor;
   self.viewController.presentationDelegate = self;
   self.mediator = [[SafeBrowsingEnhancedProtectionMediator alloc] init];
   self.mediator.consumer = self.viewController;
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_mediator.mm b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_mediator.mm
index ff64bae..df3c501e 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_mediator.mm
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_mediator.mm
@@ -8,6 +8,8 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h"
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h"
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_consumer.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#include "ios/chrome/grit/ios_google_chrome_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -20,7 +22,11 @@
 namespace {
 // List of item types.
 typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypeShield = kItemTypeEnumZero,
+  ItemTypeShieldIcon = kItemTypeEnumZero,
+  ItemTypeGIcon,
+  ItemTypeGlobeIcon,
+  ItemTypeKeyIcon,
+  ItemTypeMetricIcon,
 };
 }  // namespace
 
@@ -43,12 +49,55 @@
   if (!_safeBrowsingEnhancedProtectionItems) {
     NSMutableArray* items = [NSMutableArray array];
     SettingsImageDetailTextItem* shieldIconItem = [self
-             detailItemWithType:ItemTypeShield
+             detailItemWithType:ItemTypeShieldIcon
                      detailText:
                          IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_ONE
+                          image:[[UIImage imageNamed:@"shield"]
+                                    imageWithRenderingMode:
+                                        UIImageRenderingModeAlwaysTemplate]
         accessibilityIdentifier:kSafeBrowsingEnhancedProtectionShieldCellId];
     [items addObject:shieldIconItem];
 
+    SettingsImageDetailTextItem* gIconItem = [self
+             detailItemWithType:ItemTypeGIcon
+                     detailText:
+                         IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO
+                          image:[[UIImage imageNamed:@"g_icon"]
+                                    imageWithRenderingMode:
+                                        UIImageRenderingModeAlwaysTemplate]
+        accessibilityIdentifier:kSafeBrowsingEnhancedProtectionGIconCellId];
+    [items addObject:gIconItem];
+
+    SettingsImageDetailTextItem* globeIconItem = [self
+             detailItemWithType:ItemTypeGlobeIcon
+                     detailText:
+                         IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE
+                          image:[[UIImage imageNamed:@"globe"]
+                                    imageWithRenderingMode:
+                                        UIImageRenderingModeAlwaysTemplate]
+        accessibilityIdentifier:kSafeBrowsingEnhancedProtectionGlobeCellId];
+    [items addObject:globeIconItem];
+
+    SettingsImageDetailTextItem* keyIconItem = [self
+             detailItemWithType:ItemTypeKeyIcon
+                     detailText:
+                         IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR
+                          image:[[UIImage imageNamed:@"key"]
+                                    imageWithRenderingMode:
+                                        UIImageRenderingModeAlwaysTemplate]
+        accessibilityIdentifier:kSafeBrowsingEnhancedProtectionKeyCellId];
+    [items addObject:keyIconItem];
+
+    SettingsImageDetailTextItem* metricIconItem = [self
+             detailItemWithType:ItemTypeMetricIcon
+                     detailText:
+                         IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE
+                          image:[[UIImage imageNamed:@"bar_chart"]
+                                    imageWithRenderingMode:
+                                        UIImageRenderingModeAlwaysTemplate]
+        accessibilityIdentifier:kSafeBrowsingEnhancedProtectionMetricCellId];
+    [items addObject:metricIconItem];
+
     _safeBrowsingEnhancedProtectionItems = items;
   }
   return _safeBrowsingEnhancedProtectionItems;
@@ -67,11 +116,14 @@
 // Creates item that will show what Enhanced Protection entails.
 - (SettingsImageDetailTextItem*)detailItemWithType:(NSInteger)type
                                         detailText:(NSInteger)detailText
+                                             image:(UIImage*)image
                            accessibilityIdentifier:
                                (NSString*)accessibilityIdentifier {
   SettingsImageDetailTextItem* detailItem =
       [[SettingsImageDetailTextItem alloc] initWithType:type];
   detailItem.detailText = l10n_util::GetNSString(detailText);
+  detailItem.image = image;
+  detailItem.imageViewTintColor = [UIColor colorNamed:kGrey600Color];
   detailItem.accessibilityIdentifier = accessibilityIdentifier;
 
   return detailItem;
diff --git a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_view_controller.mm b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_view_controller.mm
index 3157f53..839700dd 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_enhanced_protection_view_controller.mm
@@ -12,6 +12,8 @@
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "net/base/mac/url_conversions.h"
@@ -45,6 +47,7 @@
       kSafeBrowsingEnhancedProtectionTableViewId;
   self.title =
       l10n_util::GetNSString(IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_TITLE);
+  self.styler.cellBackgroundColor = UIColor.clearColor;
   [self loadModel];
 }
 
@@ -58,6 +61,13 @@
   // TODO(crbug.com/1307428): Add UMA recording
 }
 
+#pragma mark - SafeBrowsingEnhancedProtectionConsumer
+
+- (void)setSafeBrowsingEnhancedProtectionItems:
+    (ItemArray)safeBrowsingEnhancedProtectionItems {
+  _safeBrowsingEnhancedProtectionItems = safeBrowsingEnhancedProtectionItems;
+}
+
 #pragma mark - CollectionViewController
 
 - (void)loadModel {
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_features.mm b/ios/chrome/browser/ui/start_surface/start_surface_features.mm
index 3ef2141..bb3c3625 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_features.mm
+++ b/ios/chrome/browser/ui/start_surface/start_surface_features.mm
@@ -36,10 +36,10 @@
 
 bool ShouldShrinkLogoForStartSurface() {
   return base::GetFieldTrialParamByFeatureAsBool(
-      kStartSurface, kStartSurfaceShrinkLogoParam, false);
+      kStartSurface, kStartSurfaceShrinkLogoParam, true);
 }
 
 bool ShouldShowReturnToMostRecentTabForStartSurface() {
   return base::GetFieldTrialParamByFeatureAsBool(
-      kStartSurface, kStartSurfaceReturnToRecentTabParam, false);
+      kStartSurface, kStartSurfaceReturnToRecentTabParam, true);
 }
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 4aa12511..3d3ab99 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -57,9 +57,6 @@
 const base::Feature kUseLensToSearchForImage{"UseLensToSearchForImage",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kCredentialProviderExtensionPromo{
-    "CredentialProviderExtensionPromo", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kRemoveExcessNTPs{"RemoveExcessNTPs",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index e59df0eb..f0f366d 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -70,10 +70,6 @@
 // Feature flag to enable using Lens to search for images.
 extern const base::Feature kUseLensToSearchForImage;
 
-// Feature flag to enable promotional view for Passwords In Other Apps in
-// Settings.
-extern const base::Feature kCredentialProviderExtensionPromo;
-
 // Feature flag to enable duplicate NTP cleanup.
 extern const base::Feature kRemoveExcessNTPs;
 
diff --git a/ios/chrome/common/app_group/app_group_field_trial_version.h b/ios/chrome/common/app_group/app_group_field_trial_version.h
index 3af5da3a..977d028 100644
--- a/ios/chrome/common/app_group/app_group_field_trial_version.h
+++ b/ios/chrome/common/app_group/app_group_field_trial_version.h
@@ -21,10 +21,6 @@
 // feature.
 extern const int kPasswordCreationFeatureVersion;
 
-// The current version of the credential provider extension's user education
-// feature.
-extern const int kCredentialProviderExtensionPromoFeatureVersion;
-
 // The current version of the credential provider extension's password manager
 // branding update feature.
 extern const int kPasswordManagerBrandingUpdateFeatureVersion;
diff --git a/ios/chrome/common/app_group/app_group_field_trial_version.mm b/ios/chrome/common/app_group/app_group_field_trial_version.mm
index 495d3f8..53db54ae 100644
--- a/ios/chrome/common/app_group/app_group_field_trial_version.mm
+++ b/ios/chrome/common/app_group/app_group_field_trial_version.mm
@@ -13,8 +13,6 @@
 
 const int kPasswordCreationFeatureVersion = 0;
 
-const int kCredentialProviderExtensionPromoFeatureVersion = 0;
-
 const int kPasswordManagerBrandingUpdateFeatureVersion = 0;
 
 const int kCredentialProviderExtensionFaviconsFeatureVersion = 0;
diff --git a/ios/chrome/common/credential_provider/constants.h b/ios/chrome/common/credential_provider/constants.h
index eaa94d1..887c5aae 100644
--- a/ios/chrome/common/credential_provider/constants.h
+++ b/ios/chrome/common/credential_provider/constants.h
@@ -39,10 +39,6 @@
 // to be sync once Chrome is updated.
 extern NSString* const kUserDefaultsCredentialProviderFirstTimeSyncCompleted;
 
-// Key for the app group user defaults indicating if the user has enabled and
-// given consent for the credential provider extension.
-extern NSString* const kUserDefaultsCredentialProviderConsentVerified;
-
 // Values of the UMA IOS.CredentialExtension.PasswordCreated. Must be kept up to
 // date with IOSCredentialProviderPasswordCreated in enums.xml. These values are
 // persisted to logs. Entries should not be renumbered and numeric values should
diff --git a/ios/chrome/common/credential_provider/constants.mm b/ios/chrome/common/credential_provider/constants.mm
index 8bceeff..5f8ab14a 100644
--- a/ios/chrome/common/credential_provider/constants.mm
+++ b/ios/chrome/common/credential_provider/constants.mm
@@ -110,6 +110,3 @@
 
 NSString* const kUserDefaultsCredentialProviderFirstTimeSyncCompleted =
     @"UserDefaultsCredentialProviderFirstTimeSyncCompleted.V1";
-
-NSString* const kUserDefaultsCredentialProviderConsentVerified =
-    @"UserDefaultsCredentialProviderConsentVerified";
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
index ea6d662..f2a5f3dc 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
@@ -13,8 +13,6 @@
 extern NSString* const kConfirmationAlertSubtitleAccessibilityIdentifier;
 extern NSString* const kConfirmationAlertPrimaryActionAccessibilityIdentifier;
 extern NSString* const kConfirmationAlertSecondaryActionAccessibilityIdentifier;
-extern NSString* const
-    kConfirmationAlertBarPrimaryActionAccessibilityIdentifier;
 
 @protocol ConfirmationAlertActionHandler;
 
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
index 519673c..f99c760 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -31,21 +31,18 @@
     @"kConfirmationAlertSecondaryActionAccessibilityIdentifier";
 NSString* const kConfirmationAlertTertiaryActionAccessibilityIdentifier =
     @"kConfirmationAlertTertiaryActionAccessibilityIdentifier";
-NSString* const kConfirmationAlertBarPrimaryActionAccessibilityIdentifier =
-    @"kConfirmationAlertBarPrimaryActionAccessibilityIdentifier";
 
 namespace {
 
+constexpr CGFloat kActionsBottomMargin = 10;
 // Gradient height.
-const CGFloat kGradientHeight = 40.;
-
+constexpr CGFloat kGradientHeight = 40.;
 constexpr CGFloat kScrollViewBottomInsets = 20;
 constexpr CGFloat kStackViewSpacing = 8;
 constexpr CGFloat kStackViewSpacingAfterIllustration = 27;
 // The multiplier used when in regular horizontal size class.
-constexpr CGFloat kSafeAreaMultiplier = 0.8;
-constexpr CGFloat kButtonMaxWidth = 327;
-constexpr CGFloat kContentMaxWidth = 500;
+constexpr CGFloat kSafeAreaMultiplier = 0.65;
+constexpr CGFloat kContentOptimalWidth = 327;
 
 }  // namespace
 
@@ -58,10 +55,6 @@
 @property(nonatomic, strong) UIButton* tertiaryActionButton;
 @property(nonatomic, strong) UIToolbar* topToolbar;
 @property(nonatomic, strong) UIImageView* imageView;
-// Constraints.
-@property(nonatomic, strong) NSLayoutConstraint* regularWidthConstraints;
-@property(nonatomic, strong)
-    NSLayoutConstraint* buttonStackViewBottomVerticalConstraint;
 @property(nonatomic, strong) NSLayoutConstraint* imageViewAspectRatioConstraint;
 @end
 
@@ -140,20 +133,23 @@
   heightConstraint.active = YES;
 
   [NSLayoutConstraint activateConstraints:@[
-    [stackView.widthAnchor
-        constraintLessThanOrEqualToConstant:kContentMaxWidth],
     [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
-
+    // Width Scroll View constraint for regular mode.
+    [stackView.widthAnchor
+        constraintGreaterThanOrEqualToAnchor:margins.widthAnchor
+                                  multiplier:kSafeAreaMultiplier],
     // Disable horizontal scrolling.
     [stackView.widthAnchor
         constraintLessThanOrEqualToAnchor:margins.widthAnchor],
-
   ]];
 
-  // Width Scroll View constraint for regular mode.
-  self.regularWidthConstraints = [stackView.widthAnchor
-      constraintLessThanOrEqualToAnchor:margins.widthAnchor
-                             multiplier:kSafeAreaMultiplier];
+  // This constraint is added to enforce that the content width should be as
+  // close to the optimal width as possible, within the range already activated
+  // for "stackView.widthAnchor" previously, with a higher priority.
+  NSLayoutConstraint* contentLayoutGuideWidthConstraint =
+      [stackView.widthAnchor constraintEqualToConstant:kContentOptimalWidth];
+  contentLayoutGuideWidthConstraint.priority = UILayoutPriorityRequired - 1;
+  contentLayoutGuideWidthConstraint.active = YES;
 
   // The bottom anchor for the scroll view.
   NSLayoutYAxisAnchor* scrollViewBottomAnchor =
@@ -164,26 +160,41 @@
                          self.tertiaryActionString;
   if (hasActionButton) {
     UIView* actionStackView = [self createActionStackView];
-
     [self.view addSubview:actionStackView];
-    self.buttonStackViewBottomVerticalConstraint = [actionStackView.bottomAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
+
     // Add a low priority width constraints to make sure that the buttons are
     // taking as much width as they can.
+    CGFloat extraBottomMargin =
+        self.secondaryActionString ? 0 : kActionsBottomMargin;
     NSLayoutConstraint* lowPriorityWidthConstraint =
-        [actionStackView.widthAnchor constraintEqualToConstant:kButtonMaxWidth];
+        [actionStackView.widthAnchor
+            constraintEqualToConstant:kContentOptimalWidth];
     lowPriorityWidthConstraint.priority = UILayoutPriorityDefaultHigh + 1;
+    // Also constrain the bottom of the action stack view to the bottom of the
+    // safe area, but with a lower priority, so that the action stack view is
+    // put as close to the bottom as possible.
+    NSLayoutConstraint* actionBottomConstraint = [actionStackView.bottomAnchor
+        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
+    actionBottomConstraint.priority = UILayoutPriorityDefaultLow;
+    actionBottomConstraint.active = YES;
 
     [NSLayoutConstraint activateConstraints:@[
       [actionStackView.leadingAnchor
           constraintGreaterThanOrEqualToAnchor:scrollView.leadingAnchor],
       [actionStackView.trailingAnchor
           constraintLessThanOrEqualToAnchor:scrollView.trailingAnchor],
-      self.buttonStackViewBottomVerticalConstraint,
       [actionStackView.centerXAnchor
           constraintEqualToAnchor:self.view.centerXAnchor],
       [actionStackView.widthAnchor
-          constraintLessThanOrEqualToConstant:kButtonMaxWidth],
+          constraintEqualToAnchor:stackView.widthAnchor],
+      [actionStackView.bottomAnchor
+          constraintLessThanOrEqualToAnchor:self.view.bottomAnchor
+                                   constant:-kActionsBottomMargin -
+                                            extraBottomMargin],
+      [actionStackView.bottomAnchor
+          constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide
+                                                .bottomAnchor
+                                   constant:-extraBottomMargin],
       lowPriorityWidthConstraint
     ]];
     scrollViewBottomAnchor = actionStackView.topAnchor;
@@ -294,23 +305,6 @@
 }
 
 - (void)updateViewConstraints {
-  CGFloat marginValue =
-      self.view.layoutMargins.left - self.view.safeAreaInsets.left;
-  if (!self.secondaryActionString) {
-    // Do not add margin padding between the bottom button and the containing
-    // view if the primary button is the bottom button to allow for more visual
-    // spacing between the content and the button. The secondary button has a
-    // transparent background so the visual spacing already exists.
-    self.buttonStackViewBottomVerticalConstraint.constant = -marginValue;
-  }
-
-  if (self.traitCollection.horizontalSizeClass ==
-      UIUserInterfaceSizeClassCompact) {
-    self.regularWidthConstraints.active = NO;
-  } else {
-    self.regularWidthConstraints.active = YES;
-  }
-
   BOOL isVerticalCompact =
       self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;
 
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
index 7ad6c61..0bbdebf 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -24,7 +24,6 @@
 #import "ios/chrome/credential_provider_extension/password_util.h"
 #import "ios/chrome/credential_provider_extension/reauthentication_handler.h"
 #import "ios/chrome/credential_provider_extension/ui/consent_coordinator.h"
-#import "ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_list_coordinator.h"
 #import "ios/chrome/credential_provider_extension/ui/feature_flags.h"
 #import "ios/chrome/credential_provider_extension/ui/stale_credentials_view_controller.h"
@@ -48,14 +47,8 @@
 // List coordinator that shows the list of passwords when started.
 @property(nonatomic, strong) CredentialListCoordinator* listCoordinator;
 
-// Legacy consent coordinator that shows a view requesting device auth in order
-// to enable the extension. Will be used when
-// IsCredentialProviderExtensionPromoEnabled() == NO.
-@property(nonatomic, strong) ConsentLegacyCoordinator* consentLegacyCoordinator;
-
 // Consent coordinator that shows a view requesting device auth in order to
-// enable the extension. Will be used when
-// IsCredentialProviderExtensionPromoEnabled() == YES.
+// enable the extension.
 @property(nonatomic, strong) ConsentCoordinator* consentCoordinator;
 
 // Date kept for ReauthenticationModule.
@@ -170,23 +163,10 @@
 }
 
 - (void)prepareInterfaceForExtensionConfiguration {
-  // Reset the consent if the extension was disabled and reenabled.
-  NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
-  [user_defaults
-      removeObjectForKey:kUserDefaultsCredentialProviderConsentVerified];
-  if (IsCredentialProviderExtensionPromoEnabled()) {
-    self.consentCoordinator = [[ConsentCoordinator alloc]
-        initWithBaseViewController:self
-                           context:self.extensionContext];
-    [self.consentCoordinator start];
-  } else {
-    self.consentLegacyCoordinator = [[ConsentLegacyCoordinator alloc]
-           initWithBaseViewController:self
-                              context:self.extensionContext
-              reauthenticationHandler:self.reauthenticationHandler
-        isInitialConfigurationRequest:YES];
-    [self.consentLegacyCoordinator start];
-  }
+  self.consentCoordinator = [[ConsentCoordinator alloc]
+      initWithBaseViewController:self
+                         context:self.extensionContext];
+  [self.consentCoordinator start];
 }
 
 #pragma mark - Properties
diff --git a/ios/chrome/credential_provider_extension/ui/BUILD.gn b/ios/chrome/credential_provider_extension/ui/BUILD.gn
index b4bec2b1..4f63cb4 100644
--- a/ios/chrome/credential_provider_extension/ui/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/ui/BUILD.gn
@@ -6,10 +6,6 @@
   sources = [
     "consent_coordinator.h",
     "consent_coordinator.mm",
-    "consent_legacy_coordinator.h",
-    "consent_legacy_coordinator.mm",
-    "consent_legacy_view_controller.h",
-    "consent_legacy_view_controller.mm",
     "consent_view_controller.h",
     "consent_view_controller.mm",
     "credential_details_consumer.h",
diff --git a/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h b/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h
deleted file mode 100644
index 16189dc..0000000
--- a/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h
+++ /dev/null
@@ -1,48 +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 IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_COORDINATOR_H_
-#define IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_COORDINATOR_H_
-
-#import <Foundation/Foundation.h>
-
-@class ASCredentialProviderExtensionContext;
-@class ConsentLegacyCoordinator;
-@class ReauthenticationHandler;
-@class UIViewController;
-
-@protocol ConsentLegacyCoordinatorDelegate <NSObject>
-
-// Called when the user accepts the consent shown by this coordinator.
-- (void)consentLegacyCoordinatorDidAcceptConsent:
-    (ConsentLegacyCoordinator*)consentCoordinator;
-
-@end
-
-@interface ConsentLegacyCoordinator : NSObject
-
-// Delegate to handle the coordinator.
-@property(nonatomic, weak) id<ConsentLegacyCoordinatorDelegate> delegate;
-
-// Default initializer. When the coordinator is started it will present on
-// |baseViewController|.
-- (instancetype)
-       initWithBaseViewController:(UIViewController*)baseViewController
-                          context:(ASCredentialProviderExtensionContext*)context
-          reauthenticationHandler:
-              (ReauthenticationHandler*)reauthenticationHandler
-    isInitialConfigurationRequest:(BOOL)isInitialConfigurationRequest
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-// Starts the consent screen.
-- (void)start;
-
-// Stops the consent screen.
-- (void)stop;
-
-@end
-
-#endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_COORDINATOR_H_
diff --git a/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.mm b/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.mm
deleted file mode 100644
index fa28de77..0000000
--- a/ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.mm
+++ /dev/null
@@ -1,125 +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.
-
-#import "ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h"
-
-#import <AuthenticationServices/AuthenticationServices.h>
-
-#include "ios/chrome/common/app_group/app_group_constants.h"
-#import "ios/chrome/common/credential_provider/constants.h"
-#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
-#import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
-#import "ios/chrome/credential_provider_extension/reauthentication_handler.h"
-#import "ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface ConsentLegacyCoordinator () <ConfirmationAlertActionHandler>
-
-// Base view controller from where |viewController| is presented.
-@property(nonatomic, weak) UIViewController* baseViewController;
-
-// The view controller of this coordinator.
-@property(nonatomic, strong) ConsentLegacyViewController* viewController;
-
-// Popover used to show learn more info, not nil when presented.
-@property(nonatomic, strong)
-    PopoverLabelViewController* learnMoreViewController;
-
-// The extension context for the credential provider.
-@property(nonatomic, weak) ASCredentialProviderExtensionContext* context;
-
-// Interface for |reauthenticationModule|, handling mostly the case when no
-// hardware for authentication is available.
-@property(nonatomic, weak) ReauthenticationHandler* reauthenticationHandler;
-
-// Indicates if the extension should finish after consent is given.
-@property(nonatomic) BOOL isInitialConfigurationRequest;
-
-@end
-
-@implementation ConsentLegacyCoordinator
-
-- (instancetype)
-       initWithBaseViewController:(UIViewController*)baseViewController
-                          context:(ASCredentialProviderExtensionContext*)context
-          reauthenticationHandler:
-              (ReauthenticationHandler*)reauthenticationHandler
-    isInitialConfigurationRequest:(BOOL)isInitialConfigurationRequest {
-  self = [super init];
-  if (self) {
-    _baseViewController = baseViewController;
-    _context = context;
-    _reauthenticationHandler = reauthenticationHandler;
-    _isInitialConfigurationRequest = isInitialConfigurationRequest;
-  }
-  return self;
-}
-
-- (void)start {
-  self.viewController = [[ConsentLegacyViewController alloc] init];
-  self.viewController.actionHandler = self;
-  self.viewController.modalInPresentation = YES;
-  self.viewController.modalPresentationStyle =
-      self.isInitialConfigurationRequest ? UIModalPresentationFullScreen
-                                         : UIModalPresentationAutomatic;
-  BOOL animated = !self.isInitialConfigurationRequest;
-  [self.baseViewController presentViewController:self.viewController
-                                        animated:animated
-                                      completion:nil];
-}
-
-- (void)stop {
-  [self.viewController.presentingViewController
-      dismissViewControllerAnimated:YES
-                         completion:nil];
-  self.viewController = nil;
-}
-
-#pragma mark - ConfirmationAlertActionHandler
-
-- (void)confirmationAlertDismissAction {
-  NSError* error =
-      [[NSError alloc] initWithDomain:ASExtensionErrorDomain
-                                 code:ASExtensionErrorCodeUserCanceled
-                             userInfo:nil];
-  [self.context cancelRequestWithError:error];
-}
-
-- (void)confirmationAlertPrimaryAction {
-  [self.reauthenticationHandler
-      verifyUserWithCompletionHandler:^(ReauthenticationResult result) {
-        if (result != ReauthenticationResult::kFailure) {
-          NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
-          [user_defaults
-              setBool:YES
-               forKey:kUserDefaultsCredentialProviderConsentVerified];
-          if (self.isInitialConfigurationRequest) {
-            [self.context completeExtensionConfigurationRequest];
-          } else {
-            [self.delegate consentLegacyCoordinatorDidAcceptConsent:self];
-          }
-        }
-      }
-      presentReminderOnViewController:self.viewController];
-}
-
-- (void)confirmationAlertLearnMoreAction {
-  NSString* message =
-      NSLocalizedString(@"IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_MORE_INFO_STRING",
-                        @"The information provided in the consent popover.");
-  self.learnMoreViewController =
-      [[PopoverLabelViewController alloc] initWithMessage:message];
-  [self.viewController presentViewController:self.learnMoreViewController
-                                    animated:YES
-                                  completion:nil];
-  self.learnMoreViewController.popoverPresentationController.barButtonItem =
-      self.viewController.helpButton;
-  self.learnMoreViewController.popoverPresentationController
-      .permittedArrowDirections = UIPopoverArrowDirectionUp;
-}
-
-@end
diff --git a/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.h b/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.h
deleted file mode 100644
index 052f0a6e..0000000
--- a/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.h
+++ /dev/null
@@ -1,13 +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 IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_VIEW_CONTROLLER_H_
-#define IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_VIEW_CONTROLLER_H_
-
-#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h"
-
-@interface ConsentLegacyViewController : ConfirmationAlertViewController
-@end
-
-#endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_CONSENT_LEGACY_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.mm b/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.mm
deleted file mode 100644
index 287e5ce..0000000
--- a/ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.mm
+++ /dev/null
@@ -1,44 +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.
-
-#import "ios/chrome/credential_provider_extension/ui/consent_legacy_view_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-constexpr CGFloat kStackViewSpacingAfterIllustration = 37;
-}  // namespace
-
-@implementation ConsentLegacyViewController
-
-#pragma mark - Public
-
-- (void)loadView {
-  self.image = [UIImage imageNamed:@"consent_illustration"];
-  self.customSpacingAfterImage = kStackViewSpacingAfterIllustration;
-
-  self.helpButtonAvailable = YES;
-  self.helpButtonAccessibilityLabel = NSLocalizedString(
-      @"IDS_IOS_CREDENTIAL_PROVIDER_HELP_ACCESSIBILITY_LABEL", @"Help.");
-
-  NSString* titleString =
-      NSLocalizedString(@"IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_TITLE_LEGACY",
-                        @"The title in the consent screen.");
-  NSString* subtitleString =
-      NSLocalizedString(@"IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_SUBTITLE_LEGACY",
-                        @"The subtitle in the consent screen.");
-  NSString* primaryActionString = NSLocalizedString(
-      @"IDS_IOS_CREDENTIAL_PROVIDER_CONSENT_ENABLE_BUTTON_TITLE_LEGACY",
-      @"The primary action title in the consent screen. Used to explicitly "
-      @"enable the extension.");
-  self.titleString = titleString;
-  self.subtitleString = subtitleString;
-  self.primaryActionString = primaryActionString;
-  self.dismissBarButtonSystemItem = UIBarButtonSystemItemCancel;
-  [super loadView];
-}
-
-@end
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
index 9800b9a4..e81f5a4de 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
 #import "ios/chrome/credential_provider_extension/password_util.h"
 #import "ios/chrome/credential_provider_extension/reauthentication_handler.h"
-#import "ios/chrome/credential_provider_extension/ui/consent_legacy_coordinator.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_details_consumer.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_details_view_controller.h"
 #import "ios/chrome/credential_provider_extension/ui/credential_list_mediator.h"
@@ -27,7 +26,6 @@
 #endif
 
 @interface CredentialListCoordinator () <ConfirmationAlertActionHandler,
-                                         ConsentLegacyCoordinatorDelegate,
                                          CredentialListUIHandler,
                                          CredentialDetailsConsumerDelegate>
 
@@ -50,11 +48,6 @@
 @property(nonatomic, strong)
     NSArray<ASCredentialServiceIdentifier*>* serviceIdentifiers;
 
-// Legacy consent coordinator that shows a view requesting device auth in order
-// to enable the extension. Will be used when
-// IsCredentialProviderExtensionPromoEnabled() == NO.
-@property(nonatomic, strong) ConsentLegacyCoordinator* consentLegacyCoordinator;
-
 // Coordinator that shows a view for the user to create a new password.
 @property(nonatomic, strong) NewPasswordCoordinator* createPasswordCoordinator;
 
@@ -102,25 +95,7 @@
   [self.baseViewController presentViewController:self.viewController
                                         animated:NO
                                       completion:nil];
-
-  if (IsCredentialProviderExtensionPromoEnabled()) {
-    [self.mediator fetchCredentials];
-  } else {
-    NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
-    BOOL isConsentGiven = [user_defaults
-        boolForKey:kUserDefaultsCredentialProviderConsentVerified];
-    if (isConsentGiven) {
-      [self.mediator fetchCredentials];
-    } else {
-      self.consentLegacyCoordinator = [[ConsentLegacyCoordinator alloc]
-             initWithBaseViewController:self.viewController
-                                context:self.context
-                reauthenticationHandler:self.reauthenticationHandler
-          isInitialConfigurationRequest:NO];
-      self.consentLegacyCoordinator.delegate = self;
-      [self.consentLegacyCoordinator start];
-    }
-  }
+  [self.mediator fetchCredentials];
 }
 
 - (void)stop {
@@ -131,14 +106,6 @@
   self.mediator = nil;
 }
 
-#pragma mark - ConsentLegacyCoordinatorDelegate
-
-- (void)consentLegacyCoordinatorDidAcceptConsent:
-    (ConsentLegacyCoordinator*)consentCoordinator {
-  [consentCoordinator stop];
-  [self.mediator fetchCredentials];
-}
-
 #pragma mark - CredentialListUIHandler
 
 - (void)showEmptyCredentials {
diff --git a/ios/chrome/credential_provider_extension/ui/feature_flags.h b/ios/chrome/credential_provider_extension/ui/feature_flags.h
index d18153f..6b63ce5bf 100644
--- a/ios/chrome/credential_provider_extension/ui/feature_flags.h
+++ b/ios/chrome/credential_provider_extension/ui/feature_flags.h
@@ -10,8 +10,6 @@
 // Whether password creation is enabled for this user by preference.
 BOOL IsPasswordCreationUserEnabled();
 
-BOOL IsCredentialProviderExtensionPromoEnabled();
-
 // Whether the password manager branding UI update feature is enabled.
 BOOL IsPasswordManagerBrandingUpdateEnable();
 
diff --git a/ios/chrome/credential_provider_extension/ui/feature_flags.mm b/ios/chrome/credential_provider_extension/ui/feature_flags.mm
index 94aa405b..f21ffa9 100644
--- a/ios/chrome/credential_provider_extension/ui/feature_flags.mm
+++ b/ios/chrome/credential_provider_extension/ui/feature_flags.mm
@@ -19,17 +19,6 @@
       boolValue];
 }
 
-BOOL IsCredentialProviderExtensionPromoEnabled() {
-  NSDictionary* allFeatures = [app_group::GetGroupUserDefaults()
-      objectForKey:app_group::kChromeExtensionFieldTrialPreference];
-  NSDictionary* featureData = allFeatures[@"CredentialProviderExtensionPromo"];
-  if (!featureData || kCredentialProviderExtensionPromoFeatureVersion !=
-                          [featureData[kFieldTrialVersionKey] intValue]) {
-    return NO;
-  }
-  return [featureData[kFieldTrialValueKey] boolValue];
-}
-
 BOOL IsPasswordManagerBrandingUpdateEnable() {
   NSDictionary* allFeatures = [app_group::GetGroupUserDefaults()
       objectForKey:app_group::kChromeExtensionFieldTrialPreference];
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 2b9666b7..cd5ca15 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -559,38 +559,14 @@
   if (!base::FeatureList::IsEnabled(kIOSOmniboxUpdatedPopupUI)) {
     return grey_kindOfClassName(@"OmniboxPopupRowCell");
   } else {
-    if (@available(iOS 15, *)) {
-      return grey_allOf(
-          grey_kindOfClassName(@"SwiftUI.ListTableViewCell"),
-          grey_ancestor(grey_kindOfClassName(@"OmniboxPopupContainerView")),
-          nil);
-    } else {
-      return grey_allOf(
-          grey_kindOfClassName(@"SwiftUI.ListCoreCellHost"),
-          grey_ancestor(grey_kindOfClassName(@"OmniboxPopupContainerView")),
-          nil);
-    }
+    return grey_allOf(
+        grey_kindOfClassName(@"SwiftUI.AccessibilityNode"),
+        grey_ancestor(grey_kindOfClassName(@"OmniboxPopupContainerView")), nil);
   }
 }
 
 + (id<GREYMatcher>)omniboxPopupList {
-  if (!base::FeatureList::IsEnabled(kIOSOmniboxUpdatedPopupUI)) {
-    return grey_accessibilityID(kOmniboxPopupTableViewAccessibilityIdentifier);
-  } else {
-    if (@available(iOS 15, *)) {
-      return grey_accessibilityID(
-          kOmniboxPopupTableViewAccessibilityIdentifier);
-    } else {
-      // Implementation detail: the list is the closest ancestor of all popup.
-      // rows. It is the unique element with popup rows as descendants but no
-      // descendant having popup rows as descendants.
-      return grey_allOf(
-          grey_descendant([ChromeMatchersAppInterface omniboxPopupRow]),
-          grey_not(grey_descendant(
-              grey_descendant([ChromeMatchersAppInterface omniboxPopupRow]))),
-          nil);
-    }
-  }
+  return grey_accessibilityID(kOmniboxPopupTableViewAccessibilityIdentifier);
 }
 
 + (id<GREYMatcher>)OKButton {
diff --git a/ios/chrome/test/providers/discover_feed/test_discover_feed.mm b/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
index 8b29485..eaedc63 100644
--- a/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
+++ b/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
@@ -33,6 +33,8 @@
   void UpdateTheme() final {}
   void RefreshFeedIfNeeded() final {}
   void RefreshFeed() final {}
+  BOOL GetFollowingFeedHasUnseenContent() final { return NO; }
+  void SetFollowingFeedContentSeen() final {}
 };
 
 }  // anonymous namespace
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 834d50d..1890f80 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e7e27a287baae04fc1c1f604ed7221a56dde882a
\ No newline at end of file
+42c1d86956222a7dbc36054d79b1dcfc062ab9c1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index c6f85f3..c61936a 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0d8f70b00049b98912b0a03ac860cc87c4e77a0c
\ No newline at end of file
+a74ef26523f4d5ad79b47b949c750a40f7d2ece9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index c4fdb47d..05f194a0 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0eddde4c4bd895c3c0bfb4ad6d2eddfa0e92ab9c
\ No newline at end of file
+e3e4587efc83cab22ee371711ca4bd6e244bcf3a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 32522a7..1e639d6 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a12b2ef6a2ad8b68dcf1298c0d3ce3e1ca7d4ad2
\ No newline at end of file
+06693307966e8d1d560fa685a17a4cb175cc0067
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index e7c27b62..b9bbcf89 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-1c1ef2982c3621fc8ead754e6c11dd215e63fdf6
\ No newline at end of file
+4ea49fb7d8d6c6dd2e74d2589d0cd134d52494f1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 9a4730fa..50a1d84d 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-4b97075b3a17bbd84904b5203073901a5ab9ef66
\ No newline at end of file
+d3a61aeb200cf92b3501ce2a6d2455645511489c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 73917cb..e61646ba 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ea90d289390812e70c7ce9d5c256f0f476a951cd
\ No newline at end of file
+e40edc33867226eb7f4fe4fca16341da9f65bdba
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 3769a65..213330a 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-319f7f0b8b33d813877e5df7f4363a624678bb5a
\ No newline at end of file
+929f68afddbe50f2605c859128b01a9913ab949a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 751b2dc7..f26d8e32 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e180938a0fbb60967a45af7a153f9bc086a782b8
\ No newline at end of file
+96bfbaf4040ff54face02d03cd0964b79ae3a5ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 6f3e5cca..8a0d828 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-08cdcde2367d6a0c71673b03194455035056d912
\ No newline at end of file
+92616378daea70da22a9f8ecefc11b76ffcc527f
\ No newline at end of file
diff --git a/ios/showcase/core/showcase_model.mm b/ios/showcase/core/showcase_model.mm
index 25d405e..f62b2ce 100644
--- a/ios/showcase/core/showcase_model.mm
+++ b/ios/showcase/core/showcase_model.mm
@@ -55,11 +55,6 @@
       showcase::kUseCaseKey : @"Credential Provider Consent UI",
     },
     @{
-      showcase::kClassForDisplayKey : @"ConsentLegacyViewController",
-      showcase::kClassForInstantiationKey : @"ConsentLegacyViewController",
-      showcase::kUseCaseKey : @"Legacy Credential Provider Consent UI",
-    },
-    @{
       showcase::kClassForDisplayKey : @"EnterpriseLoadScreenViewController",
       showcase::
       kClassForInstantiationKey : @"EnterpriseLoadScreenViewController",
diff --git a/ios/showcase/credential_provider/credential_provider_egtest.mm b/ios/showcase/credential_provider/credential_provider_egtest.mm
index ab4b0670..bffc763 100644
--- a/ios/showcase/credential_provider/credential_provider_egtest.mm
+++ b/ios/showcase/credential_provider/credential_provider_egtest.mm
@@ -75,22 +75,6 @@
   showcase_utils::Close();
 }
 
-// Tests ConsentLegacyViewController.
-- (void)testLegacyConsentScreen {
-  showcase_utils::Open(@"ConsentLegacyViewController");
-  [[EarlGrey selectElementWithMatcher:ConfirmationAlertTitleMatcher()]
-      assertWithMatcher:grey_interactable()];
-  [[EarlGrey selectElementWithMatcher:ConfirmationAlertSubtitleMatcher()]
-      assertWithMatcher:grey_interactable()];
-  [[EarlGrey
-      selectElementWithMatcher:ConfirmationAlertPrimaryActionButtonMatcher()]
-      assertWithMatcher:grey_interactable()];
-  [[EarlGrey selectElementWithMatcher:ConfirmationAlertMoreInfoButtonMatcher()]
-      assertWithMatcher:grey_interactable()];
-
-  showcase_utils::Close();
-}
-
 // Tests ConsentViewController.
 - (void)testEmptyCredentialsScreen {
   showcase_utils::Open(@"EmptyCredentialsViewController");
diff --git a/ios/third_party/gtx/BUILD.gn b/ios/third_party/gtx/BUILD.gn
index a5e2e140..7d8cc53 100644
--- a/ios/third_party/gtx/BUILD.gn
+++ b/ios/third_party/gtx/BUILD.gn
@@ -10,6 +10,14 @@
   include_dirs = [ "src/Classes" ]
 }
 
+# GTXiLibCore.m uses `-keyWindow` method which is deprecated since iOS 13.0.
+# Disable the warning until the library is updated to a version thats do not
+# use the deprecated method. See https://crbug.com/1237312 for progress.
+config("disable_deprecated_declarations") {
+  cflags_objcc = [ "-Wno-deprecated-declarations" ]
+  cflags_objc = cflags_objcc
+}
+
 proto_library("proto") {
   sources = [ "src/OOPClasses/Protos/gtx.proto" ]
   cc_generator_options = "lite"
@@ -223,5 +231,6 @@
   configs += [
     "//build/config/compiler:enable_arc",
     "//build/config/gcc:symbol_visibility_default",
+    ":disable_deprecated_declarations",
   ]
 }
diff --git a/media/base/android/java/src/test/org/chromium/media/MediaFormatBuilderTest.java b/media/base/android/java/src/test/org/chromium/media/MediaFormatBuilderTest.java
index 6bad4212..e7f3b0b 100644
--- a/media/base/android/java/src/test/org/chromium/media/MediaFormatBuilderTest.java
+++ b/media/base/android/java/src/test/org/chromium/media/MediaFormatBuilderTest.java
@@ -11,6 +11,7 @@
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaFormat;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,6 +66,11 @@
         ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application);
     }
 
+    @After
+    public void tearDown() {
+        ContextUtils.clearApplicationContextForTests();
+    }
+
     @Test
     public void testCreateVideoDecoderWithNoCodecSpecificData() {
         byte[][] csds = {};
diff --git a/media/base/video_frame_metadata.cc b/media/base/video_frame_metadata.cc
index 47d0cd1..3a958605 100644
--- a/media/base/video_frame_metadata.cc
+++ b/media/base/video_frame_metadata.cc
@@ -50,6 +50,7 @@
   MERGE_VALUE_FIELD(dcomp_surface, metadata_source);
   MERGE_VALUE_FIELD(protected_video, metadata_source);
   MERGE_VALUE_FIELD(hw_protected, metadata_source);
+  MERGE_VALUE_FIELD(is_webgpu_compatible, metadata_source);
 #if BUILDFLAG(USE_VAAPI)
   MERGE_OPTIONAL_FIELD(hw_va_protected_session_id, metadata_source);
 #endif
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h
index e74c5753..0f7560a 100644
--- a/media/base/video_frame_metadata.h
+++ b/media/base/video_frame_metadata.h
@@ -148,6 +148,10 @@
   // PROTECTED_VIDEO is also set to true.
   bool hw_protected = false;
 
+  // This video frame's shared image backing can support zero-copy WebGPU
+  // import.
+  bool is_webgpu_compatible = false;
+
 #if BUILDFLAG(USE_VAAPI)
   // The ID of the VA-API protected session used to decode this frame, if
   // applicable. The proper type is VAProtectedSessionID. However, in order to
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter.cc b/media/gpu/chromeos/mailbox_video_frame_converter.cc
index d6a482e..65903ef 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter.cc
+++ b/media/gpu/chromeos/mailbox_video_frame_converter.cc
@@ -84,7 +84,8 @@
 std::unique_ptr<VideoFrameConverter> MailboxVideoFrameConverter::Create(
     UnwrapFrameCB unwrap_frame_cb,
     scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
-    GetCommandBufferStubCB get_stub_cb) {
+    GetCommandBufferStubCB get_stub_cb,
+    bool enable_unsafe_webgpu) {
   DCHECK(unwrap_frame_cb);
   DCHECK(gpu_task_runner);
   DCHECK(get_stub_cb);
@@ -101,16 +102,18 @@
 
   return base::WrapUnique<VideoFrameConverter>(new MailboxVideoFrameConverter(
       std::move(unwrap_frame_cb), std::move(gpu_task_runner),
-      get_gpu_channel_cb));
+      get_gpu_channel_cb, enable_unsafe_webgpu));
 }
 
 MailboxVideoFrameConverter::MailboxVideoFrameConverter(
     UnwrapFrameCB unwrap_frame_cb,
     scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
-    GetGpuChannelCB get_gpu_channel_cb)
+    GetGpuChannelCB get_gpu_channel_cb,
+    bool enable_unsafe_webgpu)
     : unwrap_frame_cb_(std::move(unwrap_frame_cb)),
       gpu_task_runner_(std::move(gpu_task_runner)),
-      get_gpu_channel_cb_(get_gpu_channel_cb) {
+      get_gpu_channel_cb_(get_gpu_channel_cb),
+      enable_unsafe_webgpu_(enable_unsafe_webgpu) {
   DVLOGF(2);
 
   parent_weak_this_ = parent_weak_this_factory_.GetWeakPtr();
@@ -243,6 +246,8 @@
   mailbox_frame->set_metadata(frame->metadata());
   mailbox_frame->set_ycbcr_info(frame->ycbcr_info());
   mailbox_frame->metadata().read_lock_fences_enabled = true;
+  mailbox_frame->metadata().is_webgpu_compatible =
+      enable_unsafe_webgpu_ && frame->metadata().is_webgpu_compatible;
 
   output_cb_.Run(mailbox_frame);
 }
@@ -354,8 +359,11 @@
 
   // The allocated SharedImages should be usable for the (Display) compositor
   // and, potentially, for overlays (Scanout).
-  const uint32_t shared_image_usage =
+  uint32_t shared_image_usage =
       gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
+  if (enable_unsafe_webgpu_ && video_frame->metadata().is_webgpu_compatible)
+    shared_image_usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU;
+
   const bool success = shared_image_stub->CreateSharedImage(
       mailbox, gpu::kPlatformVideoFramePoolClientId,
       std::move(gpu_memory_buffer_handle), *buffer_format,
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter.h b/media/gpu/chromeos/mailbox_video_frame_converter.h
index be77161..6a28456 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter.h
+++ b/media/gpu/chromeos/mailbox_video_frame_converter.h
@@ -44,12 +44,14 @@
   // Creates a MailboxVideoFrameConverter instance. The callers will send
   // wrapped VideoFrames to ConvertFrame(), |unwrap_frame_cb| is the callback
   // used to get the original, unwrapped, VideoFrame. |gpu_task_runner| is the
-  // task runner of the GPU main thread. Returns nullptr if any argument is
-  // invalid.
+  // task runner of the GPU main thread. |enable_unsafe_webgpu| hints whether
+  // to create SharedImage of SHARED_IMAGE_USAGE_WEBGPU for VideoFrame. Returns
+  // nullptr if any argument is invalid.
   static std::unique_ptr<VideoFrameConverter> Create(
       UnwrapFrameCB unwrap_frame_cb,
       scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
-      GetCommandBufferStubCB get_stub_cb);
+      GetCommandBufferStubCB get_stub_cb,
+      bool enable_unsafe_webgpu);
 
   MailboxVideoFrameConverter(const MailboxVideoFrameConverter&) = delete;
   MailboxVideoFrameConverter& operator=(const MailboxVideoFrameConverter&) =
@@ -76,7 +78,8 @@
   MailboxVideoFrameConverter(
       UnwrapFrameCB unwrap_frame_cb,
       scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
-      GetGpuChannelCB get_gpu_channel_cb);
+      GetGpuChannelCB get_gpu_channel_cb,
+      bool enable_unsafe_webgpu);
   // Destructor runs on the GPU main thread.
   ~MailboxVideoFrameConverter() override;
   void Destroy() override;
@@ -164,6 +167,8 @@
   base::queue<std::pair<scoped_refptr<VideoFrame>, UniqueID>>
       input_frame_queue_;
 
+  const bool enable_unsafe_webgpu_;
+
   // The weak pointer of this, bound to |parent_task_runner_|.
   // Used at the VideoFrame destruction callback.
   base::WeakPtr<MailboxVideoFrameConverter> parent_weak_this_;
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
index bb4a179..def6217 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
+++ b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
@@ -32,7 +32,8 @@
       : converter_(new MailboxVideoFrameConverter(
             base::BindRepeating(&UnwrapVideoFrame),
             base::ThreadTaskRunnerHandle::Get(),
-            base::BindRepeating(&GetGpuChannel))) {}
+            base::BindRepeating(&GetGpuChannel),
+            /*enable_unsafe_webgpu=*/false)) {}
 
   MailboxVideoFrameConverterTest(const MailboxVideoFrameConverterTest&) =
       delete;
diff --git a/media/gpu/chromeos/platform_video_frame_utils.cc b/media/gpu/chromeos/platform_video_frame_utils.cc
index 9aa74fa..57a4a064 100644
--- a/media/gpu/chromeos/platform_video_frame_utils.cc
+++ b/media/gpu/chromeos/platform_video_frame_utils.cc
@@ -226,6 +226,9 @@
   if (gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP)
     return nullptr;
 
+  const bool supports_zero_copy_webgpu_import =
+      gmb_handle.native_pixmap_handle.supports_zero_copy_webgpu_import;
+
   auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
   DCHECK(buffer_format);
   gpu::GpuMemoryBufferSupport support;
@@ -244,6 +247,11 @@
 
   if (frame)
     frame->AddDestructionObserver(destroy_cb.Release());
+
+  // We only support importing non-DISJOINT multi-planar GbmBuffer right now.
+  // TODO(crbug.com/1258986): Add DISJOINT support.
+  frame->metadata().is_webgpu_compatible = supports_zero_copy_webgpu_import;
+
   return frame;
 }
 
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 047c5312..3443490 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -337,6 +337,8 @@
 
   bool hw_protected;
 
+  bool is_webgpu_compatible;
+
   mojo_base.mojom.UnguessableToken? overlay_plane_id;
 
   bool power_efficient;
diff --git a/media/mojo/mojom/video_frame_metadata_mojom_traits.cc b/media/mojo/mojom/video_frame_metadata_mojom_traits.cc
index 275023a..0187c8c3 100644
--- a/media/mojo/mojom/video_frame_metadata_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_metadata_mojom_traits.cc
@@ -42,6 +42,7 @@
   output->wants_promotion_hint = input.wants_promotion_hint();
   output->protected_video = input.protected_video();
   output->hw_protected = input.hw_protected();
+  output->is_webgpu_compatible = input.is_webgpu_compatible();
   output->power_efficient = input.power_efficient();
   output->read_lock_fences_enabled = input.read_lock_fences_enabled();
   output->interactive_content = input.interactive_content();
diff --git a/media/mojo/mojom/video_frame_metadata_mojom_traits.h b/media/mojo/mojom/video_frame_metadata_mojom_traits.h
index 3f24664..06578553 100644
--- a/media/mojo/mojom/video_frame_metadata_mojom_traits.h
+++ b/media/mojo/mojom/video_frame_metadata_mojom_traits.h
@@ -55,6 +55,10 @@
     return input.hw_protected;
   }
 
+  static bool is_webgpu_compatible(const media::VideoFrameMetadata& input) {
+    return input.is_webgpu_compatible;
+  }
+
   static bool power_efficient(const media::VideoFrameMetadata& input) {
     return input.power_efficient;
   }
diff --git a/media/mojo/mojom/video_frame_metadata_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_metadata_mojom_traits_unittest.cc
index 6874fb4..7c646430 100644
--- a/media/mojo/mojom/video_frame_metadata_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_frame_metadata_mojom_traits_unittest.cc
@@ -72,6 +72,7 @@
   EXPECT_FALSE(metadata_out.wants_promotion_hint);
   EXPECT_FALSE(metadata_out.protected_video);
   EXPECT_FALSE(metadata_out.hw_protected);
+  EXPECT_FALSE(metadata_out.is_webgpu_compatible);
   EXPECT_FALSE(metadata_out.power_efficient);
   EXPECT_FALSE(metadata_out.read_lock_fences_enabled);
   EXPECT_FALSE(metadata_out.interactive_content);
@@ -118,6 +119,7 @@
   metadata_in.wants_promotion_hint = true;
   metadata_in.protected_video = true;
   metadata_in.hw_protected = true;
+  metadata_in.is_webgpu_compatible = true;
   metadata_in.power_efficient = true;
   metadata_in.read_lock_fences_enabled = true;
   metadata_in.interactive_content = true;
@@ -163,6 +165,8 @@
             metadata_out.wants_promotion_hint);
   EXPECT_EQ(metadata_in.protected_video, metadata_out.protected_video);
   EXPECT_EQ(metadata_in.hw_protected, metadata_out.hw_protected);
+  EXPECT_EQ(metadata_in.is_webgpu_compatible,
+            metadata_out.is_webgpu_compatible);
   EXPECT_EQ(metadata_in.power_efficient, metadata_out.power_efficient);
   EXPECT_EQ(metadata_in.read_lock_fences_enabled,
             metadata_out.read_lock_fences_enabled);
diff --git a/media/mojo/services/gpu_mojo_media_client_cros.cc b/media/mojo/services/gpu_mojo_media_client_cros.cc
index 6bc17e1d..1f5b8d5 100644
--- a/media/mojo/services/gpu_mojo_media_client_cros.cc
+++ b/media/mojo/services/gpu_mojo_media_client_cros.cc
@@ -111,7 +111,8 @@
       auto frame_converter = MailboxVideoFrameConverter::Create(
           base::BindRepeating(&PlatformVideoFramePool::UnwrapFrame,
                               base::Unretained(frame_pool.get())),
-          traits.gpu_task_runner, traits.get_command_buffer_stub_cb);
+          traits.gpu_task_runner, traits.get_command_buffer_stub_cb,
+          traits.gpu_preferences.enable_unsafe_webgpu);
       return VideoDecoderPipeline::Create(
           traits.task_runner, std::move(frame_pool), std::move(frame_converter),
           traits.media_log->Clone());
diff --git a/mojo/public/cpp/bindings/pending_remote.h b/mojo/public/cpp/bindings/pending_remote.h
index 04335443..eb63c2c 100644
--- a/mojo/public/cpp/bindings/pending_remote.h
+++ b/mojo/public/cpp/bindings/pending_remote.h
@@ -12,7 +12,12 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/bindings/disconnect_reason.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
 #include "mojo/public/cpp/bindings/lib/pending_remote_state.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace mojo {
@@ -92,6 +97,21 @@
   // eventually detect that its remote caller is gone.
   void reset() { state_.reset(); }
 
+  // Like above but provides a reason for the disconnection.
+  void ResetWithReason(uint32_t reason, const std::string& description) {
+    CHECK(is_valid()) << "Cannot send reset reason to an invalid handle.";
+
+    Message message =
+        PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
+            kPrimaryInterfaceId, DisconnectReason(reason, description));
+    MojoResult result =
+        WriteMessageNew(state_.pipe.get(), message.TakeMojoMessage(),
+                        MOJO_WRITE_MESSAGE_FLAG_NONE);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+
+    reset();
+  }
+
   // Takes ownership of this PendingRemote's message pipe handle. After this
   // call, the PendingRemote is no longer in a valid state and can no longer be
   // used to bind a Remote.
diff --git a/mojo/public/cpp/bindings/tests/receiver_unittest.cc b/mojo/public/cpp/bindings/tests/receiver_unittest.cc
index 17efb6be..2487228 100644
--- a/mojo/public/cpp/bindings/tests/receiver_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/receiver_unittest.cc
@@ -468,6 +468,24 @@
   run_loop.Run();
 }
 
+TEST_P(ReceiverTest, PendingRemoteResetWithReason) {
+  ServiceImpl impl;
+  Receiver<sample::Service> receiver(&impl);
+  PendingRemote<sample::Service> pending_remote =
+      receiver.BindNewPipeAndPassRemote();
+
+  base::RunLoop run_loop;
+  receiver.set_disconnect_with_reason_handler(base::BindLambdaForTesting(
+      [&](uint32_t custom_reason, const std::string& description) {
+        EXPECT_EQ(1234u, custom_reason);
+        EXPECT_EQ("hello", description);
+        run_loop.Quit();
+      }));
+
+  pending_remote.ResetWithReason(1234u, "hello");
+  run_loop.Run();
+}
+
 template <typename T>
 struct WeakPtrImplRefTraits {
   using PointerType = base::WeakPtr<T>;
diff --git a/ppapi/native_client/src/untrusted/pnacl_support_extension/BUILD.gn b/ppapi/native_client/src/untrusted/pnacl_support_extension/BUILD.gn
index aad23b2..b65add1 100644
--- a/ppapi/native_client/src/untrusted/pnacl_support_extension/BUILD.gn
+++ b/ppapi/native_client/src/untrusted/pnacl_support_extension/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//components/nacl/features.gni")
+import("//components/nacl/target_cpu.gni")
 
 assert(enable_nacl)
 
@@ -28,9 +29,10 @@
   # flags (so IRT compiler flags should be fine).
   shim_toolchain_base = "//build/toolchain/nacl:irt_"
 
-  shim_target_tc_label = "$shim_target_label($shim_toolchain_base$target_cpu)"
+  shim_target_tc_label =
+      "$shim_target_label($shim_toolchain_base$nacl_target_cpu)"
   deps = [ shim_target_tc_label ]
-  shim_cpu = target_cpu
+  shim_cpu = nacl_target_cpu
 
   output_prefix = "$root_out_dir/pnacl/pnacl_public_"
   outputs = [ "${output_prefix}pnacl_json" ]
@@ -47,16 +49,16 @@
     "pnacl_sz_nexe",
   ]
 
-  if (target_cpu == "arm") {
+  if (nacl_target_cpu == "arm") {
     output_cpu = "arm"
-  } else if (target_cpu == "mipsel") {
+  } else if (nacl_target_cpu == "mipsel") {
     output_cpu = "mips32"
-  } else if (target_cpu == "x64") {
+  } else if (nacl_target_cpu == "x64") {
     output_cpu = "x86_64"
-  } else if (target_cpu == "x86") {
+  } else if (nacl_target_cpu == "x86") {
     output_cpu = "x86_32"
   } else {
-    assert(false, "unhandled target_cpu")
+    assert(false, "unhandled nacl_target_cpu")
   }
 
   foreach(output_elem, outputs_from_toolchain) {
@@ -114,7 +116,7 @@
     rebase_path("$root_out_dir/pnacl", root_build_dir),
 
     "--target_arch",
-    target_cpu,
+    nacl_target_cpu,
 
     "--info_template_path",
     rebase_path("//native_client/pnacl/driver/pnacl_info_template.json",
diff --git a/remoting/test/fake_network_manager.cc b/remoting/test/fake_network_manager.cc
index 125c57c..cacbde8c3 100644
--- a/remoting/test/fake_network_manager.cc
+++ b/remoting/test/fake_network_manager.cc
@@ -40,6 +40,14 @@
   networks->push_back(network_.get());
 }
 
+std::vector<const rtc::Network*> FakeNetworkManager::GetNetworks() const {
+  return {network_.get()};
+}
+
+std::vector<const rtc::Network*> FakeNetworkManager::GetAnyAddressNetworks() {
+  return {};
+}
+
 void FakeNetworkManager::SendNetworksChangedSignal() {
   SignalNetworksChanged();
 }
diff --git a/remoting/test/fake_network_manager.h b/remoting/test/fake_network_manager.h
index 2dda87c..e0278ce 100644
--- a/remoting/test/fake_network_manager.h
+++ b/remoting/test/fake_network_manager.h
@@ -23,6 +23,8 @@
   void StartUpdating() override;
   void StopUpdating() override;
   void GetNetworks(NetworkList* networks) const override;
+  std::vector<const rtc::Network*> GetNetworks() const override;
+  std::vector<const rtc::Network*> GetAnyAddressNetworks() override;
 
  protected:
   void SendNetworksChangedSignal();
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index de7c7793..6cbfd9d 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -205,6 +205,16 @@
 // Temporarily insulate Chrome pixel tests from Skia LOD bias change on GPU.
 #define SK_USE_LEGACY_MIPMAP_LOD_BIAS
 
+// Temporarily insulate Chrome pixel tests from Skia kStrict_SrcRectConstraint
+// change to disable mipmapping.
+#define SK_LEGACY_ALLOW_STRICT_CONSTRAINT_MIPMAPPING
+
+// Many Chrome tests use kStrict_SrcRectConstraint where the src subset rect
+// actually contains the entire image. This prevents mipmap disablement in those
+// cases until Chrome codepaths can be updated to identify this case and either
+// pass kFast_SrcRectConstraint or use drawImage instead of drawImageRect.
+#define SK_DISABLE_STRICT_CONSTRAINT_FOR_ENTIRE_IMAGE
+
 // Temporarily insulate Chrome pixel tests from Skia's edge AA -> non-AA checks.
 #define SK_USE_LEGACY_EDGE_AA_DOWNGRADE
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 408d154c..950ba68 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6277,6 +6277,25 @@
             ]
         }
     ],
+    "ScrollUnification": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ScrollUnification"
+                    ]
+                }
+            ]
+        }
+    ],
     "SendTabToSelfV2": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 9723fe1..2f3624f 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -4,7 +4,7 @@
 License: Apache 2.0
 License File: LICENSE
 Version: 0
-Revision: 3204cc0625230e9876f0310a6dea0014210ab325
+Revision: 6f43f5bb398b6685575b36874e36cf1695734df1
 Security Critical: yes
 
 Description:
diff --git a/third_party/abseil-cpp/absl/base/BUILD.bazel b/third_party/abseil-cpp/absl/base/BUILD.bazel
index 85a9488..b37c21d 100644
--- a/third_party/abseil-cpp/absl/base/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/base/BUILD.bazel
@@ -151,7 +151,9 @@
         "internal/direct_mmap.h",
         "internal/low_level_alloc.h",
     ],
-    copts = ABSL_DEFAULT_COPTS,
+    copts = ABSL_DEFAULT_COPTS + select({
+        "//conditions:default": [],
+    }),
     linkopts = select({
         "//absl:msvc_compiler": [],
         "//absl:clang-cl_compiler": [],
diff --git a/third_party/abseil-cpp/absl/base/CMakeLists.txt b/third_party/abseil-cpp/absl/base/CMakeLists.txt
index c7233cb..7e600f0b 100644
--- a/third_party/abseil-cpp/absl/base/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/base/CMakeLists.txt
@@ -16,6 +16,7 @@
 
 find_library(LIBRT rt)
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     atomic_hook
@@ -28,6 +29,7 @@
     ${ABSL_DEFAULT_COPTS}
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     errno_saver
@@ -52,6 +54,7 @@
     ${ABSL_DEFAULT_COPTS}
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     raw_logging_internal
@@ -68,6 +71,7 @@
     ${ABSL_DEFAULT_COPTS}
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     spinlock_wait
@@ -131,6 +135,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     malloc_internal
@@ -151,6 +156,7 @@
     Threads::Threads
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     base_internal
@@ -207,6 +213,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     throw_delegate
@@ -221,6 +228,7 @@
     absl::raw_logging_internal
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     exception_testing
@@ -234,6 +242,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     pretty_function
@@ -243,6 +252,7 @@
     ${ABSL_DEFAULT_COPTS}
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     exception_safety_testing
@@ -276,6 +286,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     atomic_hook_test_helper
@@ -375,6 +386,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     spinlock_test_common
@@ -409,6 +421,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     endian
@@ -519,6 +532,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     scoped_set_env
@@ -570,6 +584,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     strerror
@@ -601,6 +616,7 @@
     GTest::gtest_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     fast_type_id
diff --git a/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt b/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt
index 26a6d0d..f5af40b4 100644
--- a/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cleanup_internal
diff --git a/third_party/abseil-cpp/absl/container/BUILD.bazel b/third_party/abseil-cpp/absl/container/BUILD.bazel
index 2095e57d..d733cb2 100644
--- a/third_party/abseil-cpp/absl/container/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/container/BUILD.bazel
@@ -217,11 +217,6 @@
     ],
 )
 
-NOTEST_TAGS_NONMOBILE = [
-    "no_test_darwin_x86_64",
-    "no_test_loonix",
-]
-
 NOTEST_TAGS_MOBILE = [
     "no_test_android_arm",
     "no_test_android_arm64",
@@ -229,8 +224,6 @@
     "no_test_ios_x86_64",
 ]
 
-NOTEST_TAGS = NOTEST_TAGS_MOBILE + NOTEST_TAGS_NONMOBILE
-
 cc_library(
     name = "flat_hash_map",
     hdrs = ["flat_hash_map.h"],
@@ -251,7 +244,7 @@
     srcs = ["flat_hash_map_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":flat_hash_map",
         ":hash_generator_testing",
@@ -285,7 +278,7 @@
     srcs = ["flat_hash_set_test.cc"],
     copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":flat_hash_set",
         ":hash_generator_testing",
@@ -321,7 +314,7 @@
     srcs = ["node_hash_map_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":hash_generator_testing",
         ":node_hash_map",
@@ -354,7 +347,7 @@
     srcs = ["node_hash_set_test.cc"],
     copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":node_hash_set",
         ":unordered_set_constructor_test",
@@ -383,7 +376,7 @@
     srcs = ["internal/container_memory_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":container_memory",
         ":test_instance_tracker",
@@ -410,7 +403,7 @@
     srcs = ["internal/hash_function_defaults_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS,
+    tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"],
     deps = [
         ":hash_function_defaults",
         "//absl/hash",
@@ -608,7 +601,7 @@
     srcs = ["internal/raw_hash_set_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkstatic = 1,
-    tags = NOTEST_TAGS,
+    tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"],
     deps = [
         ":container_memory",
         ":hash_function_defaults",
@@ -698,7 +691,7 @@
     srcs = ["internal/layout_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS,
+    tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"],
     visibility = ["//visibility:private"],
     deps = [
         ":layout",
@@ -845,7 +838,7 @@
     srcs = ["internal/unordered_set_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":unordered_set_constructor_test",
         ":unordered_set_lookup_test",
@@ -860,7 +853,7 @@
     srcs = ["internal/unordered_map_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     deps = [
         ":unordered_map_constructor_test",
         ":unordered_map_lookup_test",
@@ -875,7 +868,7 @@
     srcs = ["sample_element_size_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
+    tags = ["no_test_loonix"],
     visibility = ["//visibility:private"],
     deps = [
         ":flat_hash_map",
diff --git a/third_party/abseil-cpp/absl/container/CMakeLists.txt b/third_party/abseil-cpp/absl/container/CMakeLists.txt
index 7176907d..aad69fa 100644
--- a/third_party/abseil-cpp/absl/container/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/container/CMakeLists.txt
@@ -42,6 +42,7 @@
     absl::utility
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     btree_test_common
@@ -84,6 +85,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     compressed_tuple
@@ -162,6 +164,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     inlined_vector_internal
@@ -194,6 +197,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     counting_allocator
@@ -240,6 +244,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     test_instance_tracker
@@ -411,6 +416,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     container_memory
@@ -440,6 +446,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hash_function_defaults
@@ -472,6 +479,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hash_generator_testing
@@ -489,6 +497,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hash_policy_testing
@@ -514,6 +523,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hash_policy_traits
@@ -538,6 +548,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hashtablez_sampler
@@ -569,6 +580,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hashtable_debug
@@ -580,6 +592,7 @@
     absl::hashtable_debug_hooks
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     hashtable_debug_hooks
@@ -592,6 +605,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     node_slot_policy
@@ -617,6 +631,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     raw_hash_map
@@ -631,6 +646,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     container_common
@@ -642,6 +658,7 @@
     absl::type_traits
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     raw_hash_set
@@ -704,6 +721,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     layout
@@ -737,6 +755,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     tracked
@@ -749,6 +768,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_map_constructor_test
@@ -763,6 +783,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_map_lookup_test
@@ -777,6 +798,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_map_members_test
@@ -790,6 +812,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_map_modifiers_test
@@ -804,6 +827,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_set_constructor_test
@@ -818,6 +842,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_set_lookup_test
@@ -832,6 +857,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_set_members_test
@@ -845,6 +871,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     unordered_set_modifiers_test
diff --git a/third_party/abseil-cpp/absl/debugging/CMakeLists.txt b/third_party/abseil-cpp/absl/debugging/CMakeLists.txt
index b16fa007..5850bdd 100644
--- a/third_party/abseil-cpp/absl/debugging/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/debugging/CMakeLists.txt
@@ -93,6 +93,7 @@
     GTest::gmock
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     examine_stack
@@ -147,6 +148,7 @@
     GTest::gmock
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     debugging_internal
@@ -168,6 +170,7 @@
     absl::raw_logging_internal
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     demangle_internal
@@ -298,6 +301,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     stack_consumption
diff --git a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
index c655cf4..e63ac4a3 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
+++ b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
@@ -33,7 +33,7 @@
 #endif
 #include <unistd.h>
 
-#if defined(__GLIBC__) && \
+#if !defined(__UCLIBC__) && defined(__GLIBC__) && \
     (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
 #define ABSL_HAVE_GETAUXVAL
 #endif
diff --git a/third_party/abseil-cpp/absl/flags/BUILD.bazel b/third_party/abseil-cpp/absl/flags/BUILD.bazel
index 020b791..91c6e98 100644
--- a/third_party/abseil-cpp/absl/flags/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/flags/BUILD.bazel
@@ -100,6 +100,7 @@
         "//absl/base:log_severity",
         "//absl/strings",
         "//absl/strings:str_format",
+        "//absl/types:optional",
     ],
 )
 
@@ -113,6 +114,9 @@
     ],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//visibility:private",
+    ],
     deps = [
         "//absl/base:config",
         "//absl/base:fast_type_id",
diff --git a/third_party/abseil-cpp/absl/flags/BUILD.gn b/third_party/abseil-cpp/absl/flags/BUILD.gn
index 7cf60e87..f98c71c4 100644
--- a/third_party/abseil-cpp/absl/flags/BUILD.gn
+++ b/third_party/abseil-cpp/absl/flags/BUILD.gn
@@ -59,10 +59,12 @@
     "//third_party/abseil-cpp/absl/base:log_severity",
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/strings:str_format",
+    "//third_party/abseil-cpp/absl/types:optional",
   ]
 }
 
 absl_source_set("commandlineflag_internal") {
+  visibility = [":*"]
   public = [ "internal/commandlineflag.h" ]
   sources = [ "internal/commandlineflag.cc" ]
   deps = [
diff --git a/third_party/abseil-cpp/absl/flags/CMakeLists.txt b/third_party/abseil-cpp/absl/flags/CMakeLists.txt
index 29c85ad..79e51a1 100644
--- a/third_party/abseil-cpp/absl/flags/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/flags/CMakeLists.txt
@@ -87,6 +87,7 @@
     absl::config
     absl::core_headers
     absl::log_severity
+    absl::optional
     absl::strings
     absl::str_format
 )
diff --git a/third_party/abseil-cpp/absl/flags/flag_test.cc b/third_party/abseil-cpp/absl/flags/flag_test.cc
index ced332d4..05ec79e8 100644
--- a/third_party/abseil-cpp/absl/flags/flag_test.cc
+++ b/third_party/abseil-cpp/absl/flags/flag_test.cc
@@ -990,3 +990,177 @@
   absl::SetFlag(&FLAGS_prefix_test_macro_named_flag, 1);
   EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 1);
 }
+
+// --------------------------------------------------------------------
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
+#define ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+#endif
+
+#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+ABSL_FLAG(absl::optional<bool>, optional_bool, absl::nullopt, "help");
+#endif
+ABSL_FLAG(absl::optional<int>, optional_int, {}, "help");
+ABSL_FLAG(absl::optional<double>, optional_double, 9.3, "help");
+ABSL_FLAG(absl::optional<std::string>, optional_string, absl::nullopt, "help");
+ABSL_FLAG(absl::optional<absl::Duration>, optional_duration, absl::nullopt,
+          "help");
+ABSL_FLAG(absl::optional<absl::optional<int>>, optional_optional_int,
+          absl::nullopt, "help");
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+ABSL_FLAG(std::optional<int64_t>, std_optional_int64, std::nullopt, "help");
+#endif
+
+namespace {
+
+#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+TEST_F(FlagTest, TestOptionalBool) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt);
+
+  absl::SetFlag(&FLAGS_optional_bool, false);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), false);
+
+  absl::SetFlag(&FLAGS_optional_bool, true);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), true);
+
+  absl::SetFlag(&FLAGS_optional_bool, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+#endif
+
+TEST_F(FlagTest, TestOptionalInt) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt);
+
+  absl::SetFlag(&FLAGS_optional_int, 0);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 0);
+
+  absl::SetFlag(&FLAGS_optional_int, 10);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 10);
+
+  absl::SetFlag(&FLAGS_optional_int, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalDouble) {
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+  EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 9.3);
+
+  absl::SetFlag(&FLAGS_optional_double, 0.0);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), 0.0);
+
+  absl::SetFlag(&FLAGS_optional_double, 1.234);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+  EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 1.234);
+
+  absl::SetFlag(&FLAGS_optional_double, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_double).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalString) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt);
+
+  // Setting optional string to "" leads to undefined behavior.
+
+  absl::SetFlag(&FLAGS_optional_string, " ");
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), " ");
+
+  absl::SetFlag(&FLAGS_optional_string, "QWERTY");
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), "QWERTY");
+
+  absl::SetFlag(&FLAGS_optional_string, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalDuration) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt);
+
+  absl::SetFlag(&FLAGS_optional_duration, absl::ZeroDuration());
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Seconds(0));
+
+  absl::SetFlag(&FLAGS_optional_duration, absl::Hours(3));
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Hours(3));
+
+  absl::SetFlag(&FLAGS_optional_duration, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalOptional) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt);
+
+  absl::optional<int> nullint{absl::nullopt};
+
+  absl::SetFlag(&FLAGS_optional_optional_int, nullint);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+  EXPECT_NE(absl::GetFlag(FLAGS_optional_optional_int), nullint);
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int),
+            absl::optional<absl::optional<int>>{nullint});
+
+  absl::SetFlag(&FLAGS_optional_optional_int, 0);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0);
+
+  absl::SetFlag(&FLAGS_optional_optional_int, absl::optional<int>{0});
+  EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0);
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::optional<int>{0});
+
+  absl::SetFlag(&FLAGS_optional_optional_int, absl::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+
+TEST_F(FlagTest, TestStdOptional) {
+  EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt);
+
+  absl::SetFlag(&FLAGS_std_optional_int64, 0);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0);
+
+  absl::SetFlag(&FLAGS_std_optional_int64, 0xFFFFFFFFFF16);
+  EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0xFFFFFFFFFF16);
+
+  absl::SetFlag(&FLAGS_std_optional_int64, std::nullopt);
+  EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+  EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+#endif
+
+}  // namespace
diff --git a/third_party/abseil-cpp/absl/flags/marshalling.h b/third_party/abseil-cpp/absl/flags/marshalling.h
index 7cbc136..0f63cdc 100644
--- a/third_party/abseil-cpp/absl/flags/marshalling.h
+++ b/third_party/abseil-cpp/absl/flags/marshalling.h
@@ -162,14 +162,27 @@
 #ifndef ABSL_FLAGS_MARSHALLING_H_
 #define ABSL_FLAGS_MARSHALLING_H_
 
+#include "absl/base/config.h"
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+#include <optional>
+#endif
 #include <string>
 #include <vector>
 
-#include "absl/base/config.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
+
+// Forward declaration to be used inside composable flag parse/unparse
+// implementations
+template <typename T>
+inline bool ParseFlag(absl::string_view input, T* dst, std::string* error);
+template <typename T>
+inline std::string UnparseFlag(const T& v);
+
 namespace flags_internal {
 
 // Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types.
@@ -189,6 +202,36 @@
 bool AbslParseFlag(absl::string_view, std::vector<std::string>*, std::string*);
 
 template <typename T>
+bool AbslParseFlag(absl::string_view text, absl::optional<T>* f,
+                   std::string* err) {
+  if (text.empty()) {
+    *f = absl::nullopt;
+    return true;
+  }
+  T value;
+  if (!absl::ParseFlag(text, &value, err)) return false;
+
+  *f = std::move(value);
+  return true;
+}
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+template <typename T>
+bool AbslParseFlag(absl::string_view text, std::optional<T>* f,
+                   std::string* err) {
+  if (text.empty()) {
+    *f = std::nullopt;
+    return true;
+  }
+  T value;
+  if (!absl::ParseFlag(text, &value, err)) return false;
+
+  *f = std::move(value);
+  return true;
+}
+#endif
+
+template <typename T>
 bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) {
   // Comment on next line provides a good compiler error message if T
   // does not have AbslParseFlag(absl::string_view, T*, std::string*).
@@ -202,6 +245,18 @@
 std::string AbslUnparseFlag(const std::vector<std::string>&);
 
 template <typename T>
+std::string AbslUnparseFlag(const absl::optional<T>& f) {
+  return f.has_value() ? absl::UnparseFlag(*f) : "";
+}
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+template <typename T>
+std::string AbslUnparseFlag(const std::optional<T>& f) {
+  return f.has_value() ? absl::UnparseFlag(*f) : "";
+}
+#endif
+
+template <typename T>
 std::string Unparse(const T& v) {
   // Comment on next line provides a good compiler error message if T does not
   // have UnparseFlag.
diff --git a/third_party/abseil-cpp/absl/flags/marshalling_test.cc b/third_party/abseil-cpp/absl/flags/marshalling_test.cc
index 4a64ce1..691cd2f1 100644
--- a/third_party/abseil-cpp/absl/flags/marshalling_test.cc
+++ b/third_party/abseil-cpp/absl/flags/marshalling_test.cc
@@ -659,6 +659,88 @@
 
 // --------------------------------------------------------------------
 
+TEST(MarshallingTest, TestOptionalBoolParsing) {
+  std::string err;
+  absl::optional<bool> value;
+
+  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+  EXPECT_FALSE(value.has_value());
+
+  EXPECT_TRUE(absl::ParseFlag("true", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_TRUE(*value);
+
+  EXPECT_TRUE(absl::ParseFlag("false", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_FALSE(*value);
+
+  EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalIntParsing) {
+  std::string err;
+  absl::optional<int> value;
+
+  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+  EXPECT_FALSE(value.has_value());
+
+  EXPECT_TRUE(absl::ParseFlag("10", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, 10);
+
+  EXPECT_TRUE(absl::ParseFlag("0x1F", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, 31);
+
+  EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalDoubleParsing) {
+  std::string err;
+  absl::optional<double> value;
+
+  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+  EXPECT_FALSE(value.has_value());
+
+  EXPECT_TRUE(absl::ParseFlag("1.11", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, 1.11);
+
+  EXPECT_TRUE(absl::ParseFlag("-0.12", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, -0.12);
+
+  EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalStringParsing) {
+  std::string err;
+  absl::optional<std::string> value;
+
+  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+  EXPECT_FALSE(value.has_value());
+
+  EXPECT_TRUE(absl::ParseFlag(" ", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, " ");
+
+  EXPECT_TRUE(absl::ParseFlag("aqswde", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, "aqswde");
+
+  EXPECT_TRUE(absl::ParseFlag("nullopt", &value, &err));
+  EXPECT_TRUE(value.has_value());
+  EXPECT_EQ(*value, "nullopt");
+}
+
+// --------------------------------------------------------------------
+
 TEST(MarshallingTest, TestBoolUnparsing) {
   EXPECT_EQ(absl::UnparseFlag(true), "true");
   EXPECT_EQ(absl::UnparseFlag(false), "false");
@@ -808,6 +890,86 @@
 
 // --------------------------------------------------------------------
 
+TEST(MarshallingTest, TestOptionalBoolUnparsing) {
+  absl::optional<bool> value;
+
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+  value = true;
+  EXPECT_EQ(absl::UnparseFlag(value), "true");
+  value = false;
+  EXPECT_EQ(absl::UnparseFlag(value), "false");
+  value = absl::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalIntUnparsing) {
+  absl::optional<int> value;
+
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+  value = 0;
+  EXPECT_EQ(absl::UnparseFlag(value), "0");
+  value = -12;
+  EXPECT_EQ(absl::UnparseFlag(value), "-12");
+  value = absl::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalDoubleUnparsing) {
+  absl::optional<double> value;
+
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+  value = 1.;
+  EXPECT_EQ(absl::UnparseFlag(value), "1");
+  value = -1.23;
+  EXPECT_EQ(absl::UnparseFlag(value), "-1.23");
+  value = absl::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalStringUnparsing) {
+  absl::optional<std::string> value;
+
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+  value = "asdfg";
+  EXPECT_EQ(absl::UnparseFlag(value), "asdfg");
+  value = " ";
+  EXPECT_EQ(absl::UnparseFlag(value), " ");
+  value = "";  // This is UB to set optional string flag to ""
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+  value = absl::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+
+TEST(MarshallingTest, TestStdOptionalUnparsing) {
+  std::optional<std::string> strvalue;
+
+  EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+  strvalue = "asdfg";
+  EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg");
+  strvalue = std::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+
+  std::optional<int> intvalue(10);
+
+  EXPECT_EQ(absl::UnparseFlag(intvalue), "10");
+  intvalue = std::nullopt;
+  EXPECT_EQ(absl::UnparseFlag(intvalue), "");
+}
+
+// --------------------------------------------------------------------
+
+#endif
+
 template <typename T>
 void TestRoundtrip(T v) {
   T new_v;
diff --git a/third_party/abseil-cpp/absl/hash/CMakeLists.txt b/third_party/abseil-cpp/absl/hash/CMakeLists.txt
index 34434fa..423b74b5 100644
--- a/third_party/abseil-cpp/absl/hash/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/hash/CMakeLists.txt
@@ -80,6 +80,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     spy_hash_state
@@ -94,6 +95,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     city
@@ -121,6 +123,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     low_level_hash
diff --git a/third_party/abseil-cpp/absl/memory/BUILD.bazel b/third_party/abseil-cpp/absl/memory/BUILD.bazel
index c16bf8a9..389aedf 100644
--- a/third_party/abseil-cpp/absl/memory/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/memory/BUILD.bazel
@@ -29,6 +29,9 @@
     name = "memory",
     hdrs = ["memory.h"],
     copts = ABSL_DEFAULT_COPTS,
+    defines = select({
+        "//conditions:default": [],
+    }),
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         "//absl/base:core_headers",
diff --git a/third_party/abseil-cpp/absl/random/internal/randen_detect.cc b/third_party/abseil-cpp/absl/random/internal/randen_detect.cc
index 9bb58fc..6dababa 100644
--- a/third_party/abseil-cpp/absl/random/internal/randen_detect.cc
+++ b/third_party/abseil-cpp/absl/random/internal/randen_detect.cc
@@ -24,6 +24,11 @@
 
 #include "absl/random/internal/platform.h"
 
+#if !defined(__UCLIBC__) && defined(__GLIBC__) && \
+    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
+#define ABSL_HAVE_GETAUXVAL
+#endif
+
 #if defined(ABSL_ARCH_X86_64)
 #define ABSL_INTERNAL_USE_X86_CPUID
 #elif defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \
@@ -31,7 +36,7 @@
 #if defined(__ANDROID__)
 #define ABSL_INTERNAL_USE_ANDROID_GETAUXVAL
 #define ABSL_INTERNAL_USE_GETAUXVAL
-#elif defined(__linux__)
+#elif defined(__linux__) && defined(ABSL_HAVE_GETAUXVAL)
 #define ABSL_INTERNAL_USE_LINUX_GETAUXVAL
 #define ABSL_INTERNAL_USE_GETAUXVAL
 #endif
diff --git a/third_party/abseil-cpp/absl/strings/BUILD.bazel b/third_party/abseil-cpp/absl/strings/BUILD.bazel
index 5f122ee9..813aef45 100644
--- a/third_party/abseil-cpp/absl/strings/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/strings/BUILD.bazel
@@ -736,7 +736,6 @@
         "no_test_android_arm",
         "no_test_android_arm64",
         "no_test_android_x86",
-        "no_test_darwin_x86_64",
         "no_test_ios_x86_64",
         "no_test_loonix",
         "no_test_msvc_x64",
diff --git a/third_party/abseil-cpp/absl/strings/CMakeLists.txt b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
index 5d418c8..d8715ed 100644
--- a/third_party/abseil-cpp/absl/strings/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
@@ -68,6 +68,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     strings_internal
@@ -385,6 +386,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     str_format_internal
@@ -523,6 +525,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     pow10_helper
@@ -550,6 +553,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cord_internal
@@ -588,6 +592,7 @@
     absl::type_traits
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_update_tracker
@@ -614,6 +619,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_functions
@@ -642,6 +648,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_statistics
@@ -656,6 +663,7 @@
     absl::synchronization
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_handle
@@ -689,6 +697,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_info
@@ -756,6 +765,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_sample_token
@@ -794,6 +804,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_update_scope
@@ -859,6 +870,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cord_rep_test_util
@@ -889,6 +901,7 @@
   TESTONLY
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     cordz_test_helpers
diff --git a/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc b/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc
index d9a9a76..8e7183b 100644
--- a/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc
+++ b/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc
@@ -126,7 +126,7 @@
 
   reader.Reset(ring);
   size_t consumed = 0;
-  size_t remaining = ring->length;;
+  size_t remaining = ring->length;
   for (int i = 0; i < flats.size(); ++i) {
     CordRepRing::index_type index = ring->advance(head, i);
     size_t offset = consumed;
diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
index 2087ffe..5ca5e589a 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
@@ -156,7 +156,7 @@
   // used for the StringConstant constructor to avoid collecting immutable
   // constant cords.
   // kReservedFlag is reserved for future use.
-  enum {
+  enum Flags {
     kNumFlags = 2,
 
     kImmortalFlag = 0x1,
diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h
index 971b92e..3d581c87 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h
@@ -143,8 +143,8 @@
   // `index_` and `node_` contain the navigation state as the 'path' to the
   // current data edge which is at `node_[0]->Edge(index_[0])`. The contents
   // of these are undefined until the instance is initialized (`height_ >= 0`).
-  uint8_t index_[CordRepBtree::kMaxHeight];
-  CordRepBtree* node_[CordRepBtree::kMaxHeight];
+  uint8_t index_[CordRepBtree::kMaxDepth];
+  CordRepBtree* node_[CordRepBtree::kMaxDepth];
 };
 
 // Returns true if this instance is not empty.
@@ -173,6 +173,7 @@
 inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) {
   assert(tree != nullptr);
   assert(tree->size() > 0);
+  assert(tree->height() <= CordRepBtree::kMaxHeight);
   int height = height_ = tree->height();
   size_t index = tree->index(edge_type);
   node_[height] = tree;
@@ -206,6 +207,7 @@
 inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset(
     CordRepBtree* tree, size_t offset) {
   assert(tree != nullptr);
+  assert(tree->height() <= CordRepBtree::kMaxHeight);
   if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0};
   height_ = tree->height();
   node_[height_] = tree;
diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc
index ce09b199..4f9bd4e 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc
@@ -319,6 +319,27 @@
   ASSERT_THAT(result.tree, Eq(nullptr));
 }
 
+TEST(CordRepBtreeNavigatorTest, NavigateMaximumTreeDepth) {
+  CordRepFlat* flat1 = MakeFlat("Hello world");
+  CordRepFlat* flat2 = MakeFlat("World Hello");
+
+  CordRepBtree* node = CordRepBtree::Create(flat1);
+  node = CordRepBtree::Append(node, flat2);
+  while (node->height() < CordRepBtree::kMaxHeight) {
+    node = CordRepBtree::New(node);
+  }
+
+  CordRepBtreeNavigator nav;
+  CordRep* edge = nav.InitFirst(node);
+  EXPECT_THAT(edge, Eq(flat1));
+  EXPECT_THAT(nav.Next(), Eq(flat2));
+  EXPECT_THAT(nav.Next(), Eq(nullptr));
+  EXPECT_THAT(nav.Previous(), Eq(flat1));
+  EXPECT_THAT(nav.Previous(), Eq(nullptr));
+
+  CordRep::Unref(node);
+}
+
 }  // namespace
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc
index 9f54301..6be1770d 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc
@@ -167,7 +167,7 @@
           if (cord.data.is_profiled()) {
             // 1) Untrack
             cord.data.cordz_info()->Untrack();
-            cord.data.clear_cordz_info();;
+            cord.data.clear_cordz_info();
           } else {
             // 2) Track
             CordzInfo::TrackCord(cord.data, kTrackCordMethod);
diff --git a/third_party/abseil-cpp/absl/strings/str_join.h b/third_party/abseil-cpp/absl/strings/str_join.h
index 3353453..ee5ae7ef 100644
--- a/third_party/abseil-cpp/absl/strings/str_join.h
+++ b/third_party/abseil-cpp/absl/strings/str_join.h
@@ -72,21 +72,15 @@
 // functions. You may provide your own Formatter to enable `absl::StrJoin()` to
 // work with arbitrary types.
 //
-// The following is an example of a custom Formatter that simply uses
-// `std::to_string()` to format an integer as a std::string.
+// The following is an example of a custom Formatter that uses
+// `absl::FormatDuration` to join a list of `absl::Duration`s.
 //
-//   struct MyFormatter {
-//     void operator()(std::string* out, int i) const {
-//       out->append(std::to_string(i));
-//     }
-//   };
-//
-// You would use the above formatter by passing an instance of it as the final
-// argument to `absl::StrJoin()`:
-//
-//   std::vector<int> v = {1, 2, 3, 4};
-//   std::string s = absl::StrJoin(v, "-", MyFormatter());
-//   EXPECT_EQ("1-2-3-4", s);
+//   std::vector<absl::Duration> v = {absl::Seconds(1), absl::Milliseconds(10)};
+//   std::string s =
+//       absl::StrJoin(v, ", ", [](std::string* out, absl::Duration dur) {
+//         absl::StrAppend(out, absl::FormatDuration(dur));
+//       });
+//   EXPECT_EQ("1s, 10ms", s);
 //
 // The following standard formatters are provided within this file:
 //
diff --git a/third_party/abseil-cpp/absl/strings/string_view_test.cc b/third_party/abseil-cpp/absl/strings/string_view_test.cc
index 2c13dd1c..9d5463a1 100644
--- a/third_party/abseil-cpp/absl/strings/string_view_test.cc
+++ b/third_party/abseil-cpp/absl/strings/string_view_test.cc
@@ -1189,7 +1189,7 @@
   EXPECT_LT("hello", std::string("world"));
 }
 
-TEST(ComparisonOpsTest, HeterogenousStringViewEquals) {
+TEST(ComparisonOpsTest, HeterogeneousStringViewEquals) {
   EXPECT_EQ(absl::string_view("hello"), std::string("hello"));
   EXPECT_EQ("hello", absl::string_view("hello"));
 }
diff --git a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
index d719547..a0492c5 100644
--- a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
@@ -34,7 +34,9 @@
     hdrs = [
         "internal/graphcycles.h",
     ],
-    copts = ABSL_DEFAULT_COPTS,
+    copts = ABSL_DEFAULT_COPTS + select({
+        "//conditions:default": [],
+    }),
     linkopts = ABSL_DEFAULT_LINKOPTS,
     visibility = [
         "//absl:__subpackages__",
diff --git a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
index 605efe2..9335c26 100644
--- a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     graphcycles_internal
@@ -32,6 +33,7 @@
     absl::raw_logging_internal
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     kernel_timeout_internal
@@ -125,6 +127,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     thread_pool
@@ -170,6 +173,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     per_thread_sem_test_common
diff --git a/third_party/abseil-cpp/absl/time/CMakeLists.txt b/third_party/abseil-cpp/absl/time/CMakeLists.txt
index f6ff8bd1..debab3ba 100644
--- a/third_party/abseil-cpp/absl/time/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/time/CMakeLists.txt
@@ -87,6 +87,7 @@
     $<$<PLATFORM_ID:Darwin>:${CoreFoundation}>
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     time_internal_test_util
diff --git a/third_party/abseil-cpp/absl/types/CMakeLists.txt b/third_party/abseil-cpp/absl/types/CMakeLists.txt
index d7e8614e..830953ae 100644
--- a/third_party/abseil-cpp/absl/types/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/types/CMakeLists.txt
@@ -43,6 +43,7 @@
   PUBLIC
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     bad_any_cast_impl
@@ -239,6 +240,7 @@
     GTest::gmock_main
 )
 
+# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     conformance_testing
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index fe47061..8ebacd4 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1361,6 +1361,12 @@
 // TODO(crbug.com/1277431): This flag should be eventually disabled.
 const base::Feature kEventPath{"EventPath", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Removes a paint invalidation of viewport constrained objects (sticky or
+// fixed) after scrolling.
+const base::Feature kOptimizeViewportConstrainedPaintInvalidation{
+    "OptimizeViewportConstrainedPaintInvalidation",
+    base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kReduceUserAgentMinorVersion{
     "ReduceUserAgentMinorVersion", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -1387,5 +1393,22 @@
 const base::Feature kUACHOverrideBlank{"UACHOverrideBlank",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if BUILDFLAG(IS_WIN)
+const base::Feature kPrewarmDefaultFontFamilies{
+    "PrewarmDefaultFontFamilies", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<bool> kPrewarmStandard = {&kPrewarmDefaultFontFamilies,
+                                                   "prewarm_standard", true};
+const base::FeatureParam<bool> kPrewarmFixed = {&kPrewarmDefaultFontFamilies,
+                                                "prewarm_fixed", true};
+const base::FeatureParam<bool> kPrewarmSerif = {&kPrewarmDefaultFontFamilies,
+                                                "prewarm_serif", true};
+const base::FeatureParam<bool> kPrewarmSansSerif = {
+    &kPrewarmDefaultFontFamilies, "prewarm_sans_serif", true};
+const base::FeatureParam<bool> kPrewarmCursive = {&kPrewarmDefaultFontFamilies,
+                                                  "prewarm_cursive", true};
+const base::FeatureParam<bool> kPrewarmFantasy = {&kPrewarmDefaultFontFamilies,
+                                                  "prewarm_fantasy", true};
+#endif
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 05766d94..45d481d 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -663,6 +663,11 @@
 // Gates the non-standard API Event.path to help its deprecation and removal.
 BLINK_COMMON_EXPORT extern const base::Feature kEventPath;
 
+// Removes a paint invalidation of viewport constrained objects (sticky or
+// fixed) after scrolling.
+BLINK_COMMON_EXPORT extern const base::Feature
+    kOptimizeViewportConstrainedPaintInvalidation;
+
 // If enabled, the minor version of the User-Agent string will be reduced.
 BLINK_COMMON_EXPORT extern const base::Feature kReduceUserAgentMinorVersion;
 
@@ -685,6 +690,17 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kUACHOverrideBlank;
 
+#if BUILDFLAG(IS_WIN)
+// Enables prewarming the default font families.
+BLINK_COMMON_EXPORT extern const base::Feature kPrewarmDefaultFontFamilies;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmStandard;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmFixed;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmSerif;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmSansSerif;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmCursive;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<bool> kPrewarmFantasy;
+#endif
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 1615d25b..17308879 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3515,6 +3515,10 @@
   kNavigatorUAData_Mobile = 4194,
   kNavigatorUAData_Platform = 4195,
   kNavigatorUAData_Brands = 4196,
+  kOldConstraintsParsed = 4197,
+  kOldConstraintNotReported = 4198,
+  kOldConstraintRejected = 4199,
+  kOldConstraintIgnored = 4200,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 75e5984..502a795 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1886,3 +1886,27 @@
   ]
   seed_corpus = "//third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_source_registration_corpus"
 }
+
+fuzzer_test("attribution_aggregatable_values_fuzzer") {
+  sources = [ "frame/attribution_aggregatable_values_fuzzer.cc" ]
+  deps = [
+    ":core",
+    "//testing/libfuzzer/proto:json_proto",
+    "//testing/libfuzzer/proto:json_proto_converter",
+    "//third_party/blink/renderer/platform:blink_fuzzer_test_support",
+    "//third_party/libprotobuf-mutator",
+  ]
+  seed_corpus = "//third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_values_corpus"
+}
+
+fuzzer_test("attribution_aggregatable_trigger_data_fuzzer") {
+  sources = [ "frame/attribution_aggregatable_trigger_data_fuzzer.cc" ]
+  deps = [
+    ":core",
+    "//testing/libfuzzer/proto:json_proto",
+    "//testing/libfuzzer/proto:json_proto_converter",
+    "//third_party/blink/renderer/platform:blink_fuzzer_test_support",
+    "//third_party/libprotobuf-mutator",
+  ]
+  seed_corpus = "//third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_trigger_data_corpus"
+}
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index f1540de..b1ab6987 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -532,8 +532,6 @@
   "property_registry.h",
   "property_set_css_style_declaration.cc",
   "property_set_css_style_declaration.h",
-  "style_image_cache.cc",
-  "style_image_cache.h",
   "style_request.h",
   "remote_font_face_source.cc",
   "remote_font_face_source.h",
@@ -769,7 +767,6 @@
   "selector_query_test.cc",
   "style_element_test.cc",
   "style_engine_test.cc",
-  "style_image_cache_test.cc",
   "style_recalc_change_test.cc",
   "style_recalc_context_test.cc",
   "style_environment_variables_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_font_selector.cc b/third_party/blink/renderer/core/css/css_font_selector.cc
index e62a8fd6..c4cb3ff3f 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector.cc
@@ -34,15 +34,8 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
-#include "third_party/blink/renderer/core/frame/web_feature.h"
-#include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/font_fallback_map.h"
-#include "third_party/blink/renderer/platform/fonts/font_matching_metrics.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
-#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -63,8 +56,8 @@
 
 CSSFontSelector::~CSSFontSelector() = default;
 
-UseCounter* CSSFontSelector::GetUseCounter() {
-  return &GetDocument();
+UseCounter* CSSFontSelector::GetUseCounter() const {
+  return GetExecutionContext();
 }
 
 void CSSFontSelector::RegisterForInvalidationCallbacks(
@@ -133,30 +126,29 @@
   if (!font_family.FamilyIsGeneric()) {
     if (CSSSegmentedFontFace* face =
             font_face_cache_->Get(request_description, family_name)) {
-      document.GetFontMatchingMetrics()->ReportWebFontFamily(family_name);
+      ReportWebFontFamily(family_name);
       return face->GetFontData(request_description);
     }
   }
 
-  document.GetFontMatchingMetrics()->ReportSystemFontFamily(family_name);
+  ReportSystemFontFamily(family_name);
 
   // Try to return the correct font based off our settings, in case we were
   // handed the generic font family name.
   AtomicString settings_family_name =
-      FamilyNameFromSettings(generic_font_family_settings_, request_description,
-                             font_family, &document);
+      FamilyNameFromSettings(request_description, font_family);
   if (settings_family_name.IsEmpty())
     return nullptr;
 
-  document.GetFontMatchingMetrics()->ReportFontFamilyLookupByGenericFamily(
+  ReportFontFamilyLookupByGenericFamily(
       family_name, request_description.GetScript(),
       request_description.GenericFamily(), settings_family_name);
 
   scoped_refptr<SimpleFontData> font_data =
       FontCache::Get().GetFontData(request_description, settings_family_name);
 
-  document.GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      settings_family_name, request_description, font_data.get());
+  ReportFontLookupByUniqueOrFamilyName(settings_family_name,
+                                       request_description, font_data.get());
 
   return font_data;
 }
@@ -169,72 +161,8 @@
   FontCacheInvalidated();
 }
 
-void CSSFontSelector::ReportNotDefGlyph() const {
-  UseCounter::Count(GetDocument(), WebFeature::kFontShapingNotDefGlyphObserved);
-}
-
-void CSSFontSelector::ReportEmojiSegmentGlyphCoverage(
-    unsigned num_clusters,
-    unsigned num_broken_clusters) {
-  GetDocument().GetFontMatchingMetrics()->ReportEmojiSegmentGlyphCoverage(
-      num_clusters, num_broken_clusters);
-}
-
-void CSSFontSelector::ReportSuccessfulFontFamilyMatch(
-    const AtomicString& font_family_name) {
-  GetDocument().GetFontMatchingMetrics()->ReportSuccessfulFontFamilyMatch(
-      font_family_name);
-}
-
-void CSSFontSelector::ReportFailedFontFamilyMatch(
-    const AtomicString& font_family_name) {
-  GetDocument().GetFontMatchingMetrics()->ReportFailedFontFamilyMatch(
-      font_family_name);
-}
-
-void CSSFontSelector::ReportSuccessfulLocalFontMatch(
-    const AtomicString& font_name) {
-  GetDocument().GetFontMatchingMetrics()->ReportSuccessfulLocalFontMatch(
-      font_name);
-}
-
-void CSSFontSelector::ReportFailedLocalFontMatch(
-    const AtomicString& font_name) {
-  GetDocument().GetFontMatchingMetrics()->ReportFailedLocalFontMatch(font_name);
-}
-
-void CSSFontSelector::ReportFontLookupByUniqueOrFamilyName(
-    const AtomicString& name,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  GetDocument().GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      name, font_description, resulting_font_data);
-}
-
-void CSSFontSelector::ReportFontLookupByUniqueNameOnly(
-    const AtomicString& name,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data,
-    bool is_loading_fallback) {
-  GetDocument().GetFontMatchingMetrics()->ReportFontLookupByUniqueNameOnly(
-      name, font_description, resulting_font_data, is_loading_fallback);
-}
-
-void CSSFontSelector::ReportFontLookupByFallbackCharacter(
-    UChar32 fallback_character,
-    FontFallbackPriority fallback_priority,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  GetDocument().GetFontMatchingMetrics()->ReportFontLookupByFallbackCharacter(
-      fallback_character, fallback_priority, font_description,
-      resulting_font_data);
-}
-
-void CSSFontSelector::ReportLastResortFallbackFontLookup(
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  GetDocument().GetFontMatchingMetrics()->ReportLastResortFallbackFontLookup(
-      font_description, resulting_font_data);
+FontMatchingMetrics* CSSFontSelector::GetFontMatchingMetrics() const {
+  return GetDocument().GetFontMatchingMetrics();
 }
 
 void CSSFontSelector::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/css/css_font_selector.h b/third_party/blink/renderer/core/css/css_font_selector.h
index 9726390..ca66eff 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.h
+++ b/third_party/blink/renderer/core/css/css_font_selector.h
@@ -41,6 +41,8 @@
 class FontDescription;
 class FontFamily;
 
+// `CSSFontSelector` is owned by `StyleEngine`. There is derived class
+// ` PopupMenuCSSFontSelector`.
 class CORE_EXPORT CSSFontSelector : public CSSFontSelectorBase {
  public:
   explicit CSSFontSelector(const TreeScope&);
@@ -48,42 +50,6 @@
 
   unsigned Version() const override { return font_face_cache_->Version(); }
 
-  void ReportSuccessfulFontFamilyMatch(
-      const AtomicString& font_family_name) override;
-
-  void ReportFailedFontFamilyMatch(
-      const AtomicString& font_family_name) override;
-
-  void ReportSuccessfulLocalFontMatch(const AtomicString& font_name) override;
-
-  void ReportFailedLocalFontMatch(const AtomicString& font_name) override;
-
-  void ReportFontLookupByUniqueOrFamilyName(
-      const AtomicString& name,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportFontLookupByUniqueNameOnly(
-      const AtomicString& name,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data,
-      bool is_loading_fallback = false) override;
-
-  void ReportFontLookupByFallbackCharacter(
-      UChar32 fallback_character,
-      FontFallbackPriority fallback_priority,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportLastResortFallbackFontLookup(
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportNotDefGlyph() const override;
-
-  void ReportEmojiSegmentGlyphCoverage(unsigned num_clusters,
-                                       unsigned num_broken_clusters) override;
-
   scoped_refptr<FontData> GetFontData(const FontDescription&,
                                       const FontFamily&) override;
 
@@ -114,9 +80,12 @@
   void Trace(Visitor*) const override;
 
  protected:
-  UseCounter* GetUseCounter() override;
   void DispatchInvalidationCallbacks(FontInvalidationReason);
 
+  // `CSSFontSelectorBase` overrides
+  FontMatchingMetrics* GetFontMatchingMetrics() const override;
+  UseCounter* GetUseCounter() const override;
+
  private:
   // TODO(Oilpan): Ideally this should just be a traced Member but that will
   // currently leak because ComputedStyle and its data are not on the heap.
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.cc b/third_party/blink/renderer/core/css/css_font_selector_base.cc
index c06520b..aeec9b2 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.cc
@@ -6,17 +6,27 @@
 
 #include "build/build_config.h"
 #include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_matching_metrics.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
+void CSSFontSelectorBase::CountUse(WebFeature feature) const {
+  return UseCounter::Count(GetUseCounter(), feature);
+}
+
 AtomicString CSSFontSelectorBase::FamilyNameFromSettings(
     const FontDescription& font_description,
     const FontFamily& generic_family_name) {
-  return FamilyNameFromSettings(generic_font_family_settings_, font_description,
-                                generic_family_name, GetUseCounter());
+  return FontSelector::FamilyNameFromSettings(
+      generic_font_family_settings_, font_description, generic_family_name,
+      GetUseCounter());
 }
-
 bool CSSFontSelectorBase::IsPlatformFamilyMatchAvailable(
     const FontDescription& font_description,
     const FontFamily& passed_family) {
@@ -27,6 +37,91 @@
                                                          family);
 }
 
+void CSSFontSelectorBase::ReportEmojiSegmentGlyphCoverage(
+    unsigned num_clusters,
+    unsigned num_broken_clusters) {
+  GetFontMatchingMetrics()->ReportEmojiSegmentGlyphCoverage(
+      num_clusters, num_broken_clusters);
+}
+
+void CSSFontSelectorBase::ReportFontFamilyLookupByGenericFamily(
+    const AtomicString& generic_font_family_name,
+    UScriptCode script,
+    FontDescription::GenericFamilyType generic_family_type,
+    const AtomicString& resulting_font_name) {
+  GetFontMatchingMetrics()->ReportFontFamilyLookupByGenericFamily(
+      generic_font_family_name, script, generic_family_type,
+      resulting_font_name);
+}
+
+void CSSFontSelectorBase::ReportSuccessfulFontFamilyMatch(
+    const AtomicString& font_family_name) {
+  GetFontMatchingMetrics()->ReportSuccessfulFontFamilyMatch(font_family_name);
+}
+
+void CSSFontSelectorBase::ReportFailedFontFamilyMatch(
+    const AtomicString& font_family_name) {
+  GetFontMatchingMetrics()->ReportFailedFontFamilyMatch(font_family_name);
+}
+
+void CSSFontSelectorBase::ReportSuccessfulLocalFontMatch(
+    const AtomicString& font_name) {
+  GetFontMatchingMetrics()->ReportSuccessfulLocalFontMatch(font_name);
+}
+
+void CSSFontSelectorBase::ReportFailedLocalFontMatch(
+    const AtomicString& font_name) {
+  GetFontMatchingMetrics()->ReportFailedLocalFontMatch(font_name);
+}
+
+void CSSFontSelectorBase::ReportFontLookupByUniqueOrFamilyName(
+    const AtomicString& name,
+    const FontDescription& font_description,
+    SimpleFontData* resulting_font_data) {
+  GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
+      name, font_description, resulting_font_data);
+}
+
+void CSSFontSelectorBase::ReportFontLookupByUniqueNameOnly(
+    const AtomicString& name,
+    const FontDescription& font_description,
+    SimpleFontData* resulting_font_data,
+    bool is_loading_fallback) {
+  GetFontMatchingMetrics()->ReportFontLookupByUniqueNameOnly(
+      name, font_description, resulting_font_data, is_loading_fallback);
+}
+
+void CSSFontSelectorBase::ReportFontLookupByFallbackCharacter(
+    UChar32 fallback_character,
+    FontFallbackPriority fallback_priority,
+    const FontDescription& font_description,
+    SimpleFontData* resulting_font_data) {
+  GetFontMatchingMetrics()->ReportFontLookupByFallbackCharacter(
+      fallback_character, fallback_priority, font_description,
+      resulting_font_data);
+}
+
+void CSSFontSelectorBase::ReportLastResortFallbackFontLookup(
+    const FontDescription& font_description,
+    SimpleFontData* resulting_font_data) {
+  GetFontMatchingMetrics()->ReportLastResortFallbackFontLookup(
+      font_description, resulting_font_data);
+}
+
+void CSSFontSelectorBase::ReportNotDefGlyph() const {
+  CountUse(WebFeature::kFontShapingNotDefGlyphObserved);
+}
+
+void CSSFontSelectorBase::ReportSystemFontFamily(
+    const AtomicString& font_family_name) {
+  GetFontMatchingMetrics()->ReportSystemFontFamily(font_family_name);
+}
+
+void CSSFontSelectorBase::ReportWebFontFamily(
+    const AtomicString& font_family_name) {
+  GetFontMatchingMetrics()->ReportWebFontFamily(font_family_name);
+}
+
 void CSSFontSelectorBase::WillUseFontData(
     const FontDescription& font_description,
     const FontFamily& family,
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.h b/third_party/blink/renderer/core/css/css_font_selector_base.h
index 0b691bc3..00eea28 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.h
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.h
@@ -15,7 +15,12 @@
 
 class FontDescription;
 class FontFamily;
+class FontMatchingMetrics;
 
+// `CSSFontSelectorBase` is the base class of CSS related font selectors:
+//  * `CSSFontSelector` for `StyleEngine`
+//  * `PopupMenuCSSFontSelector` derived from `CSSFontSelector`
+//  * `OffscreenFontSelector` for `WorkerGlobalScope`.
 class CORE_EXPORT CSSFontSelectorBase : public FontSelector {
  public:
   bool IsPlatformFamilyMatchAvailable(const FontDescription&,
@@ -28,13 +33,59 @@
                     const AtomicString& family_name,
                     const FontDataForRangeSet&) override;
 
+  void ReportSuccessfulFontFamilyMatch(
+      const AtomicString& font_family_name) override;
+
+  void ReportFailedFontFamilyMatch(
+      const AtomicString& font_family_name) override;
+
+  void ReportSuccessfulLocalFontMatch(const AtomicString& font_name) override;
+
+  void ReportFailedLocalFontMatch(const AtomicString& font_name) override;
+
+  void ReportFontLookupByUniqueOrFamilyName(
+      const AtomicString& name,
+      const FontDescription& font_description,
+      SimpleFontData* resulting_font_data) override;
+
+  void ReportFontLookupByUniqueNameOnly(
+      const AtomicString& name,
+      const FontDescription& font_description,
+      SimpleFontData* resulting_font_data,
+      bool is_loading_fallback = false) override;
+
+  void ReportFontLookupByFallbackCharacter(
+      UChar32 fallback_character,
+      FontFallbackPriority fallback_priority,
+      const FontDescription& font_description,
+      SimpleFontData* resulting_font_data) override;
+
+  void ReportLastResortFallbackFontLookup(
+      const FontDescription& font_description,
+      SimpleFontData* resulting_font_data) override;
+
+  void ReportFontFamilyLookupByGenericFamily(
+      const AtomicString& generic_font_family_name,
+      UScriptCode script,
+      FontDescription::GenericFamilyType generic_family_type,
+      const AtomicString& resulting_font_name);
+
+  void ReportNotDefGlyph() const override;
+
+  void ReportEmojiSegmentGlyphCoverage(unsigned num_clusters,
+                                       unsigned num_broken_clusters) override;
+
   void Trace(Visitor*) const override;
 
  protected:
-  virtual UseCounter* GetUseCounter() = 0;
+  virtual FontMatchingMetrics* GetFontMatchingMetrics() const = 0;
+  virtual UseCounter* GetUseCounter() const = 0;
+
+  void CountUse(WebFeature feature) const;
   AtomicString FamilyNameFromSettings(const FontDescription&,
                                       const FontFamily& generic_family_name);
-  using FontSelector::FamilyNameFromSettings;
+  void ReportSystemFontFamily(const AtomicString& font_family_name);
+  void ReportWebFontFamily(const AtomicString& font_family_name);
 
   Member<FontFaceCache> font_face_cache_;
   GenericFontFamilySettings generic_font_family_settings_;
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index f66e81b..7bb50209 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -24,7 +24,6 @@
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
-#include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -127,8 +126,10 @@
 
     FetchParameters params =
         PrepareFetch(document, image_request_behavior, cross_origin);
-    cached_image_ = document.GetStyleEngine().CacheStyleImage(
-        params, origin_clean_, is_ad_related_);
+    cached_image_ = MakeGarbageCollected<StyleFetchedImage>(
+        ImageResourceContent::Fetch(params, document.Fetcher()), document,
+        params.GetImageRequestBehavior() == FetchParameters::kDeferImageLoad,
+        origin_clean_ == OriginClean::kTrue, is_ad_related_, params.Url());
   }
   return cached_image_.Get();
 }
diff --git a/third_party/blink/renderer/core/css/offscreen_font_selector.cc b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
index 71a05a5..fa1b592 100644
--- a/third_party/blink/renderer/core/css/offscreen_font_selector.cc
+++ b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
@@ -5,18 +5,9 @@
 #include "third_party/blink/renderer/core/css/offscreen_font_selector.h"
 
 #include "build/build_config.h"
-#include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
-#include "third_party/blink/renderer/core/css/css_value_list.h"
-#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
-#include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/font_matching_metrics.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
-#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -29,7 +20,11 @@
 
 OffscreenFontSelector::~OffscreenFontSelector() = default;
 
-UseCounter* OffscreenFontSelector::GetUseCounter() {
+FontMatchingMetrics* OffscreenFontSelector::GetFontMatchingMetrics() const {
+  return worker_->GetFontMatchingMetrics();
+}
+
+UseCounter* OffscreenFontSelector::GetUseCounter() const {
   return GetExecutionContext();
 }
 
@@ -50,107 +45,32 @@
   const auto& family_name = font_family.FamilyName();
   if (CSSSegmentedFontFace* face =
           font_face_cache_->Get(font_description, family_name)) {
-    worker_->GetFontMatchingMetrics()->ReportWebFontFamily(family_name);
+    ReportWebFontFamily(family_name);
     return face->GetFontData(font_description);
   }
 
-  worker_->GetFontMatchingMetrics()->ReportSystemFontFamily(family_name);
+  ReportSystemFontFamily(family_name);
 
   // Try to return the correct font based off our settings, in case we were
   // handed the generic font family name.
   AtomicString settings_family_name =
-      FamilyNameFromSettings(generic_font_family_settings_, font_description,
-                             font_family, GetExecutionContext());
+      FamilyNameFromSettings(font_description, font_family);
   if (settings_family_name.IsEmpty())
     return nullptr;
 
-  worker_->GetFontMatchingMetrics()->ReportFontFamilyLookupByGenericFamily(
+  ReportFontFamilyLookupByGenericFamily(
       family_name, font_description.GetScript(),
       font_description.GenericFamily(), settings_family_name);
 
   auto font_data =
       FontCache::Get().GetFontData(font_description, settings_family_name);
 
-  worker_->GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      settings_family_name, font_description, font_data.get());
+  ReportFontLookupByUniqueOrFamilyName(settings_family_name, font_description,
+                                       font_data.get());
 
   return font_data;
 }
 
-void OffscreenFontSelector::ReportNotDefGlyph() const {}
-
-void OffscreenFontSelector::ReportEmojiSegmentGlyphCoverage(
-    unsigned num_clusters,
-    unsigned num_broken_clusters) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportEmojiSegmentGlyphCoverage(
-      num_clusters, num_broken_clusters);
-}
-
-void OffscreenFontSelector::ReportSuccessfulFontFamilyMatch(
-    const AtomicString& font_family_name) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportSuccessfulFontFamilyMatch(
-      font_family_name);
-}
-
-void OffscreenFontSelector::ReportFailedFontFamilyMatch(
-    const AtomicString& font_family_name) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportFailedFontFamilyMatch(
-      font_family_name);
-}
-
-void OffscreenFontSelector::ReportSuccessfulLocalFontMatch(
-    const AtomicString& font_name) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportSuccessfulLocalFontMatch(font_name);
-}
-
-void OffscreenFontSelector::ReportFailedLocalFontMatch(
-    const AtomicString& font_name) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportFailedLocalFontMatch(font_name);
-}
-
-void OffscreenFontSelector::ReportFontLookupByUniqueOrFamilyName(
-    const AtomicString& name,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      name, font_description, resulting_font_data);
-}
-
-void OffscreenFontSelector::ReportFontLookupByUniqueNameOnly(
-    const AtomicString& name,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data,
-    bool is_loading_fallback) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportFontLookupByUniqueNameOnly(
-      name, font_description, resulting_font_data, is_loading_fallback);
-}
-
-void OffscreenFontSelector::ReportFontLookupByFallbackCharacter(
-    UChar32 fallback_character,
-    FontFallbackPriority fallback_priority,
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportFontLookupByFallbackCharacter(
-      fallback_character, fallback_priority, font_description,
-      resulting_font_data);
-}
-
-void OffscreenFontSelector::ReportLastResortFallbackFontLookup(
-    const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
-  DCHECK(worker_);
-  worker_->GetFontMatchingMetrics()->ReportLastResortFallbackFontLookup(
-      font_description, resulting_font_data);
-}
-
 void OffscreenFontSelector::FontCacheInvalidated() {
   font_face_cache_->IncrementVersion();
 }
diff --git a/third_party/blink/renderer/core/css/offscreen_font_selector.h b/third_party/blink/renderer/core/css/offscreen_font_selector.h
index 5e3a1bb..fcfe711e7 100644
--- a/third_party/blink/renderer/core/css/offscreen_font_selector.h
+++ b/third_party/blink/renderer/core/css/offscreen_font_selector.h
@@ -27,42 +27,6 @@
 
   unsigned Version() const override { return 1; }
 
-  void ReportSuccessfulFontFamilyMatch(
-      const AtomicString& font_family_name) override;
-
-  void ReportFailedFontFamilyMatch(
-      const AtomicString& font_family_name) override;
-
-  void ReportSuccessfulLocalFontMatch(const AtomicString& font_name) override;
-
-  void ReportFailedLocalFontMatch(const AtomicString& font_name) override;
-
-  void ReportFontLookupByUniqueOrFamilyName(
-      const AtomicString& name,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportFontLookupByUniqueNameOnly(
-      const AtomicString& name,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data,
-      bool is_loading_fallback = false) override;
-
-  void ReportFontLookupByFallbackCharacter(
-      UChar32 fallback_character,
-      FontFallbackPriority fallback_priority,
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportLastResortFallbackFontLookup(
-      const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
-
-  void ReportNotDefGlyph() const override;
-
-  void ReportEmojiSegmentGlyphCoverage(unsigned num_clusters,
-                                       unsigned num_broken_clusters) override;
-
   scoped_refptr<FontData> GetFontData(const FontDescription&,
                                       const FontFamily&) override;
 
@@ -87,9 +51,12 @@
   void Trace(Visitor*) const override;
 
  protected:
-  UseCounter* GetUseCounter() override;
   void DispatchInvalidationCallbacks();
 
+  // `CSSFontSelectorBase` overrides
+  FontMatchingMetrics* GetFontMatchingMetrics() const override;
+  UseCounter* GetUseCounter() const override;
+
  private:
   Member<WorkerGlobalScope> worker_;
 };
diff --git a/third_party/blink/renderer/core/css/resolver/cascade_map.h b/third_party/blink/renderer/core/css/resolver/cascade_map.h
index e2d341d..104e6dc 100644
--- a/third_party/blink/renderer/core/css/resolver/cascade_map.h
+++ b/third_party/blink/renderer/core/css/resolver/cascade_map.h
@@ -73,6 +73,17 @@
       Node(CascadePriority priority, wtf_size_t next_index)
           : priority(priority), next_index(next_index) {}
 
+      // This terrible sequence convinces the compiler to use 32-bit loads and
+      // stores for copying a Node into the BackingStore, instead of coalescing
+      // them into 64-bit, which would cause a store-to-load forwarding stall.
+      // See crbug.com/1313148 (remove when it has been fixed).
+      Node(Node&& other) {
+        priority = other.priority;
+        next_index = other.next_index + 1;
+        other.next_index = next_index;
+        --next_index;
+      }
+
       CascadePriority priority;
       // 0 for null; Otherwise, next_index - 1 is index in the backing vector.
       wtf_size_t next_index;
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 7c8633f..23bdbf8b 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3179,7 +3179,6 @@
   visitor->Trace(vtt_originating_element_);
   visitor->Trace(parent_for_detached_subtree_);
   visitor->Trace(ua_document_transition_style_);
-  visitor->Trace(style_image_cache_);
   FontSelectorClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 2657b43..cb18cdf 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -47,7 +47,6 @@
 #include "third_party/blink/renderer/core/css/layout_tree_rebuild_root.h"
 #include "third_party/blink/renderer/core/css/pending_sheet_type.h"
 #include "third_party/blink/renderer/core/css/rule_feature_set.h"
-#include "third_party/blink/renderer/core/css/style_image_cache.h"
 #include "third_party/blink/renderer/core/css/style_invalidation_root.h"
 #include "third_party/blink/renderer/core/css/style_recalc_root.h"
 #include "third_party/blink/renderer/core/css/vision_deficiency.h"
@@ -555,13 +554,6 @@
     return document_transition_tags_;
   }
 
-  StyleFetchedImage* CacheStyleImage(FetchParameters& params,
-                                     OriginClean origin_clean,
-                                     bool is_ad_related) {
-    return style_image_cache_.CacheStyleImage(GetDocument(), params,
-                                              origin_clean, is_ad_related);
-  }
-
   void Trace(Visitor*) const override;
   const char* NameInHeapSnapshot() const override { return "StyleEngine"; }
 
@@ -894,7 +886,6 @@
   friend class StyleEngineTest;
   friend class WhitespaceAttacherTest;
   friend class StyleCascadeTest;
-  friend class StyleImageCacheTest;
 
   HeapHashSet<Member<TextTrack>> text_tracks_;
   Member<Element> vtt_originating_element_;
@@ -908,10 +899,6 @@
   // The set of IDs for which ::page-transition-container pseudo elements are
   // generated during a DocumentTransition.
   Vector<AtomicString> document_transition_tags_;
-
-  // Cache for sharing StyleFetchedImage between CSSValues referencing the same
-  // URL.
-  StyleImageCache style_image_cache_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_image_cache.cc b/third_party/blink/renderer/core/css/style_image_cache.cc
deleted file mode 100644
index cecc830..0000000
--- a/third_party/blink/renderer/core/css/style_image_cache.cc
+++ /dev/null
@@ -1,32 +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/renderer/core/css/style_image_cache.h"
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
-#include "third_party/blink/renderer/core/style/style_fetched_image.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
-
-namespace blink {
-
-StyleFetchedImage* StyleImageCache::CacheStyleImage(Document& document,
-                                                    FetchParameters& params,
-                                                    OriginClean origin_clean,
-                                                    bool is_ad_related) {
-  auto result = fetched_image_map_.insert(params.Url(), nullptr);
-  if (result.is_new_entry || !result.stored_value->value) {
-    result.stored_value->value = MakeGarbageCollected<StyleFetchedImage>(
-        ImageResourceContent::Fetch(params, document.Fetcher()), document,
-        params.GetImageRequestBehavior() == FetchParameters::kDeferImageLoad,
-        origin_clean == OriginClean::kTrue, is_ad_related, params.Url());
-  }
-  return result.stored_value->value;
-}
-
-void StyleImageCache::Trace(Visitor* visitor) const {
-  visitor->Trace(fetched_image_map_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_image_cache.h b/third_party/blink/renderer/core/css/style_image_cache.h
deleted file mode 100644
index 935b9034..0000000
--- a/third_party/blink/renderer/core/css/style_image_cache.h
+++ /dev/null
@@ -1,50 +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_RENDERER_CORE_CSS_STYLE_IMAGE_CACHE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_IMAGE_CACHE_H_
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/css/css_origin_clean.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class Document;
-class FetchParameters;
-class StyleFetchedImage;
-
-// A per-StyleEngine cache for StyleImages. A CSSImageValue points to a
-// StyleImage, but different CSSImageValue objects with the same URL would not
-// have shared the same StyleImage without this cache.
-class CORE_EXPORT StyleImageCache {
-  DISALLOW_NEW();
-
- public:
-  StyleImageCache() = default;
-
-  // Look up an existing StyleFetchedImage in the cache, or create a new one,
-  // add it to the cache, and start the fetch.
-  StyleFetchedImage* CacheStyleImage(Document&,
-                                     FetchParameters&,
-                                     OriginClean,
-                                     bool is_ad_related);
-
-  void Trace(Visitor*) const;
-
- private:
-  // Map from URL to style image. A weak reference makes sure the entry is
-  // removed when no style declarations nor computed styles have a reference to
-  // the image.
-  HeapHashMap<KURL, WeakMember<StyleFetchedImage>> fetched_image_map_;
-
-  friend class StyleImageCacheTest;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_IMAGE_CACHE_H_
diff --git a/third_party/blink/renderer/core/css/style_image_cache_test.cc b/third_party/blink/renderer/core/css/style_image_cache_test.cc
deleted file mode 100644
index ee3bdd9..0000000
--- a/third_party/blink/renderer/core/css/style_image_cache_test.cc
+++ /dev/null
@@ -1,109 +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/renderer/core/css/style_image_cache.h"
-
-#include "third_party/blink/renderer/core/css/style_engine.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/element.h"
-#include "third_party/blink/renderer/core/dom/node_computed_style.h"
-#include "third_party/blink/renderer/core/style/style_fetched_image.h"
-#include "third_party/blink/renderer/core/testing/page_test_base.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
-
-namespace blink {
-
-class StyleImageCacheTest : public PageTestBase {
- protected:
-  void SetUp() override {
-    PageTestBase::SetUp();
-    GetDocument().SetBaseURLOverride(KURL("http://test.com"));
-  }
-  const HeapHashMap<KURL, WeakMember<StyleFetchedImage>>& FetchedImageMap() {
-    return GetDocument().GetStyleEngine().style_image_cache_.fetched_image_map_;
-  }
-};
-
-TEST_F(StyleImageCacheTest, DuplicateBackgroundImageURLs) {
-  SetBodyInnerHTML(R"HTML(
-    <style>
-      .rule1 { background-image: url(url.png) }
-      .rule2 { background-image: url(url.png) }
-    </style>
-    <div id="target"></div>
-  )HTML");
-
-  Element* target = GetDocument().getElementById("target");
-  ASSERT_TRUE(target);
-  ASSERT_FALSE(target->ComputedStyleRef().BackgroundLayers().GetImage());
-
-  target->setAttribute(blink::html_names::kClassAttr, "rule1");
-  UpdateAllLifecyclePhasesForTest();
-
-  StyleImage* rule1_image =
-      target->ComputedStyleRef().BackgroundLayers().GetImage();
-  EXPECT_TRUE(rule1_image);
-
-  target->setAttribute(blink::html_names::kClassAttr, "rule2");
-  UpdateAllLifecyclePhasesForTest();
-
-  StyleImage* rule2_image =
-      target->ComputedStyleRef().BackgroundLayers().GetImage();
-  EXPECT_EQ(rule1_image, rule2_image);
-}
-
-TEST_F(StyleImageCacheTest, CustomPropertyURL) {
-  SetBodyInnerHTML(R"HTML(
-    <style>
-      :root { --bg: url(url.png) }
-      #target { background-image: var(--bg) }
-      .green { background-color: green }
-    </style>
-    <div id="target"></div>
-  )HTML");
-
-  Element* target = GetDocument().getElementById("target");
-
-  StyleImage* initial_image =
-      target->ComputedStyleRef().BackgroundLayers().GetImage();
-  EXPECT_TRUE(initial_image);
-
-  target->setAttribute(blink::html_names::kClassAttr, "green");
-  UpdateAllLifecyclePhasesForTest();
-
-  StyleImage* image_after_recalc =
-      target->ComputedStyleRef().BackgroundLayers().GetImage();
-  EXPECT_EQ(initial_image, image_after_recalc);
-}
-
-TEST_F(StyleImageCacheTest, WeakReferenceGC) {
-  SetBodyInnerHTML(R"HTML(
-    <style id="sheet">
-      #target1 { background-image: url(url.png) }
-      #target2 { background-image: url(url2.png) }
-    </style>
-    <div id="target1"></div>
-    <div id="target2"></div>
-  )HTML");
-  UpdateAllLifecyclePhasesForTest();
-
-  EXPECT_TRUE(FetchedImageMap().Contains(KURL("http://test.com/url.png")));
-  EXPECT_TRUE(FetchedImageMap().Contains(KURL("http://test.com/url2.png")));
-  EXPECT_EQ(FetchedImageMap().size(), 2u);
-
-  Element* sheet = GetDocument().getElementById("sheet");
-  ASSERT_TRUE(sheet);
-  sheet->remove();
-  UpdateAllLifecyclePhasesForTest();
-  ThreadState::Current()->CollectAllGarbageForTesting();
-
-  // After the sheet has been removed, the lifecycle update and garbage
-  // collection have been run, the weak references in the cache should have been
-  // collected.
-  EXPECT_FALSE(FetchedImageMap().Contains(KURL("http://test.com/url.png")));
-  EXPECT_FALSE(FetchedImageMap().Contains(KURL("http://test.com/url2.png")));
-  EXPECT_EQ(FetchedImageMap().size(), 0u);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.cc b/third_party/blink/renderer/core/document_transition/document_transition.cc
index 5f0dac5..067be55 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition.cc
@@ -398,18 +398,18 @@
     style_tracker_->VerifySharedElements();
 }
 
-void DocumentTransition::RunPostLayoutSteps() {
+void DocumentTransition::RunPostPrePaintSteps() {
   DCHECK(document_->Lifecycle().GetState() >=
-         DocumentLifecycle::LifecycleState::kLayoutClean);
+         DocumentLifecycle::LifecycleState::kPrePaintClean);
 
   if (style_tracker_) {
-    style_tracker_->RunPostLayoutSteps();
+    style_tracker_->RunPostPrePaintSteps();
     // If we don't have active animations, schedule a frame to end the
     // transition. Note that if we don't have start_promise_resolver_ we don't
     // need to finish the animation, since it should already be done. See the
     // DCHECK below.
     //
-    // TODO(vmpstr): Note that RunPostLayoutSteps can happen multiple times
+    // TODO(vmpstr): Note that RunPostPrePaintSteps can happen multiple times
     // during a lifecycle update. These checks don't have to happen here, and
     // could perhaps be moved to DidFinishLifecycleUpdate.
     //
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.h b/third_party/blink/renderer/core/document_transition/document_transition.h
index 373bf76f..fffb50a710 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition.h
@@ -99,7 +99,7 @@
   void VerifySharedElements();
 
   // Dispatched during a lifecycle update after clean layout.
-  void RunPostLayoutSteps();
+  void RunPostPrePaintSteps();
 
   // Creates a pseudo element for the given |pseudo_id|.
   PseudoElement* CreatePseudoElement(
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
index 9f8cf9fb..113ac6f 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
@@ -540,11 +540,7 @@
   return nullptr;
 }
 
-void DocumentTransitionStyleTracker::RunPostLayoutSteps() {
-  // TODO(khushalsagar) : This callback needs to be switched to PostPrepaint or
-  // PostPaint since we need to paint the pseudo elements in the same order in
-  // which they appear in the DOM. See crbug.com/1275740.
-
+void DocumentTransitionStyleTracker::RunPostPrePaintSteps() {
   bool needs_style_invalidation = false;
 
   for (auto& entry : element_data_map_) {
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
index 41d81f9..fe0fffc 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
@@ -79,9 +79,9 @@
       PseudoId pseudo_id,
       const AtomicString& document_transition_tag);
 
-  // Dispatched after the layout lifecycle stage after each rendering lifecycle
-  // update when a transition is in progress.
-  void RunPostLayoutSteps();
+  // Dispatched after the pre-paint lifecycle stage after each rendering
+  // lifecycle update when a transition is in progress.
+  void RunPostPrePaintSteps();
 
   // Provides a UA stylesheet applied to ::transition* pseudo elements.
   const String& UAStyleSheet();
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index c410ec7..5004b164 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2400,7 +2400,7 @@
       !FrameLoader::NeedsHistoryItemRestore(document_loader->LoadType()))
     return;
 
-  auto* history_item = document_loader->GetHistoryItem();
+  HistoryItem* history_item = document_loader->GetHistoryItem();
 
   if (!history_item || !history_item->GetViewState())
     return;
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 9d57501..7a8b6c0 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -30,6 +30,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "third_party/blink/public/common/input/web_keyboard_event.h"
+#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
diff --git a/third_party/blink/renderer/core/editing/commands/insert_commands.cc b/third_party/blink/renderer/core/editing/commands/insert_commands.cc
index b9905613..78b6c251 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_commands.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/core/editing/selection_template.h"
 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
 #include "third_party/blink/renderer/core/html/html_hr_element.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
diff --git a/third_party/blink/renderer/core/editing/commands/style_commands.cc b/third_party/blink/renderer/core/editing/commands/style_commands.cc
index 95d993e..b2069c88 100644
--- a/third_party/blink/renderer/core/editing/commands/style_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/style_commands.cc
@@ -31,6 +31,7 @@
 
 #include "third_party/blink/renderer/core/editing/commands/style_commands.h"
 
+#include "mojo/public/mojom/base/text_direction.mojom-blink.h"
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 651a8c9..6f29a71 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -28,6 +28,7 @@
 
 #include "base/memory/values_equivalent.h"
 #include "base/stl_util.h"
+#include "mojo/public/mojom/base/text_direction.mojom-blink.h"
 #include "third_party/blink/renderer/core/css/css_color.h"
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index d1dde32..188f1379 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -26,6 +26,7 @@
 
 #include "third_party/blink/renderer/core/editing/editor.h"
 
+#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/clipboard/data_object.h"
 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
diff --git a/third_party/blink/renderer/core/editing/editor.h b/third_party/blink/renderer/core/editing/editor.h
index 6556bfe..7b836dc7 100644
--- a/third_party/blink/renderer/core/editing/editor.h
+++ b/third_party/blink/renderer/core/editing/editor.h
@@ -30,7 +30,6 @@
 
 #include "mojo/public/mojom/base/text_direction.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
-#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/editing_style.h"
 #include "third_party/blink/renderer/core/editing/finder/find_options.h"
@@ -52,6 +51,7 @@
 class KeyboardEvent;
 class KillRing;
 class SpellChecker;
+enum class SyncCondition;
 class CSSPropertyValueSet;
 class TextEvent;
 class UndoStack;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 4f51114..0dc6c707 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -159,6 +159,7 @@
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
 #include "third_party/blink/renderer/core/timing/window_performance.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
@@ -1787,6 +1788,30 @@
 
   RuntimeEnabledFeatures::SetTranslateServiceEnabled(
       prefs.translate_service_available);
+
+#if BUILDFLAG(IS_WIN)
+  if (web_view_impl->GetPage() &&
+      base::FeatureList::IsEnabled(features::kPrewarmDefaultFontFamilies)) {
+    if (auto* prewarmer = WebFontRendering::GetFontPrewarmer()) {
+      GenericFontFamilySettings& font_settings =
+          web_view_impl->GetPage()
+              ->GetSettings()
+              .GetGenericFontFamilySettings();
+      if (features::kPrewarmStandard.Get())
+        prewarmer->PrewarmFamily(font_settings.Standard());
+      if (features::kPrewarmFixed.Get())
+        prewarmer->PrewarmFamily(font_settings.Fixed());
+      if (features::kPrewarmSerif.Get())
+        prewarmer->PrewarmFamily(font_settings.Serif());
+      if (features::kPrewarmSansSerif.Get())
+        prewarmer->PrewarmFamily(font_settings.SansSerif());
+      if (features::kPrewarmCursive.Get())
+        prewarmer->PrewarmFamily(font_settings.Cursive());
+      if (features::kPrewarmFantasy.Get())
+        prewarmer->PrewarmFamily(font_settings.Fantasy());
+    }
+  }
+#endif
 }
 
 void WebViewImpl::ThemeChanged() {
diff --git a/third_party/blink/renderer/core/frame/attribution_aggregatable_trigger_data_fuzzer.cc b/third_party/blink/renderer/core/frame/attribution_aggregatable_trigger_data_fuzzer.cc
new file mode 100644
index 0000000..5bc5237b
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/attribution_aggregatable_trigger_data_fuzzer.cc
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+
+#include "testing/libfuzzer/proto/json.pb.h"
+#include "testing/libfuzzer/proto/json_proto_converter.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-blink.h"
+#include "third_party/blink/renderer/core/frame/attribution_response_parsing.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+DEFINE_PROTO_FUZZER(const json_proto::JsonValue& json_value) {
+  static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+
+  json_proto::JsonProtoConverter converter;
+  std::string native_input = converter.Convert(json_value);
+
+  if (getenv("LPM_DUMP_NATIVE_INPUT"))
+    std::cout << native_input << std::endl;
+
+  const AtomicString input(native_input.c_str());
+  WTF::Vector<mojom::blink::AttributionAggregatableTriggerDataPtr> output;
+  attribution_response_parsing::ParseAttributionAggregatableTriggerData(input,
+                                                                        output);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/attribution_aggregatable_values_fuzzer.cc b/third_party/blink/renderer/core/frame/attribution_aggregatable_values_fuzzer.cc
new file mode 100644
index 0000000..ccbfb36
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/attribution_aggregatable_values_fuzzer.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+
+#include "testing/libfuzzer/proto/json.pb.h"
+#include "testing/libfuzzer/proto/json_proto_converter.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-blink.h"
+#include "third_party/blink/renderer/core/frame/attribution_response_parsing.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+DEFINE_PROTO_FUZZER(const json_proto::JsonValue& json_value) {
+  static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+
+  json_proto::JsonProtoConverter converter;
+  std::string native_input = converter.Convert(json_value);
+
+  if (getenv("LPM_DUMP_NATIVE_INPUT"))
+    std::cout << native_input << std::endl;
+
+  const AtomicString input(native_input.c_str());
+  WTF::HashMap<String, uint32_t> output;
+  attribution_response_parsing::ParseAttributionAggregatableValues(input,
+                                                                   output);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_trigger_data_corpus/all_params.textproto b/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_trigger_data_corpus/all_params.textproto
new file mode 100644
index 0000000..bab1d54
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_trigger_data_corpus/all_params.textproto
@@ -0,0 +1,81 @@
+array_value {
+  value {
+    object_value {
+      field {
+        name: "key_piece"
+        value {
+          string_value {
+            value: "0x1"
+          }
+        }
+      }
+      field {
+        name: "source_keys"
+        value {
+          array_value {
+            value {
+              string_value {
+                value: "a"
+              }
+            }
+          }
+        }
+      }
+      field {
+        name: "filters"
+        value {
+          object_value {
+            field {
+              name: "a"
+              value {
+                array_value {
+                  value {
+                    string_value {
+                      value: "x"
+                    }
+                  }
+                }
+              }
+            }
+            field {
+              name: "b"
+              value {
+                array_value {
+                  value {
+                    string_value {
+                      value: "y"
+                    }
+                  }
+                  value {
+                    string_value {
+                      value: "z"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      field {
+        name: "not_filters"
+        value {
+          object_value {
+            field {
+              name: "c"
+              value {
+                array_value {
+                  value {
+                    string_value {
+                      value: "d"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_values_corpus/all_params.textproto b/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_values_corpus/all_params.textproto
new file mode 100644
index 0000000..558cea0b
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/attribution_src/attribution_aggregatable_values_corpus/all_params.textproto
@@ -0,0 +1,18 @@
+object_value {
+  field {
+    name: "a"
+    value {
+      number_value {
+        value: 123
+      }
+    }
+  }
+  field {
+    name: "b"
+    value {
+      number_value {
+        value: 456
+      }
+    }
+  }
+}
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 02b8807..cf424b4 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -512,10 +512,8 @@
   IgnoreOpensDuringUnloadCountIncrementer ignore_opens_during_unload(
       GetDocument());
 
-  // If the frame is detached for a frame swap, the new document that is being
-  // swapped in might need the unload info of the old document.
-  bool need_unload_info_for_new_document = (type == FrameDetachType::kSwap);
-  loader_.DispatchUnloadEvent(need_unload_info_for_new_document);
+  loader_.DispatchUnloadEventAndFillOldDocumentInfoIfNeeded(
+      type == FrameDetachType::kSwap);
   if (evict_cached_session_storage_on_freeze_or_unload_) {
     // Evicts the cached data of Session Storage to avoid reusing old data in
     // the cache after the session storage has been modified by another renderer
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index a162b97..e8d42782 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -1187,7 +1187,7 @@
   // when unloading itself.
   IgnoreOpensDuringUnloadCountIncrementer ignore_opens_during_unload(
       frame_->GetDocument());
-  frame_->Loader().DispatchUnloadEvent(
+  frame_->Loader().DispatchUnloadEventAndFillOldDocumentInfoIfNeeded(
       false /* need_unload_info_for_new_document */);
 
   std::move(completion_callback).Run();
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 346c2f7..f90012f2 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1365,6 +1365,29 @@
   }
 }
 
+bool LocalFrameView::InvalidateViewportConstrainedObjects() {
+  DCHECK(!base::FeatureList::IsEnabled(
+      features::kOptimizeViewportConstrainedPaintInvalidation));
+  bool fast_path_allowed = true;
+  for (const auto& layout_object : *viewport_constrained_objects_) {
+    DCHECK(layout_object->StyleRef().HasViewportConstrainedPosition() ||
+           layout_object->StyleRef().HasStickyConstrainedPosition());
+    DCHECK(layout_object->HasLayer());
+    PaintLayer* layer = To<LayoutBoxModelObject>(layout_object.Get())->Layer();
+    // If the layer has no visible content, then we shouldn't invalidate; but
+    // if we're not compositing-inputs-clean, then we can't query
+    // layer->SubtreeIsInvisible() here.
+    layout_object->SetSubtreeShouldCheckForPaintInvalidation();
+
+    // If the fixed layer has a blur/drop-shadow filter applied on at least one
+    // of its parents, we cannot scroll using the fast path, otherwise the
+    // outsets of the filter will be moved around the page.
+    if (layer->HasAncestorWithFilterThatMovesPixels())
+      fast_path_allowed = false;
+  }
+  return fast_path_allowed;
+}
+
 HitTestResult LocalFrameView::HitTestWithThrottlingAllowed(
     const HitTestLocation& location,
     HitTestRequest::HitTestRequestType request_type) const {
@@ -2522,7 +2545,7 @@
   if (!document_transition_supplement)
     return false;
 
-  document_transition_supplement->GetTransition()->RunPostLayoutSteps();
+  document_transition_supplement->GetTransition()->RunPostPrePaintSteps();
   return Lifecycle().GetState() < DocumentLifecycle::kPrePaintClean;
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index cbf91e0..89f4b06b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -408,6 +408,8 @@
 
   void ScheduleVisualUpdateForPaintInvalidationIfNeeded();
 
+  bool InvalidateViewportConstrainedObjects();
+
   // Perform a hit test on the frame with throttling allowed. Normally, a hit
   // test will do a synchronous lifecycle update to kPrePaintClean with
   // throttling disabled. This will do the same lifecycle update, but with
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 513bee6..77f3044 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -238,7 +238,7 @@
 void DevToolsSession::DidStartProvisionalLoad(LocalFrame* frame) {
   if (v8_session_ && agent_->inspected_frames_->Root() == frame) {
     v8_session_->setSkipAllPauses(true);
-    v8_session_->resume();
+    v8_session_->resume(true /* terminate on resume */);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/grid.cc b/third_party/blink/renderer/core/layout/grid.cc
index e02f922..ce7b0e5 100644
--- a/third_party/blink/renderer/core/layout/grid.cc
+++ b/third_party/blink/renderer/core/layout/grid.cc
@@ -231,7 +231,7 @@
 const GridItemList& ListGrid::Cell(wtf_size_t row_index,
                                    wtf_size_t column_index) const {
   DEFINE_STATIC_LOCAL(const GridItemList, empty_vector, ());
-  for (auto* row = rows_.Head(); row; row = row->Next()) {
+  for (auto* row = rows_->Head(); row; row = row->Next()) {
     if (row->Index() == row_index) {
       GridCell* cell = row->Find(column_index);
       return cell ? cell->Items() : empty_vector;
@@ -243,7 +243,7 @@
 }
 
 ListGrid::GridTrack* ListGrid::InsertTracks(
-    DoublyLinkedList<GridTrack>& tracks,
+    GridLinkedList<GridTrack>& tracks,
     const GridSpan& span,
     GridTrackSizingDirection direction) {
   auto compare_tracks = [](ListGrid::GridTrack* first,
@@ -254,8 +254,8 @@
   wtf_size_t start_line = span.StartLine();
   wtf_size_t end_line = span.EndLine();
 
-  DoublyLinkedList<ListGrid::GridTrack>::AddResult result = tracks.Insert(
-      base::WrapUnique(new GridTrack(start_line, direction)), compare_tracks);
+  GridLinkedList<ListGrid::GridTrack>::AddResult result = tracks.Insert(
+      MakeGarbageCollected<GridTrack>(start_line, direction), compare_tracks);
   auto* track = result.node;
   DCHECK(track);
 
@@ -264,7 +264,7 @@
        ++track_index) {
     if (!iter->Next() || track_index < iter->Next()->Index()) {
       tracks.InsertAfter(
-          base::WrapUnique(new GridTrack(track_index, direction)), iter);
+          MakeGarbageCollected<GridTrack>(track_index, direction), iter);
     }
     iter = iter->Next();
   }
@@ -277,9 +277,9 @@
          area.columns.IsTranslatedDefinite());
   EnsureGridSize(area.rows.EndLine(), area.columns.EndLine());
 
-  GridTrack* first_row = InsertTracks(rows_, area.rows, kForRows);
+  GridTrack* first_row = InsertTracks(*rows_, area.rows, kForRows);
   DCHECK(first_row);
-  GridTrack* first_column = InsertTracks(columns_, area.columns, kForColumns);
+  GridTrack* first_column = InsertTracks(*columns_, area.columns, kForColumns);
   DCHECK(first_column);
 
   GridCell* above_cell = nullptr;
@@ -319,16 +319,8 @@
 
 void ListGrid::ClearGridDataStructure() {
   num_rows_ = num_columns_ = 0;
-  while (!rows_.IsEmpty())
-    delete rows_.RemoveHead();
-  DCHECK(rows_.IsEmpty());
-  while (!columns_.IsEmpty())
-    delete columns_.RemoveHead();
-  DCHECK(columns_.IsEmpty());
-}
-
-ListGrid::~ListGrid() {
-  ClearGridDataStructure();
+  rows_->Clear();
+  columns_->Clear();
 }
 
 void ListGrid::GridCell::SetTraversalMode(GridTrackSizingDirection direction) {
@@ -369,7 +361,7 @@
 
   bool is_row_axis = direction_ == kForColumns;
   if (!cell_node_) {
-    auto* track = is_row_axis ? grid_.columns_.Head() : grid_.rows_.Head();
+    auto* track = is_row_axis ? grid_.columns_->Head() : grid_.rows_->Head();
     DCHECK(track);
     const wtf_size_t fixed_index = is_row_axis ? column_index_ : row_index_;
     while (track && track->Index() != fixed_index)
diff --git a/third_party/blink/renderer/core/layout/grid.h b/third_party/blink/renderer/core/layout/grid.h
index 3a9cb0c..539df80 100644
--- a/third_party/blink/renderer/core/layout/grid.h
+++ b/third_party/blink/renderer/core/layout/grid.h
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/doubly_linked_list.h"
 #include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -160,7 +159,10 @@
 // the track index.
 class CORE_EXPORT ListGrid final : public Grid {
  public:
-  explicit ListGrid(const LayoutGrid* grid) : Grid(grid) {}
+  explicit ListGrid(const LayoutGrid* grid)
+      : Grid(grid),
+        rows_(MakeGarbageCollected<GridLinkedList<GridTrack>>()),
+        columns_(MakeGarbageCollected<GridLinkedList<GridTrack>>()) {}
 
   wtf_size_t NumTracks(GridTrackSizingDirection direction) const override {
     return direction == kForRows ? num_rows_ : num_columns_;
@@ -170,8 +172,6 @@
   void EnsureGridSize(wtf_size_t maximum_row_size,
                       wtf_size_t maximum_column_size) override;
 
-  ~ListGrid() final;
-
   // This is the class representing a cell in the grid. GridCell's are
   // only created for those cells which do have items inside. Each
   // GridCell will be part of two different DLL, one representing the
@@ -235,10 +235,7 @@
   // index of the cell in the orthogonal direction, i.e., the list of
   // cells in a GridTrack representing a column will be sorted by
   // their row index.
-  class CORE_EXPORT GridTrack final : public DoublyLinkedListNode<GridTrack> {
-    USING_FAST_MALLOC(GridTrack);
-    friend class WTF::DoublyLinkedListNode<GridTrack>;
-
+  class CORE_EXPORT GridTrack final : public GridLinkedListNodeBase<GridTrack> {
    public:
     GridTrack(wtf_size_t index, GridTrackSizingDirection direction)
         : cells_(MakeGarbageCollected<GridLinkedList<GridCell>>()),
@@ -246,6 +243,12 @@
           direction_(direction) {}
 
     wtf_size_t Index() const { return index_; }
+
+    void Trace(Visitor* visitor) const final {
+      visitor->Trace(cells_);
+      GridLinkedListNodeBase<GridTrack>::Trace(visitor);
+    }
+
     GridLinkedList<GridCell>::AddResult Insert(GridCell*);
     GridLinkedList<GridCell>::AddResult InsertAfter(GridCell* cell,
                                                     GridCell* insertion_point);
@@ -255,28 +258,25 @@
     const GridLinkedList<GridCell>& Cells() const { return *cells_; }
 
    private:
-    Persistent<GridLinkedList<GridCell>> cells_;
+    Member<GridLinkedList<GridCell>> cells_;
     wtf_size_t index_;
     GridTrackSizingDirection direction_;
-
-    GridTrack* prev_;
-    GridTrack* next_;
   };
 
  private:
   friend class ListGridIterator;
 
   // Returns a pointer to the first track.
-  GridTrack* InsertTracks(DoublyLinkedList<GridTrack>&,
+  GridTrack* InsertTracks(GridLinkedList<GridTrack>&,
                           const GridSpan&,
                           GridTrackSizingDirection);
 
   void ClearGridDataStructure() override;
   void ConsolidateGridDataStructure() override {}
 
-  const DoublyLinkedList<GridTrack>& Tracks(
+  const GridLinkedList<GridTrack>& Tracks(
       GridTrackSizingDirection direction) const {
-    return direction == kForRows ? rows_ : columns_;
+    return direction == kForRows ? *rows_ : *columns_;
   }
 
   std::unique_ptr<GridIterator> CreateIterator(
@@ -287,8 +287,8 @@
   wtf_size_t num_rows_{0};
   wtf_size_t num_columns_{0};
 
-  DoublyLinkedList<GridTrack> columns_;
-  DoublyLinkedList<GridTrack> rows_;
+  Persistent<GridLinkedList<GridTrack>> rows_;
+  Persistent<GridLinkedList<GridTrack>> columns_;
 };
 
 class ListGridIterator final : public Grid::GridIterator {
diff --git a/third_party/blink/renderer/core/layout/grid_linked_list.h b/third_party/blink/renderer/core/layout/grid_linked_list.h
index ca72023..c6c075b 100644
--- a/third_party/blink/renderer/core/layout/grid_linked_list.h
+++ b/third_party/blink/renderer/core/layout/grid_linked_list.h
@@ -102,6 +102,7 @@
   NodeType* Tail() const { return tail_; }
 
   bool IsEmpty() { return !head_; }
+  void Clear();
 
   // Returns the size of the list. O(n).
   int Size();
@@ -157,6 +158,12 @@
 };
 
 template <typename NodeType>
+void GridLinkedList<NodeType>::Clear() {
+  head_.Clear();
+  tail_.Clear();
+}
+
+template <typename NodeType>
 int GridLinkedList<NodeType>::Size() {
   int len = 0;
   NodeType* node = head_;
diff --git a/third_party/blink/renderer/core/layout/grid_linked_list_test.cc b/third_party/blink/renderer/core/layout/grid_linked_list_test.cc
index 1563470..4bc0bfa 100644
--- a/third_party/blink/renderer/core/layout/grid_linked_list_test.cc
+++ b/third_party/blink/renderer/core/layout/grid_linked_list_test.cc
@@ -152,6 +152,11 @@
   EXPECT_FALSE(gll->Insert(num2_again, IntNode::Compare));
   EXPECT_EQ(gll->Insert(num2_again, IntNode::Compare).node, num2);
   EXPECT_TRUE(IsSorted(gll, IntNode::Compare));
+
+  gll->Clear();
+  DCHECK(gll->IsEmpty());
+  ThreadState::Current()->CollectAllGarbageForTesting();
+  EXPECT_EQ(4, IntNode::destructor_calls);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/grid_test.cc b/third_party/blink/renderer/core/layout/grid_test.cc
index a88a4c2..53360b7a 100644
--- a/third_party/blink/renderer/core/layout/grid_test.cc
+++ b/third_party/blink/renderer/core/layout/grid_test.cc
@@ -339,14 +339,14 @@
   if (RuntimeEnabledFeatures::LayoutNGEnabled())
     return;
 
-  auto track = base::WrapUnique(new ListGrid::GridTrack(0, kForColumns));
-  ListGrid::GridCell* cell = MakeGarbageCollected<ListGrid::GridCell>(0, 0);
+  auto* track = MakeGarbageCollected<ListGrid::GridTrack>(0, kForColumns);
+  auto* cell = MakeGarbageCollected<ListGrid::GridCell>(0, 0);
 
   auto result = track->Insert(cell);
   EXPECT_TRUE(result.is_new_entry);
   EXPECT_EQ(cell, result.node);
 
-  ListGrid::GridCell* cell2 = MakeGarbageCollected<ListGrid::GridCell>(1, 0);
+  auto* cell2 = MakeGarbageCollected<ListGrid::GridCell>(1, 0);
   result = track->Insert(cell2);
   EXPECT_TRUE(result.is_new_entry);
   EXPECT_EQ(cell2, result.node);
@@ -355,7 +355,7 @@
   EXPECT_FALSE(result.is_new_entry);
   EXPECT_EQ(cell2, result.node);
 
-  ListGrid::GridCell* cell3 = MakeGarbageCollected<ListGrid::GridCell>(2, 0);
+  auto* cell3 = MakeGarbageCollected<ListGrid::GridCell>(2, 0);
   result = track->InsertAfter(cell3, cell2);
   EXPECT_TRUE(result.is_new_entry);
   EXPECT_EQ(cell3, result.node);
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 06c43a2..63dd819 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
@@ -3016,8 +3017,6 @@
     if (new_child->IsInline() && !new_child->IsLayoutNGOutsideListMarker()) {
       // No suitable existing anonymous box - create a new one.
       auto* new_block = To<LayoutBlockFlow>(CreateAnonymousBlock());
-      if (new_block->IsLayoutNGObject() && IsLayoutFlowThread())
-        new_block->SetIsAnonymousNGMulticolInlineWrapper();
       LayoutBox::AddChild(new_block, before_child);
       // Reparent adjacent floating or out-of-flow siblings to the new box.
       new_block->ReparentPrecedingFloatingOrOutOfFlowSiblings();
@@ -4500,20 +4499,6 @@
 
   flow_thread->Populate();
 
-  if (auto* child = DynamicTo<LayoutNGBlockFlow>(flow_thread->FirstChild())) {
-    // Attempt to identify the anonymous inline wrapper that may have been
-    // created, if all multicol children are inline. The child insertion
-    // machinery (invoked above, when adding |flow_thread|) may already have
-    // inserted an anonymous block for other reasons (when the flow thread
-    // temporarily becomes a sibling of the actual DOM children), in which case
-    // we haven't tagged this anonymous wrapper properly. Do it now. This is
-    // important for OOF descendants, as this anonymous wrapper may act as their
-    // containing block, but that will only happen if it is tagged correctly;
-    // see LayoutObject::FindNonAnonymousContainingBlock().
-    if (child->IsAnonymousBlock() && !child->NextSibling())
-      child->SetIsAnonymousNGMulticolInlineWrapper();
-  }
-
   LayoutBlockFlowRareData& rare_data = EnsureRareData();
   DCHECK(!rare_data.multi_column_flow_thread_);
   rare_data.multi_column_flow_thread_ = flow_thread;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index ccf299a7..601a8f5f 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1593,6 +1593,18 @@
   return ancestor;
 }
 
+bool LayoutObject::IsAnonymousNGMulticolInlineWrapper() const {
+  NOT_DESTROYED();
+  if (!IsLayoutNGBlockFlow() || !IsAnonymousBlock())
+    return false;
+
+  const LayoutBlock* containing_block = ContainingNGBlock();
+  if (!containing_block)
+    return false;
+
+  return containing_block->IsFragmentationContextRoot();
+}
+
 LayoutBlock* LayoutObject::FindNonAnonymousContainingBlock(
     LayoutObject* container,
     AncestorSkipInfo* skip_info) {
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 69d45cf..df4b706 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -2372,6 +2372,8 @@
   // See LayoutBlock.h for some extra explanations on containing blocks.
   LayoutBlock* ContainingBlock(AncestorSkipInfo* = nullptr) const;
 
+  bool IsAnonymousNGMulticolInlineWrapper() const;
+
   // Returns |container|'s containing block.
   static LayoutBlock* FindNonAnonymousContainingBlock(
       LayoutObject* container,
@@ -3739,15 +3741,6 @@
     bitfields_.SetBackgroundIsKnownToBeObscured(b);
   }
 
-  bool IsAnonymousNGMulticolInlineWrapper() const {
-    NOT_DESTROYED();
-    return bitfields_.IsAnonymousNGMulticolInlineWrapper();
-  }
-  void SetIsAnonymousNGMulticolInlineWrapper() {
-    NOT_DESTROYED();
-    bitfields_.SetIsAnonymousNGMulticolInlineWrapper(true);
-  }
-
   // Returns ContainerForAbsolutePosition() if it's a LayoutBlock, or the
   // containing LayoutBlock of it.
   LayoutBlock* ContainingBlockForAbsolutePosition(
@@ -3977,7 +3970,6 @@
           should_assume_paint_offset_translation_for_layout_shift_tracking_(
               false),
           might_traverse_physical_fragments_(false),
-          is_anonymous_ng_multicol_inline_wrapper_(false),
           whitespace_children_may_change_(false),
           needs_devtools_info_(false),
           positioned_state_(kIsStaticallyPositioned),
@@ -4318,11 +4310,6 @@
     ADD_BOOLEAN_BITFIELD(might_traverse_physical_fragments_,
                          MightTraversePhysicalFragments);
 
-    // True if this is an anonymous inline wrapper created for NG, and the
-    // wrapper is a direct child of a multicol.
-    ADD_BOOLEAN_BITFIELD(is_anonymous_ng_multicol_inline_wrapper_,
-                         IsAnonymousNGMulticolInlineWrapper);
-
     // True if children that may affect whitespace have been removed. If true
     // during style recalc, mark ancestors for layout tree rebuild to cause a
     // re-evaluation of whitespace children.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index bae82d06..c886ed7d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -294,11 +294,13 @@
         node.Style().GetPosition() == EPosition::kFixed) {
       // A fixedpos containing block was found in |multicol|. Add the fixedpos
       // as a fragmentainer descendant instead.
+      DCHECK(!candidate.inline_container.container);
       destination_builder->AddOutOfFlowFragmentainerDescendant(
-          {node, candidate.static_position, candidate.inline_container,
+          {node, candidate.static_position, multicol->fixedpos_inline_container,
            candidate.needs_block_offset_adjustment,
            multicol->fixedpos_containing_block,
-           multicol->fixedpos_containing_block});
+           multicol->fixedpos_containing_block,
+           multicol->fixedpos_inline_container});
       continue;
     }
     destination_builder->AddOutOfFlowChildCandidate(candidate);
@@ -344,6 +346,7 @@
     const NGInlineContainer<LogicalOffset>* inline_container,
     LayoutUnit containing_block_adjustment,
     const NGContainingBlock<LogicalOffset>* fixedpos_containing_block,
+    const NGInlineContainer<LogicalOffset>* fixedpos_inline_container,
     LogicalOffset additional_fixedpos_offset) {
   // Calling this method without any work to do is expensive, even if it ends up
   // skipping all its parts (probably due to its size). Make sure that we have a
@@ -394,10 +397,14 @@
       static_position.offset +=
           relative_offset - fixedpos_containing_block->RelativeOffset();
       if (fixedpos_containing_block && fixedpos_containing_block->Fragment()) {
+        NGInlineContainer<LogicalOffset> new_fixedpos_inline_container;
+        if (fixedpos_inline_container)
+          new_fixedpos_inline_container = *fixedpos_inline_container;
         AddOutOfFlowFragmentainerDescendant(
-            {node, static_position, new_inline_container,
+            {node, static_position, new_fixedpos_inline_container,
              /* needs_block_offset_adjustment */ false,
-             *fixedpos_containing_block, *fixedpos_containing_block});
+             *fixedpos_containing_block, *fixedpos_containing_block,
+             new_fixedpos_inline_container});
         continue;
       }
     }
@@ -430,13 +437,17 @@
       LogicalOffset multicol_offset =
           converter.ToLogical(multicol_info->multicol_offset, PhysicalSize());
 
+      LogicalOffset fixedpos_inline_relative_offset = converter.ToLogical(
+          multicol_info->fixedpos_inline_container.relative_offset,
+          PhysicalSize());
+      NGInlineContainer<LogicalOffset> new_fixedpos_inline_container(
+          multicol_info->fixedpos_inline_container.container,
+          fixedpos_inline_relative_offset);
       const NGPhysicalFragment* fixedpos_containing_block_fragment =
           multicol_info->fixedpos_containing_block.Fragment();
-      if (!fixedpos_containing_block_fragment && fragment.GetLayoutObject() &&
-          fragment.GetLayoutObject()->CanContainFixedPositionObjects()) {
-        DCHECK(box_fragment);  // shouldn't be line box by |GetLayoutObject()|.
-        fixedpos_containing_block_fragment = box_fragment;
-      }
+
+      AdjustFixedposContainerInfo(box_fragment, &new_fixedpos_inline_container,
+                                  &fixedpos_containing_block_fragment);
 
       // If a fixedpos containing block was found, the |multicol_offset|
       // should remain relative to the fixedpos containing block. Otherwise,
@@ -473,11 +484,12 @@
       AddMulticolWithPendingOOFs(
           NGBlockNode(multicol.key),
           MakeGarbageCollected<NGMulticolWithPendingOOFs<LogicalOffset>>(
-              multicol_offset, NGContainingBlock<LogicalOffset>(
-                                   fixedpos_containing_block_offset,
-                                   fixedpos_containing_block_rel_offset,
-                                   fixedpos_containing_block_fragment,
-                                   is_inside_column_spanner)));
+              multicol_offset,
+              NGContainingBlock<LogicalOffset>(
+                  fixedpos_containing_block_offset,
+                  fixedpos_containing_block_rel_offset,
+                  fixedpos_containing_block_fragment, is_inside_column_spanner),
+              new_fixedpos_inline_container));
     }
   }
 
@@ -537,13 +549,16 @@
       containing_block_offset += offset;
     containing_block_offset.block_offset += containing_block_adjustment;
 
+    LogicalOffset fixedpos_inline_relative_offset = converter.ToLogical(
+        descendant.fixedpos_inline_container.relative_offset, PhysicalSize());
+    NGInlineContainer<LogicalOffset> new_fixedpos_inline_container(
+        descendant.fixedpos_inline_container.container,
+        fixedpos_inline_relative_offset);
     const NGPhysicalFragment* fixedpos_containing_block_fragment =
         descendant.fixedpos_containing_block.Fragment();
-    if (!fixedpos_containing_block_fragment && fragment.GetLayoutObject() &&
-        fragment.GetLayoutObject()->CanContainFixedPositionObjects()) {
-      DCHECK(box_fragment);  // shouldn't ba line box by |GetLayoutObject()|.
-      fixedpos_containing_block_fragment = box_fragment;
-    }
+
+    AdjustFixedposContainerInfo(box_fragment, &new_fixedpos_inline_container,
+                                &fixedpos_containing_block_fragment);
 
     LogicalOffset fixedpos_containing_block_offset;
     LogicalOffset fixedpos_containing_block_rel_offset;
@@ -588,6 +603,9 @@
     // The relative offset should be applied after fragmentation. Subtract out
     // the accumulated relative offset from the inline container to the
     // containing block so that it can be re-applied at the correct time.
+    // TODO(almaher): We will want to do something similar for
+    // |new_fixedpos_inline_container|, but this would need to happen at a
+    // different point.
     if (new_inline_container.container && box_fragment &&
         containing_block_fragment == box_fragment)
       static_position.offset -= inline_relative_offset;
@@ -602,7 +620,8 @@
              fixedpos_containing_block_offset,
              fixedpos_containing_block_rel_offset,
              fixedpos_containing_block_fragment,
-             fixedpos_container_inside_column_spanner)});
+             fixedpos_container_inside_column_spanner),
+         new_fixedpos_inline_container});
 
     // Remove any descendants that were propagated to the next fragmentation
     // context root (as a result of a column spanner).
@@ -613,6 +632,38 @@
   }
 }
 
+void NGContainerFragmentBuilder::AdjustFixedposContainerInfo(
+    const NGPhysicalFragment* box_fragment,
+    NGInlineContainer<LogicalOffset>* fixedpos_inline_container,
+    const NGPhysicalFragment** fixedpos_containing_block_fragment) const {
+  DCHECK(fixedpos_inline_container);
+  DCHECK(fixedpos_containing_block_fragment);
+  if (!box_fragment)
+    return;
+
+  if (!*fixedpos_containing_block_fragment && box_fragment->GetLayoutObject()) {
+    if (box_fragment->GetLayoutObject()->CanContainFixedPositionObjects()) {
+      if (!fixedpos_inline_container->container &&
+          box_fragment->GetLayoutObject()->IsLayoutInline()) {
+        *fixedpos_inline_container = NGInlineContainer<LogicalOffset>(
+            To<LayoutInline>(box_fragment->GetLayoutObject()),
+            /* relative_offset */ LogicalOffset());
+      } else {
+        *fixedpos_containing_block_fragment = box_fragment;
+      }
+    } else if (fixedpos_inline_container->container) {
+      // Ensure that the inline_container is a continuation root.
+      fixedpos_inline_container->container = To<LayoutInline>(
+          fixedpos_inline_container->container->ContinuationRoot());
+      // Candidates whose containing block is inline are always positioned
+      // inside closest parent block flow.
+      if (box_fragment->GetLayoutObject() ==
+          fixedpos_inline_container->container->ContainingBlock())
+        *fixedpos_containing_block_fragment = box_fragment;
+    }
+  }
+}
+
 const NGLayoutResult* NGContainerFragmentBuilder::Abort(
     NGLayoutResult::EStatus status) {
   return MakeGarbageCollected<NGLayoutResult>(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index d285cce..af41f7af 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -246,6 +246,8 @@
       LayoutUnit containing_block_adjustment = LayoutUnit(),
       const NGContainingBlock<LogicalOffset>* fixedpos_containing_block =
           nullptr,
+      const NGInlineContainer<LogicalOffset>* fixedpos_inline_container =
+          nullptr,
       LogicalOffset additional_fixedpos_offset = LogicalOffset());
 
   void SetIsSelfCollapsing() { is_self_collapsing_ = true; }
@@ -358,6 +360,13 @@
 
   void AddChildInternal(const NGPhysicalFragment*, const LogicalOffset&);
 
+  // Set the fixedpos inline container and containing block based on the current
+  // |box_fragment|.
+  void AdjustFixedposContainerInfo(
+      const NGPhysicalFragment* box_fragment,
+      NGInlineContainer<LogicalOffset>* fixedpos_inline_container,
+      const NGPhysicalFragment** fixedpos_containing_block_fragment) const;
+
   NGLayoutInputNode node_;
   const NGConstraintSpace* space_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 67a4fd54..ad637377 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -956,6 +956,12 @@
           converter.ToLogical(descendant.inline_container.relative_offset,
                               PhysicalSize()));
 
+      NGInlineContainer<LogicalOffset> fixedpos_inline_container(
+          descendant.fixedpos_inline_container.container,
+          converter.ToLogical(
+              descendant.fixedpos_inline_container.relative_offset,
+              PhysicalSize()));
+
       // The static position should remain relative to its containing block
       // fragment.
       const WritingModeConverter containing_block_converter(
@@ -977,7 +983,8 @@
               fixedpos_containing_block_offset,
               fixedpos_containing_block_rel_offset,
               fixedpos_containing_block_fragment,
-              descendant.fixedpos_containing_block.IsInsideColumnSpanner())};
+              descendant.fixedpos_containing_block.IsInsideColumnSpanner()),
+          fixedpos_inline_container};
       oof_nodes_to_layout.push_back(node);
     }
     previous_multicol_break_token = break_token;
@@ -1277,6 +1284,7 @@
                   default_writing_direction_,
                   /* is_fragmentainer_descendant */ containing_block_fragment,
                   oof_node.fixedpos_containing_block,
+                  oof_node.fixedpos_inline_container,
                   oof_node.inline_container.container);
 }
 
@@ -1753,6 +1761,7 @@
         offset_adjustment,
         /* inline_container */ nullptr, containing_block_adjustment,
         &descendant.node_info.fixedpos_containing_block,
+        &descendant.node_info.fixedpos_inline_container,
         additional_fixedpos_offset);
   }
   algorithm->AppendOutOfFlowResult(result);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 65cc131e..ff2589de 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -135,6 +135,7 @@
     const ContainingBlockInfo container_info;
     const WritingDirectionMode default_writing_direction;
     const NGContainingBlock<LogicalOffset>& fixedpos_containing_block;
+    const NGInlineContainer<LogicalOffset>& fixedpos_inline_container;
     bool inline_container = false;
 
     NodeInfo(NGBlockNode node,
@@ -145,6 +146,7 @@
              const WritingDirectionMode default_writing_direction,
              bool is_fragmentainer_descendant,
              const NGContainingBlock<LogicalOffset>& fixedpos_containing_block,
+             const NGInlineContainer<LogicalOffset>& fixedpos_inline_container,
              bool inline_container)
         : node(node),
           constraint_space(constraint_space),
@@ -153,6 +155,7 @@
           container_info(container_info),
           default_writing_direction(default_writing_direction),
           fixedpos_containing_block(fixedpos_containing_block),
+          fixedpos_inline_container(fixedpos_inline_container),
           inline_container(inline_container) {}
 
     void Trace(Visitor* visitor) const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.cc
index 292d3b7d..4258738 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.cc
@@ -26,6 +26,7 @@
   NGPhysicalOutOfFlowPositionedNode::TraceAfterDispatch(visitor);
   visitor->Trace(containing_block);
   visitor->Trace(fixedpos_containing_block);
+  visitor->Trace(fixedpos_inline_container);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
index 52049f4ec..83ba8bc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
@@ -96,19 +96,23 @@
   // If an OOF node in a nested fragmentation context has fixedpos descendants,
   // those descendants will not find their containing block if the containing
   // block lives inside an outer fragmentation context. Thus, we also need to
-  // store information on the containing block for any fixedpos descendants, if
-  // one exists.
+  // store information on the containing block and inline container for any
+  // fixedpos descendants, if one exists.
   NGContainingBlock<OffsetType> fixedpos_containing_block;
+  NGInlineContainer<OffsetType> fixedpos_inline_container;
 
   NGMulticolWithPendingOOFs() = default;
   NGMulticolWithPendingOOFs(
       OffsetType multicol_offset,
-      NGContainingBlock<OffsetType> fixedpos_containing_block)
+      NGContainingBlock<OffsetType> fixedpos_containing_block,
+      NGInlineContainer<OffsetType> fixedpos_inline_container)
       : multicol_offset(multicol_offset),
-        fixedpos_containing_block(fixedpos_containing_block) {}
+        fixedpos_containing_block(fixedpos_containing_block),
+        fixedpos_inline_container(fixedpos_inline_container) {}
 
   void Trace(Visitor* visitor) const {
     visitor->Trace(fixedpos_containing_block);
+    visitor->Trace(fixedpos_inline_container);
   }
 };
 
@@ -186,8 +190,8 @@
 // If an OOF node in a fragmentation context has fixedpos descendants, those
 // descendants will not find their containing block if the containing block
 // lives inside the fragmentation context root. Thus, we also need to store
-// information on the containing block for any fixedpos descendants, if one
-// exists.
+// information on the containing block and inline container for any fixedpos
+// descendants, if one exists.
 //
 // This is struct is allowed to be stored/persisted.
 struct CORE_EXPORT NGPhysicalOOFNodeForFragmentation final
@@ -197,6 +201,7 @@
  public:
   NGContainingBlock<PhysicalOffset> containing_block;
   NGContainingBlock<PhysicalOffset> fixedpos_containing_block;
+  NGInlineContainer<PhysicalOffset> fixedpos_inline_container;
 
   NGPhysicalOOFNodeForFragmentation(
       NGBlockNode node,
@@ -206,12 +211,15 @@
       NGContainingBlock<PhysicalOffset> containing_block =
           NGContainingBlock<PhysicalOffset>(),
       NGContainingBlock<PhysicalOffset> fixedpos_containing_block =
-          NGContainingBlock<PhysicalOffset>())
+          NGContainingBlock<PhysicalOffset>(),
+      NGInlineContainer<PhysicalOffset> fixedpos_inline_container =
+          NGInlineContainer<PhysicalOffset>())
       : NGPhysicalOutOfFlowPositionedNode(node,
                                           static_position,
                                           inline_container),
         containing_block(containing_block),
-        fixedpos_containing_block(fixedpos_containing_block) {
+        fixedpos_containing_block(fixedpos_containing_block),
+        fixedpos_inline_container(fixedpos_inline_container) {
     is_for_fragmentation = true;
   }
 
@@ -235,6 +243,7 @@
   const LayoutUnit fragmentainer_consumed_block_size;
   NGContainingBlock<LogicalOffset> containing_block;
   NGContainingBlock<LogicalOffset> fixedpos_containing_block;
+  NGInlineContainer<LogicalOffset> fixedpos_inline_container;
 
   NGLogicalOutOfFlowPositionedNode(
       NGBlockNode node,
@@ -245,13 +254,16 @@
       NGContainingBlock<LogicalOffset> containing_block =
           NGContainingBlock<LogicalOffset>(),
       NGContainingBlock<LogicalOffset> fixedpos_containing_block =
-          NGContainingBlock<LogicalOffset>())
+          NGContainingBlock<LogicalOffset>(),
+      NGInlineContainer<LogicalOffset> fixedpos_inline_container =
+          NGInlineContainer<LogicalOffset>())
       : box(node.GetLayoutBox()),
         static_position(static_position),
         inline_container(inline_container),
         needs_block_offset_adjustment(needs_block_offset_adjustment),
         containing_block(containing_block),
-        fixedpos_containing_block(fixedpos_containing_block) {
+        fixedpos_containing_block(fixedpos_containing_block),
+        fixedpos_inline_container(fixedpos_inline_container) {
     DCHECK(!inline_container.container ||
            inline_container.container ==
                inline_container.container->ContinuationRoot());
@@ -265,6 +277,7 @@
     visitor->Trace(inline_container);
     visitor->Trace(containing_block);
     visitor->Trace(fixedpos_containing_block);
+    visitor->Trace(fixedpos_inline_container);
   }
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 2840c63..9abcae8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -471,14 +471,19 @@
   fragmented_data->oof_positioned_fragmentainer_descendants.ReserveCapacity(
       builder->oof_positioned_fragmentainer_descendants_.size());
   const PhysicalSize& size = Size();
+  WritingDirectionMode writing_direction = builder->GetWritingDirection();
+  const WritingModeConverter converter(writing_direction, size);
   for (const auto& descendant :
        builder->oof_positioned_fragmentainer_descendants_) {
-    WritingDirectionMode writing_direction = builder->GetWritingDirection();
-    const WritingModeConverter converter(writing_direction, size);
     NGInlineContainer<PhysicalOffset> inline_container(
         descendant.inline_container.container,
         converter.ToPhysical(descendant.inline_container.relative_offset,
                              PhysicalSize()));
+    NGInlineContainer<PhysicalOffset> fixedpos_inline_container(
+        descendant.fixedpos_inline_container.container,
+        converter.ToPhysical(
+            descendant.fixedpos_inline_container.relative_offset,
+            PhysicalSize()));
 
     // The static position should remain relative to the containing block.
     PhysicalSize containing_block_size =
@@ -496,17 +501,23 @@
         PhysicalContainingBlock(builder, size, containing_block_size,
                                 descendant.containing_block),
         PhysicalContainingBlock(builder, size,
-                                descendant.fixedpos_containing_block));
+                                descendant.fixedpos_containing_block),
+        fixedpos_inline_container);
   }
   for (const auto& multicol : builder->multicols_with_pending_oofs_) {
     auto& value = multicol.value;
+    NGInlineContainer<PhysicalOffset> fixedpos_inline_container(
+        value->fixedpos_inline_container.container,
+        converter.ToPhysical(value->fixedpos_inline_container.relative_offset,
+                             PhysicalSize()));
     fragmented_data->multicols_with_pending_oofs.insert(
         multicol.key,
         MakeGarbageCollected<NGMulticolWithPendingOOFs<PhysicalOffset>>(
             value->multicol_offset.ConvertToPhysical(
                 builder->Style().GetWritingDirection(), size, PhysicalSize()),
             PhysicalContainingBlock(builder, size,
-                                    value->fixedpos_containing_block)));
+                                    value->fixedpos_containing_block),
+            fixedpos_inline_container));
   }
   return fragmented_data;
 }
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index 45c516d4..aa83df6 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -33,6 +33,7 @@
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -47,6 +48,7 @@
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 #include "third_party/blink/renderer/platform/network/form_data_encoder.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
diff --git a/third_party/blink/renderer/core/loader/form_submission.h b/third_party/blink/renderer/core/loader/form_submission.h
index 1aaabad..52cdb5d 100644
--- a/third_party/blink/renderer/core/loader/form_submission.h
+++ b/third_party/blink/renderer/core/loader/form_submission.h
@@ -32,13 +32,14 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_
 
 #include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/mojom/frame/policy_container.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-blink-forward.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
-#include "third_party/blink/renderer/core/loader/frame_load_request.h"
+#include "third_party/blink/renderer/core/loader/frame_loader_types.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -48,6 +49,9 @@
 class Frame;
 class HTMLFormControlElement;
 class HTMLFormElement;
+class LocalDOMWindow;
+class ResourceRequest;
+class SourceLocation;
 
 class FormSubmission final : public GarbageCollected<FormSubmission> {
  public:
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 5f74e28..9542e82 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -175,10 +175,15 @@
   return request;
 }
 
-FrameLoader::OldDocumentInfoForCommit*
-    FrameLoader::ScopedOldDocumentInfoForCommitCapturer::g_current_info_ =
+FrameLoader::ScopedOldDocumentInfoForCommitCapturer*
+    FrameLoader::ScopedOldDocumentInfoForCommitCapturer::current_capturer_ =
         nullptr;
 
+FrameLoader::ScopedOldDocumentInfoForCommitCapturer::
+    ~ScopedOldDocumentInfoForCommitCapturer() {
+  current_capturer_ = previous_capturer_;
+}
+
 FrameLoader::FrameLoader(LocalFrame* frame)
     : frame_(frame),
       progress_tracker_(MakeGarbageCollected<ProgressTracker>(frame)),
@@ -372,26 +377,31 @@
   Client()->DidUpdateCurrentHistoryItem();
 }
 
-void FrameLoader::DispatchUnloadEvent(bool need_unload_info_for_new_document) {
+void FrameLoader::DispatchUnloadEventAndFillOldDocumentInfoIfNeeded(
+    bool will_commit_new_document_in_this_frame) {
   FrameNavigationDisabler navigation_disabler(*frame_);
   SaveScrollState();
 
   if (SVGImage::IsInSVGImage(frame_->GetDocument()))
     return;
+
+  // Only fill in the info of the unloading document if it is needed for a new
+  // document committing in this frame (either due to frame swap or committing
+  // a new document in the same FrameLoader). This avoids overwriting the info
+  // saved of a parent frame that's already saved in
+  // ScopedOldDocumentInfoForCommitCapturer when a child frame is being
+  // destroyed due to the parent frame committing. In that case, only the parent
+  // frame needs should fill in the info.
   OldDocumentInfoForCommit* old_document_info =
       ScopedOldDocumentInfoForCommitCapturer::CurrentInfo();
-  if (old_document_info && need_unload_info_for_new_document) {
-    // Only fill in the unload timing info if it is needed for a new document
-    // (due to frame swap or committing a new document in the same FrameLoader).
-    // This avoids overwriting the unload timing value of a parent frame that's
-    // already saved in ScopedOldDocumentInfoForCommitCapturer when a child
-    // frame is being destroyed due to the parent frame committing. In that
-    // case, only the parent frame needs should fill in the unload timing info.
-    frame_->GetDocument()->DispatchUnloadEvents(
-        &old_document_info->unload_timing_info);
-  } else {
+  if (!old_document_info || !will_commit_new_document_in_this_frame) {
     frame_->GetDocument()->DispatchUnloadEvents(nullptr);
+    return;
   }
+  old_document_info->history_item = GetDocumentLoader()->GetHistoryItem();
+
+  frame_->GetDocument()->DispatchUnloadEvents(
+      &old_document_info->unload_timing_info);
 }
 
 void FrameLoader::DidExplicitOpen() {
@@ -1034,12 +1044,6 @@
   FillStaticResponseIfNeeded(navigation_params.get(), frame_);
   AssertCanNavigate(navigation_params.get(), frame_);
 
-  // Keep track of the current Document HistoryItem as the new DocumentLoader
-  // might need to copy state from it. Note that the current DocumentLoader
-  // should always exist, as the initial empty document is committed through
-  // FrameLoader::Init.
-  HistoryItem* previous_history_item = GetDocumentLoader()->GetHistoryItem();
-
   // If this is a javascript: URL or XSLT commit, we must copy the ExtraData
   // from the previous DocumentLoader to ensure the new DocumentLoader behaves
   // the same way as the previous one.
@@ -1052,11 +1056,13 @@
     }
   }
 
-  // Create the OldDocumentInfoForCommit for the old document, and save it in
-  // ScopedOldDocumentInfoForCommitCapturer, so that the old document can access
-  // it and fill in the information.
+  // Create the OldDocumentInfoForCommit for the old document (that might be in
+  // another FrameLoader) and save it in ScopedOldDocumentInfoForCommitCapturer,
+  // so that the old document can access it and fill in the information as it
+  // is being unloaded/swapped out.
   ScopedOldDocumentInfoForCommitCapturer scoped_old_document_info(
-      OldDocumentInfoForCommit(SecurityOrigin::Create(navigation_params->url)));
+      MakeGarbageCollected<OldDocumentInfoForCommit>(
+          SecurityOrigin::Create(navigation_params->url)));
 
   FrameSwapScope frame_swap_scope(frame_owner);
   {
@@ -1147,8 +1153,10 @@
       frame_, navigation_type, std::move(navigation_params),
       std::move(policy_container), std::move(extra_data));
 
-  CommitDocumentLoader(new_document_loader, previous_history_item,
-                       commit_reason);
+  CommitDocumentLoader(
+      new_document_loader,
+      ScopedOldDocumentInfoForCommitCapturer::CurrentInfo()->history_item,
+      commit_reason);
 
   RestoreScrollPositionAndViewState();
 
@@ -1221,7 +1229,7 @@
   // Don't allow this frame to navigate anymore. This line is needed for
   // navigation triggered from children's unload handlers. Blocking navigations
   // triggered from this frame's unload handler is already covered in
-  // DispatchUnloadEvent().
+  // DispatchUnloadEventAndFillOldDocumentInfoIfNeeded().
   FrameNavigationDisabler navigation_disabler(*frame_);
   // Don't allow any new child frames to load in this frame: attaching a new
   // child frame during or after detaching children results in an attached frame
@@ -1232,12 +1240,13 @@
   // both when unloading itself and when unloading its descendants.
   IgnoreOpensDuringUnloadCountIncrementer ignore_opens_during_unload(
       frame_->GetDocument());
-  DispatchUnloadEvent(true /* need_unload_info_for_new_document */);
+  DispatchUnloadEventAndFillOldDocumentInfoIfNeeded(
+      true /* will_commit_new_document_in_this_frame */);
   frame_->DetachChildren();
-  // The previous calls to dispatchUnloadEvent() and detachChildren() can
-  // execute arbitrary script via things like unload events. If the executed
-  // script causes the current frame to be detached, we need to abandon the
-  // current load.
+  // The previous calls to DispatchUnloadEventAndFillOldDocumentInfoIfNeeded()
+  // and detachChildren() can execute arbitrary script via things like unload
+  // events. If the executed script causes the current frame to be detached, we
+  // need to abandon the current load.
   if (!frame_->Client())
     return false;
   // FrameNavigationDisabler should prevent another load from starting.
@@ -1811,4 +1820,8 @@
     : unload_timing_info(
           UnloadEventTimingInfo(std::move(new_document_origin))) {}
 
+void FrameLoader::OldDocumentInfoForCommit::Trace(Visitor* visitor) const {
+  visitor->Trace(history_item);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index e065aa3..91de86a 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -192,13 +192,13 @@
 
   bool ShouldClose(bool is_reload = false);
 
-  // Dispatches the Unload event for the current document. If this is due to the
-  // commit of a navigation, `need_unload_info_for_new_document` will be true.
-  // If the new document is allowed to access the unload timings of the current
-  // document (i.e. it's same-origin), the unload timing info in the current
-  // OldDocumentInfoForCommit (from ScopedOldDocumentInfoForCommitCapturer)
-  // will be modified to reflect the unload timing of this document.
-  void DispatchUnloadEvent(bool need_unload_info_for_new_document);
+  // Dispatches the Unload event for the current document and fills in this
+  // document's info in OldDocumentInfoForCommit if
+  // `will_commit_new_document_in_this_frame` is true (which will only be
+  // the case when the current document in this frame is being unloaded for
+  // committing a new document).
+  void DispatchUnloadEventAndFillOldDocumentInfoIfNeeded(
+      bool will_commit_new_document_in_this_frame);
 
   bool AllowPlugins();
 
@@ -258,6 +258,25 @@
 
   mojo::PendingRemote<blink::mojom::CodeCacheHost> CreateWorkerCodeCacheHost();
 
+  // Contains information related to the previous document in the frame, to be
+  // given to the next document that is going to commit in this FrameLoader.
+  // Note that the "previous document" might not necessarily use the same
+  // FrameLoader as this one, e.g. in case of local RenderFrame swap.
+  struct OldDocumentInfoForCommit : GarbageCollected<OldDocumentInfoForCommit> {
+    explicit OldDocumentInfoForCommit(
+        scoped_refptr<SecurityOrigin> new_document_origin);
+    void Trace(Visitor* visitor) const;
+    // The unload timing info of the previous document in the frame. The new
+    // document can access this information if it is a same-origin, to be
+    // exposed through the Navigation Timing API.
+    UnloadEventTimingInfo unload_timing_info;
+    // The HistoryItem of the previous document in the frame. Some of the state
+    // from the old document's HistoryItem will be copied to the new document
+    // e.g. history.state will be copied on same-URL navigations. See also
+    // https://github.com/whatwg/html/issues/6213.
+    Member<HistoryItem> history_item;
+  };
+
  private:
   bool AllowRequestForThisFrame(const FrameLoadRequest&);
 
@@ -350,41 +369,31 @@
   // size of this set is capped, after which no more warnings are printed.
   HashSet<String> tls_version_warning_origins_;
 
-  // Contains information related to the previous document in the frame, to be
-  // given to the next document that is going to commit in this FrameLoader.
-  // Note that the "previous document" might not necessarily use the same
-  // FrameLoader as this one, e.g. in case of local RenderFrame swap.
-  struct OldDocumentInfoForCommit {
-    explicit OldDocumentInfoForCommit(
-        scoped_refptr<SecurityOrigin> new_document_origin);
-    // The unload timing info of the previous document in the frame.
-    UnloadEventTimingInfo unload_timing_info;
-  };
-
-  // Owns the OldDocumentInfoForCommit and exposes it through `g_current_info_`
+  // Owns the OldDocumentInfoForCommit and exposes it through `info_`
   // so that both the unloading old document and the committing new document
   // can access and modify the value, without explicitly passing it between
   // them on unload/commit time.
   class ScopedOldDocumentInfoForCommitCapturer {
+    STACK_ALLOCATED();
+
    public:
     explicit ScopedOldDocumentInfoForCommitCapturer(
-        const OldDocumentInfoForCommit& info)
-        : info_(info), previous_info_(g_current_info_) {
-      g_current_info_ = &info_;
+        OldDocumentInfoForCommit* info)
+        : info_(info), previous_capturer_(current_capturer_) {
+      current_capturer_ = this;
     }
 
-    ~ScopedOldDocumentInfoForCommitCapturer() {
-      g_current_info_ = previous_info_;
-    }
+    ~ScopedOldDocumentInfoForCommitCapturer();
 
     // The last OldDocumentInfoForCommit set for `info_` that is still in scope.
-    static OldDocumentInfoForCommit* CurrentInfo() { return g_current_info_; }
+    static OldDocumentInfoForCommit* CurrentInfo() {
+      return current_capturer_ ? current_capturer_->info_ : nullptr;
+    }
 
    private:
-    OldDocumentInfoForCommit info_;
-    OldDocumentInfoForCommit* previous_info_;
-
-    static OldDocumentInfoForCommit* g_current_info_;
+    OldDocumentInfoForCommit* info_;
+    ScopedOldDocumentInfoForCommitCapturer* previous_capturer_;
+    static ScopedOldDocumentInfoForCommitCapturer* current_capturer_;
   };
 };
 
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc b/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
index 4dca667..041af01 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
@@ -38,7 +38,8 @@
 bool NavigationHistoryEntry::sameDocument() const {
   if (!DomWindow())
     return false;
-  auto* current_item = DomWindow()->document()->Loader()->GetHistoryItem();
+  HistoryItem* current_item =
+      DomWindow()->document()->Loader()->GetHistoryItem();
   return current_item->DocumentSequenceNumber() ==
          item_->DocumentSequenceNumber();
 }
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 2827fc0c..6ba4153 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -31,6 +31,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "third_party/blink/public/common/page/drag_operation.h"
+#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/renderer/core/clipboard/data_object.h"
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
index f754e4d..05b0651f2 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/editing/markers/highlight_pseudo_marker.h"
 #include "third_party/blink/renderer/core/editing/markers/text_match_marker.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_box.h"
 #include "third_party/blink/renderer/core/layout/layout_text_combine.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index d7ccd2f..6f22f8e9 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -846,6 +846,14 @@
   return false;
 }
 
+bool PaintLayer::HasAncestorWithFilterThatMovesPixels() const {
+  for (const PaintLayer* curr = this; curr; curr = curr->Parent()) {
+    if (curr->HasFilterThatMovesPixels())
+      return true;
+  }
+  return false;
+}
+
 void PaintLayer::AddChild(PaintLayer* child, PaintLayer* before_child) {
 #if DCHECK_IS_ON()
   DCHECK(layer_list_mutation_allowed_);
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 6366000..7c5c3b1 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -324,6 +324,8 @@
   const PaintLayer* EnclosingCompositedScrollingLayerUnderPagination(
       IncludeSelfOrNot) const;
 
+  bool HasAncestorWithFilterThatMovesPixels() const;
+
   void ConvertToLayerCoords(const PaintLayer* ancestor_layer,
                             PhysicalOffset&) const;
   void ConvertToLayerCoords(const PaintLayer* ancestor_layer,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index d25f7f1..2d361cf8 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -541,6 +541,17 @@
   auto* frame_view = box->GetFrameView();
   frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box);
 
+  // The feature |kOptimizeViewportConstrainedPaintInvalidation| is for testing
+  // the performance of removing the paint invalidation of viewport constrained
+  // objects after scrolling.
+  if (IsA<LayoutView>(box) && frame_view->HasViewportConstrainedObjects() &&
+      !base::FeatureList::IsEnabled(
+          features::kOptimizeViewportConstrainedPaintInvalidation) &&
+      !frame_view->InvalidateViewportConstrainedObjects()) {
+    box->SetShouldDoFullPaintInvalidation();
+    box->SetSubtreeShouldCheckForPaintInvalidation();
+  }
+
   // TODO(chrishtr): remove this slow path once crbug.com/906885 is fixed.
   // See also https://bugs.chromium.org/p/chromium/issues/detail?id=903287#c10.
   if (Layer()->EnclosingPaginationLayer())
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.h b/third_party/blink/renderer/core/style/style_fetched_image.h
index 98a59d1..6016fc5 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image.h
@@ -38,8 +38,8 @@
 
 // This class represents an <image> that loads a single image resource (the
 // url(...) function.)
-class CORE_EXPORT StyleFetchedImage final : public StyleImage,
-                                            public ImageResourceObserver {
+class StyleFetchedImage final : public StyleImage,
+                                public ImageResourceObserver {
   USING_PRE_FINALIZER(StyleFetchedImage, Prefinalize);
 
  public:
diff --git a/third_party/blink/renderer/modules/DEPS b/third_party/blink/renderer/modules/DEPS
index 273e579..b7e54fb 100644
--- a/third_party/blink/renderer/modules/DEPS
+++ b/third_party/blink/renderer/modules/DEPS
@@ -55,5 +55,8 @@
 
     "tcp_writable_stream_wrapper.h": [
         "+base/allocator/partition_allocator/partition_root.h",
+    ],
+    ".*_stream_wrapper.cc": [
+        "+net/base/ip_endpoint.h"
     ]
 }
diff --git a/third_party/blink/renderer/modules/direct_sockets/BUILD.gn b/third_party/blink/renderer/modules/direct_sockets/BUILD.gn
index 93ba023..7823326 100644
--- a/third_party/blink/renderer/modules/direct_sockets/BUILD.gn
+++ b/third_party/blink/renderer/modules/direct_sockets/BUILD.gn
@@ -10,6 +10,8 @@
     "direct_sockets_service_mojo_remote.h",
     "socket.cc",
     "socket.h",
+    "stream_wrapper.cc",
+    "stream_wrapper.h",
     "tcp_readable_stream_wrapper.cc",
     "tcp_readable_stream_wrapper.h",
     "tcp_socket.cc",
diff --git a/third_party/blink/renderer/modules/direct_sockets/socket.cc b/third_party/blink/renderer/modules/direct_sockets/socket.cc
index 0d57cecc..e987b90 100644
--- a/third_party/blink/renderer/modules/direct_sockets/socket.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/socket.cc
@@ -14,6 +14,8 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -138,6 +140,15 @@
   return !closed_resolver_;
 }
 
+bool Socket::Initialized() const {
+  return readable_stream_wrapper_ && writable_stream_wrapper_;
+}
+
+bool Socket::HasPendingActivity() const {
+  return writable_stream_wrapper_ &&
+         writable_stream_wrapper_->HasPendingWrite();
+}
+
 void Socket::ResolveOrRejectClosed(bool error) {
   if (error) {
     closed_resolver_->Reject();
@@ -164,6 +175,9 @@
   visitor->Trace(closed_resolver_);
   visitor->Trace(closed_);
 
+  visitor->Trace(readable_stream_wrapper_);
+  visitor->Trace(writable_stream_wrapper_);
+
   ExecutionContextLifecycleStateObserver::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/direct_sockets/socket.h b/third_party/blink/renderer/modules/direct_sockets/socket.h
index 54227815..db22ca71 100644
--- a/third_party/blink/renderer/modules/direct_sockets/socket.h
+++ b/third_party/blink/renderer/modules/direct_sockets/socket.h
@@ -10,7 +10,10 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h"
 #include "third_party/blink/renderer/modules/direct_sockets/direct_sockets_service_mojo_remote.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 
@@ -44,10 +47,12 @@
   // Connects DirectSocketsServiceMojoRemote.
   void ConnectService();
 
-  bool Closed() const;
-
   virtual void Close(const SocketCloseOptions*, ExceptionState&) = 0;
 
+  bool Closed() const;
+  bool Initialized() const;
+  bool HasPendingActivity() const;
+
   // Resolves or rejects |closed| promise.
   void ResolveOrRejectClosed(bool error);
 
@@ -71,6 +76,9 @@
 
   Member<ScriptPromiseResolver> closed_resolver_;
   const TraceWrapperV8Reference<v8::Promise> closed_;
+
+  Member<ReadableStreamWrapper> readable_stream_wrapper_;
+  Member<WritableStreamWrapper> writable_stream_wrapper_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/socket_options.idl b/third_party/blink/renderer/modules/direct_sockets/socket_options.idl
index 202ee39..a8182798 100644
--- a/third_party/blink/renderer/modules/direct_sockets/socket_options.idl
+++ b/third_party/blink/renderer/modules/direct_sockets/socket_options.idl
@@ -19,7 +19,7 @@
 };
 
 dictionary UDPSocketOptions : SocketOptions {
-
+  [EnforceRange] unsigned short readableStreamBufferSize;
 };
 
 dictionary SocketCloseOptions {
diff --git a/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.cc b/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.cc
new file mode 100644
index 0000000..b664398
--- /dev/null
+++ b/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.cc
@@ -0,0 +1,139 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
+#include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
+#include "third_party/blink/renderer/core/streams/underlying_source_base.h"
+#include "third_party/blink/renderer/core/streams/writable_stream.h"
+#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+StreamWrapper::StreamWrapper(ScriptState* script_state)
+    : script_state_(script_state) {}
+
+StreamWrapper::~StreamWrapper() = default;
+
+ScriptValue StreamWrapper::CreateException(ScriptState* script_state,
+                                           DOMExceptionCode code,
+                                           const String& message) {
+  return ScriptValue(script_state->GetIsolate(),
+                     V8ThrowDOMException::CreateOrEmpty(
+                         script_state->GetIsolate(), code, message));
+}
+
+void StreamWrapper::Trace(Visitor* visitor) const {
+  visitor->Trace(script_state_);
+}
+
+ReadableStreamWrapper::ReadableStreamWrapper(ScriptState* script_state)
+    : StreamWrapper(script_state) {}
+
+bool ReadableStreamWrapper::Locked() const {
+  return ReadableStream::IsLocked(readable_);
+}
+
+void ReadableStreamWrapper::Trace(Visitor* visitor) const {
+  visitor->Trace(source_);
+  visitor->Trace(readable_);
+  StreamWrapper::Trace(visitor);
+}
+
+void ReadableStreamWrapper::InitSourceAndReadable(UnderlyingSource* source,
+                                                  size_t high_water_mark) {
+  source_ = source;
+  ScriptState::Scope scope(GetScriptState());
+  readable_ = ReadableStream::CreateWithCountQueueingStrategy(
+      GetScriptState(), source, high_water_mark);
+}
+
+ReadableStreamDefaultControllerWithScriptScope*
+ReadableStreamWrapper::Controller() const {
+  return source_->Controller();
+}
+
+WritableStreamWrapper::WritableStreamWrapper(ScriptState* script_state)
+    : StreamWrapper(script_state) {}
+
+bool WritableStreamWrapper::Locked() const {
+  return WritableStream::IsLocked(writable_);
+}
+
+void WritableStreamWrapper::Trace(Visitor* visitor) const {
+  visitor->Trace(sink_);
+  visitor->Trace(writable_);
+  StreamWrapper::Trace(visitor);
+}
+
+void WritableStreamWrapper::InitSinkAndWritable(UnderlyingSink* sink,
+                                                size_t high_water_mark) {
+  sink_ = sink;
+  ScriptState::Scope scope(GetScriptState());
+  writable_ = WritableStream::CreateWithCountQueueingStrategy(
+      GetScriptState(), sink, high_water_mark);
+}
+
+WritableStreamDefaultController* WritableStreamWrapper::Controller() const {
+  return sink_->Controller();
+}
+
+ReadableStreamWrapper::UnderlyingSource::UnderlyingSource(
+    ScriptState* script_state,
+    ReadableStreamWrapper* readable_stream_wrapper)
+    : UnderlyingSourceBase(script_state),
+      readable_stream_wrapper_(readable_stream_wrapper) {}
+
+ScriptPromise ReadableStreamWrapper::UnderlyingSource::Start(
+    ScriptState* script_state) {
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+ScriptPromise ReadableStreamWrapper::UnderlyingSource::pull(
+    ScriptState* script_state) {
+  readable_stream_wrapper_->Pull();
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+void ReadableStreamWrapper::UnderlyingSource::Trace(Visitor* visitor) const {
+  visitor->Trace(readable_stream_wrapper_);
+  UnderlyingSourceBase::Trace(visitor);
+}
+
+WritableStreamWrapper::UnderlyingSink::UnderlyingSink(
+    WritableStreamWrapper* writable_stream_wrapper)
+    : writable_stream_wrapper_(writable_stream_wrapper) {}
+
+ScriptPromise WritableStreamWrapper::UnderlyingSink::start(
+    ScriptState* script_state,
+    WritableStreamDefaultController*,
+    ExceptionState&) {
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+ScriptPromise WritableStreamWrapper::UnderlyingSink::write(
+    ScriptState* script_state,
+    ScriptValue chunk,
+    WritableStreamDefaultController*,
+    ExceptionState& exception_state) {
+  return writable_stream_wrapper_->Write(chunk, exception_state);
+}
+
+ScriptPromise WritableStreamWrapper::UnderlyingSink::abort(
+    ScriptState* script_state,
+    ScriptValue reason,
+    ExceptionState& exception_state) {
+  return close(script_state, exception_state);
+}
+
+void WritableStreamWrapper::UnderlyingSink::Trace(Visitor* visitor) const {
+  visitor->Trace(writable_stream_wrapper_);
+  UnderlyingSinkBase::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h b/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h
new file mode 100644
index 0000000..4531474
--- /dev/null
+++ b/third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h
@@ -0,0 +1,170 @@
+// 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_RENDERER_MODULES_DIRECT_SOCKETS_STREAM_WRAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_DIRECT_SOCKETS_STREAM_WRAPPER_H_
+
+#include "base/containers/span.h"
+#include "base/notreached.h"
+#include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
+#include "third_party/blink/renderer/core/streams/underlying_source_base.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace net {
+
+class IPEndPoint;
+
+}  // namespace net
+
+namespace blink {
+
+class ExceptionState;
+
+class ReadableStream;
+class WritableStream;
+
+class ScriptValue;
+
+}  // namespace blink
+
+namespace blink {
+
+class MODULES_EXPORT StreamWrapper : public GarbageCollectedMixin {
+ public:
+  enum class State { kOpen, kAborted, kClosed };
+
+  explicit StreamWrapper(ScriptState*);
+  virtual ~StreamWrapper() = 0;
+
+  State GetState() const { return state_; }
+  ScriptState* GetScriptState() const { return script_state_; }
+
+  virtual bool Locked() const = 0;
+
+  virtual void CloseStream(bool error) = 0;
+  virtual void CloseSocket(bool error) { NOTIMPLEMENTED(); }
+
+  void Trace(Visitor* visitor) const override;
+
+ protected:
+  void SetState(State state) { state_ = state; }
+
+  static ScriptValue CreateException(ScriptState*,
+                                     DOMExceptionCode,
+                                     const String& message);
+
+ private:
+  const Member<ScriptState> script_state_;
+  State state_ = State::kOpen;
+};
+
+class ReadableStreamWrapper : public StreamWrapper {
+ public:
+  explicit ReadableStreamWrapper(ScriptState*);
+
+  ReadableStream* Readable() const { return readable_; }
+  bool Locked() const override;
+
+  // Implements UnderlyingSource::pull(...)
+  virtual void Pull() = 0;
+
+  // Enqueues the data in the stream controller queue.
+  virtual bool Push(base::span<const uint8_t> data,
+                    const absl::optional<net::IPEndPoint>& src_addr) = 0;
+
+  void Trace(Visitor*) const override;
+
+ protected:
+  class UnderlyingSource;
+  void InitSourceAndReadable(UnderlyingSource*, size_t high_water_mark);
+
+  ReadableStreamDefaultControllerWithScriptScope* Controller() const;
+
+ private:
+  Member<UnderlyingSource> source_;
+  Member<ReadableStream> readable_;
+};
+
+class WritableStreamWrapper : public StreamWrapper {
+ public:
+  explicit WritableStreamWrapper(ScriptState*);
+
+  WritableStream* Writable() const { return writable_; }
+  bool Locked() const override;
+
+  // Implements UnderlyingSink::write(...)
+  virtual ScriptPromise Write(ScriptValue, ExceptionState&) = 0;
+
+  // Checks whether there's a write in progress.
+  virtual bool HasPendingWrite() const { return false; }
+
+  void Trace(Visitor*) const override;
+
+ protected:
+  class UnderlyingSink;
+  void InitSinkAndWritable(UnderlyingSink*, size_t high_water_mark);
+
+  WritableStreamDefaultController* Controller() const;
+
+ private:
+  Member<UnderlyingSink> sink_;
+  Member<WritableStream> writable_;
+};
+
+class ReadableStreamWrapper::UnderlyingSource : public UnderlyingSourceBase {
+ public:
+  UnderlyingSource(ScriptState*, ReadableStreamWrapper*);
+
+  ScriptPromise Start(ScriptState*) override;
+  ScriptPromise pull(ScriptState*) override;
+  ScriptPromise Cancel(ScriptState*, ScriptValue reason) override = 0;
+
+  void Trace(Visitor*) const override;
+
+ protected:
+  ReadableStreamWrapper* GetReadableStreamWrapper() const {
+    return readable_stream_wrapper_;
+  }
+
+ private:
+  friend class ReadableStreamWrapper;
+  const Member<ReadableStreamWrapper> readable_stream_wrapper_;
+};
+
+class WritableStreamWrapper::UnderlyingSink : public UnderlyingSinkBase {
+ public:
+  explicit UnderlyingSink(WritableStreamWrapper*);
+
+  ScriptPromise start(ScriptState*,
+                      WritableStreamDefaultController*,
+                      ExceptionState&) override;
+  ScriptPromise write(ScriptState*,
+                      ScriptValue chunk,
+                      WritableStreamDefaultController*,
+                      ExceptionState&) override;
+  ScriptPromise close(ScriptState*, ExceptionState&) override = 0;
+  ScriptPromise abort(ScriptState*,
+                      ScriptValue reason,
+                      ExceptionState&) override;
+
+  void Trace(Visitor*) const override;
+
+ protected:
+  WritableStreamWrapper* GetWritableStreamWrapper() const {
+    return writable_stream_wrapper_;
+  }
+
+ private:
+  friend class WritableStreamWrapper;
+  const Member<WritableStreamWrapper> writable_stream_wrapper_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_DIRECT_SOCKETS_STREAM_WRAPPER_H_
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.cc
index c149020..e571b5b4 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h"
 
+#include "base/containers/span.h"
+#include "net/base/ip_endpoint.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
@@ -12,64 +14,41 @@
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 
 namespace blink {
 
 // An implementation of UnderlyingSourceBase that forwards all operations to the
 // TCPReadableStreamWrapper object that created it.
-class TCPReadableStreamWrapper::UnderlyingSource final
-    : public UnderlyingSourceBase {
+class TCPReadableStreamWrapper::TCPUnderlyingSource final
+    : public ReadableStreamWrapper::UnderlyingSource {
  public:
-  UnderlyingSource(ScriptState* script_state, TCPReadableStreamWrapper* stream)
-      : UnderlyingSourceBase(script_state),
-        tcp_readable_stream_wrapper_(stream) {}
-
-  ScriptPromise Start(ScriptState* script_state) override {
-    DVLOG(1) << "TCPReadableStreamWrapper::UnderlyingSource::start() "
-                "tcp_readable_stream_wrapper_="
-             << tcp_readable_stream_wrapper_;
-
-    tcp_readable_stream_wrapper_->controller_ = Controller();
-    return ScriptPromise::CastUndefined(script_state);
-  }
-
-  ScriptPromise pull(ScriptState* script_state) override {
-    DVLOG(1) << "TCPReadableStreamWrapper::UnderlyingSource::pull() "
-                "tcp_readable_stream_wrapper_="
-             << tcp_readable_stream_wrapper_;
-
-    tcp_readable_stream_wrapper_->ReadFromPipeAndEnqueue();
-    return ScriptPromise::CastUndefined(script_state);
-  }
+  TCPUnderlyingSource(ScriptState* script_state,
+                      TCPReadableStreamWrapper* readable_stream_wrapper)
+      : ReadableStreamWrapper::UnderlyingSource(script_state,
+                                                readable_stream_wrapper) {}
 
   ScriptPromise Cancel(ScriptState* script_state, ScriptValue reason) override {
-    DVLOG(1) << "TCPReadableStreamWrapper::UnderlyingSource::Cancel() "
-                "tcp_readable_stream_wrapper_="
-             << tcp_readable_stream_wrapper_;
-
-    tcp_readable_stream_wrapper_->Close();
+    GetReadableStreamWrapper()->CloseSocket(/*error=*/false);
     return ScriptPromise::CastUndefined(script_state);
   }
 
   void Trace(Visitor* visitor) const override {
-    visitor->Trace(tcp_readable_stream_wrapper_);
-    UnderlyingSourceBase::Trace(visitor);
+    ReadableStreamWrapper::UnderlyingSource::Trace(visitor);
   }
-
- private:
-  const Member<TCPReadableStreamWrapper> tcp_readable_stream_wrapper_;
 };
 
 TCPReadableStreamWrapper::TCPReadableStreamWrapper(
     ScriptState* script_state,
-    base::OnceClosure on_abort,
+    base::OnceCallback<void(bool)> on_close,
     mojo::ScopedDataPipeConsumerHandle handle)
-    : script_state_(script_state),
-      on_abort_(std::move(on_abort)),
+    : ReadableStreamWrapper(script_state),
+      on_close_(std::move(on_close)),
       data_pipe_(std::move(handle)),
       read_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       close_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {
@@ -84,36 +63,21 @@
       WTF::BindRepeating(&TCPReadableStreamWrapper::OnPeerClosed,
                          WrapWeakPersistent(this)));
 
-  ScriptState::Scope scope(script_state_);
   // Set queuing strategy of default behavior with a high water mark of 1.
-  readable_ = ReadableStream::CreateWithCountQueueingStrategy(
-      script_state_,
-      MakeGarbageCollected<UnderlyingSource>(script_state_, this), 1);
+  InitSourceAndReadable(
+      /*source=*/MakeGarbageCollected<TCPUnderlyingSource>(script_state, this),
+      /*high_water_mark=*/1);
 }
 
-TCPReadableStreamWrapper::~TCPReadableStreamWrapper() = default;
-
-void TCPReadableStreamWrapper::Close(bool error) {
-  if (GetState() != State::kOpen) {
-    return;
+void TCPReadableStreamWrapper::CloseSocket(bool error) {
+  if (on_close_) {
+    std::move(on_close_).Run(error);
   }
-
-  // We no longer need to call |on_abort_|.
-  on_abort_.Reset();
-
-  if (error) {
-    state_ = State::kAborted;
-  } else {
-    state_ = State::kClosed;
-  }
-
-  CloseOrErrorStreamAbortAndReset(error);
+  DCHECK_NE(GetState(), State::kOpen);
 }
 
 void TCPReadableStreamWrapper::Trace(Visitor* visitor) const {
-  visitor->Trace(script_state_);
-  visitor->Trace(readable_);
-  visitor->Trace(controller_);
+  ReadableStreamWrapper::Trace(visitor);
 }
 
 void TCPReadableStreamWrapper::OnHandleReady(MojoResult result,
@@ -123,7 +87,7 @@
 
   switch (result) {
     case MOJO_RESULT_OK:
-      ReadFromPipeAndEnqueue();
+      Pull();
       break;
 
     case MOJO_RESULT_FAILED_PRECONDITION:
@@ -141,18 +105,16 @@
            << " result=" << result;
 
   DCHECK_EQ(result, MOJO_RESULT_OK);
+  DCHECK_EQ(GetState(), State::kOpen);
 
-  DCHECK_EQ(state_, State::kOpen);
-  state_ = State::kAborted;
-
-  CloseOrErrorStreamAbortAndReset(/*error=*/true);
+  CloseSocket(/*error=*/true);
 }
 
-void TCPReadableStreamWrapper::ReadFromPipeAndEnqueue() {
-  if (!script_state_->ContextIsValid())
+void TCPReadableStreamWrapper::Pull() {
+  if (!GetScriptState()->ContextIsValid())
     return;
 
-  DVLOG(1) << "TCPReadableStreamWrapper::ReadFromPipeAndEnqueue() this=" << this
+  DVLOG(1) << "TCPReadableStreamWrapper::Pull() this=" << this
            << " in_two_phase_read_=" << in_two_phase_read_
            << " read_pending_=" << read_pending_;
 
@@ -170,15 +132,17 @@
   switch (result) {
     case MOJO_RESULT_OK: {
       in_two_phase_read_ = true;
-      // EnqueueBytes() may re-enter this method via pull().
-      EnqueueBytes(buffer, buffer_num_bytes);
+      // Push() may re-enter this method via TCPUnderlyingSource::pull().
+      Push(base::make_span(static_cast<const uint8_t*>(buffer),
+                           buffer_num_bytes),
+           {});
       data_pipe_->EndReadData(buffer_num_bytes);
       in_two_phase_read_ = false;
       if (read_pending_) {
         read_pending_ = false;
         // pull() will not be called when another pull() is in progress, so the
         // maximum recursion depth is 1.
-        ReadFromPipeAndEnqueue();
+        Pull();
       }
       break;
     }
@@ -197,39 +161,29 @@
   }
 }
 
-void TCPReadableStreamWrapper::EnqueueBytes(const void* source,
-                                            uint32_t byte_length) {
-  DVLOG(1) << "TCPReadableStreamWrapper::EnqueueBytes() this=" << this;
+bool TCPReadableStreamWrapper::Push(base::span<const uint8_t> data,
+                                    const absl::optional<net::IPEndPoint>&) {
+  auto* buffer = DOMUint8Array::Create(data.data(), data.size_bytes());
+  Controller()->Enqueue(buffer);
 
-  auto* buffer =
-      DOMUint8Array::Create(static_cast<const uint8_t*>(source), byte_length);
-  controller_->Enqueue(buffer);
+  return true;
 }
 
-// static
-ScriptValue TCPReadableStreamWrapper::CreateException(ScriptState* script_state,
-                                                      DOMExceptionCode code,
-                                                      const String& message) {
-  return ScriptValue(script_state->GetIsolate(),
-                     V8ThrowDOMException::CreateOrEmpty(
-                         script_state->GetIsolate(), code, message));
-}
-
-void TCPReadableStreamWrapper::CloseOrErrorStreamAbortAndReset(bool error) {
-  DVLOG(1) << "TCPReadableStreamWrapper::ErrorStreamAbortAndReset() this="
-           << this;
+void TCPReadableStreamWrapper::CloseStream(bool error) {
+  if (GetState() != State::kOpen) {
+    return;
+  }
+  SetState(error ? State::kAborted : State::kClosed);
 
   if (error) {
-    ScriptState::Scope scope(script_state_);
-    controller_->Error(CreateException(
-        script_state_, DOMExceptionCode::kNetworkError, "Error."));
+    ScriptState::Scope scope(GetScriptState());
+    Controller()->Error(CreateException(
+        GetScriptState(), DOMExceptionCode::kNetworkError, "Error."));
   } else {
-    controller_->Close();
+    Controller()->Close();
   }
 
-  if (on_abort_) {
-    std::move(on_abort_).Run();
-  }
+  on_close_.Reset();
 
   ResetPipe();
 }
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h
index 8ab999e..00c794fe 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/heap/prefinalizer.h"
@@ -18,43 +19,28 @@
 
 namespace blink {
 
-class ScriptState;
-class ReadableStream;
-class ReadableStreamDefaultControllerWithScriptScope;
-
 // Helper class to read from a mojo consumer handle
-class MODULES_EXPORT TCPReadableStreamWrapper final
-    : public GarbageCollected<TCPReadableStreamWrapper> {
+class MODULES_EXPORT TCPReadableStreamWrapper
+    : public GarbageCollected<TCPReadableStreamWrapper>,
+      public ReadableStreamWrapper {
   USING_PRE_FINALIZER(TCPReadableStreamWrapper, Dispose);
 
  public:
-  enum class State {
-    kOpen,
-    kAborted,
-    kClosed,
-  };
-
   TCPReadableStreamWrapper(ScriptState*,
-                           base::OnceClosure on_abort,
+                           base::OnceCallback<void(bool)> on_close,
                            mojo::ScopedDataPipeConsumerHandle);
-  ~TCPReadableStreamWrapper();
 
-  ReadableStream* Readable() const {
-    DVLOG(1) << "TCPReadableStreamWrapper::readable() called";
+  void CloseSocket(bool error) override;
+  void CloseStream(bool error) override;
 
-    return readable_;
-  }
+  void Pull() override;
+  bool Push(base::span<const uint8_t> data,
+            const absl::optional<net::IPEndPoint>&) override;
 
-  ScriptState* GetScriptState() { return script_state_; }
-
-  void Close(bool error = false);
-
-  State GetState() const { return state_; }
-
-  void Trace(Visitor*) const;
+  void Trace(Visitor*) const override;
 
  private:
-  class UnderlyingSource;
+  class TCPUnderlyingSource;
 
   // Called when |data_pipe_| becomes readable or errored.
   void OnHandleReady(MojoResult, const mojo::HandleSignalsState&);
@@ -62,19 +48,6 @@
   // Called when |data_pipe_| is closed.
   void OnPeerClosed(MojoResult, const mojo::HandleSignalsState&);
 
-  // Reads all the data currently in the pipe and enqueues it. If no data is
-  // currently available, triggers the |read_watcher_| and enqueues when data
-  // becomes available.
-  void ReadFromPipeAndEnqueue();
-
-  // Copies a sequence of bytes into an ArrayBuffer and enqueues it.
-  void EnqueueBytes(const void* source, uint32_t byte_length);
-
-  // Creates a DOMException.
-  static ScriptValue CreateException(ScriptState*,
-                                     DOMExceptionCode,
-                                     const String& message);
-
   // Errors or closes |readable_| and resets |data_pipe_|.
   void CloseOrErrorStreamAbortAndReset(bool error);
 
@@ -84,9 +57,7 @@
   // Prepares the object for destruction.
   void Dispose();
 
-  const Member<ScriptState> script_state_;
-
-  base::OnceClosure on_abort_;
+  base::OnceCallback<void(bool)> on_close_;
 
   mojo::ScopedDataPipeConsumerHandle data_pipe_;
 
@@ -96,11 +67,6 @@
   // Always armed to detect close.
   mojo::SimpleWatcher close_watcher_;
 
-  Member<ReadableStream> readable_;
-  Member<ReadableStreamDefaultControllerWithScriptScope> controller_;
-
-  State state_ = State::kOpen;
-
   // Indicates if we are currently performing a two-phase read from the pipe and
   // so can't start another read.
   bool in_two_phase_read_ = false;
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper_unittest.cc
index 68cbef3..e52c816 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper_unittest.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 namespace blink {
@@ -22,14 +23,10 @@
 // The purpose of this class is to ensure that the data pipe is reset before the
 // V8TestingScope is destroyed, so that the TCPReadableStreamWrapper object
 // doesn't try to create a DOMException after the ScriptState has gone away.
-class StreamCreator {
-  STACK_ALLOCATED();
-
+class StreamCreator : public GarbageCollected<StreamCreator> {
  public:
   StreamCreator() = default;
   ~StreamCreator() {
-    ClosePipe();
-
     // Let the TCPReadableStreamWrapper object respond to the closure if it
     // needs to.
     test::RunPendingTasks();
@@ -52,12 +49,11 @@
     }
 
     auto* script_state = scope.GetScriptState();
-    auto* tcp_readable_stream_wrapper =
-        MakeGarbageCollected<TCPReadableStreamWrapper>(
-            script_state,
-            base::BindOnce(&StreamCreator::OnAbort, base::Unretained(this)),
-            std::move(data_pipe_consumer));
-    return tcp_readable_stream_wrapper;
+    stream_wrapper_ = MakeGarbageCollected<TCPReadableStreamWrapper>(
+        script_state,
+        base::BindOnce(&StreamCreator::Close, base::Unretained(this)),
+        std::move(data_pipe_consumer));
+    return stream_wrapper_;
   }
 
   void WriteToPipe(Vector<uint8_t> data) {
@@ -125,42 +121,47 @@
     return ret;
   }
 
-  void OnAbort() { on_abort_called_ = true; }
-  bool HasAborted() const { return on_abort_called_; }
+  void Close(bool error) { stream_wrapper_->CloseStream(error); }
+
+  void Trace(Visitor* visitor) const { visitor->Trace(stream_wrapper_); }
 
  private:
-  bool on_abort_called_ = false;
   mojo::ScopedDataPipeProducerHandle data_pipe_producer_;
+  Member<TCPReadableStreamWrapper> stream_wrapper_;
 };
 
 TEST(TCPReadableStreamWrapperTest, Create) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  auto* tcp_readable_stream_wrapper = stream_creator.Create(scope);
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_readable_stream_wrapper = stream_creator->Create(scope);
+
   EXPECT_TRUE(tcp_readable_stream_wrapper->Readable());
 }
 
 TEST(TCPReadableStreamWrapperTest, ReadArrayBuffer) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
 
-  auto* tcp_readable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_readable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
   auto* reader =
       tcp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
-  stream_creator.WriteToPipe({'A'});
+  stream_creator->WriteToPipe({'A'});
 
-  StreamCreator::Iterator result = stream_creator.Read(scope, reader);
+  StreamCreator::Iterator result = stream_creator->Read(scope, reader);
   EXPECT_FALSE(result.done);
   EXPECT_THAT(result.value, ElementsAre('A'));
 }
 
 TEST(TCPReadableStreamWrapperTest, WriteToPipeWithPendingRead) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
 
-  auto* tcp_readable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_readable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
   auto* reader =
       tcp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
@@ -170,23 +171,23 @@
 
   test::RunPendingTasks();
 
-  stream_creator.WriteToPipe({'A'});
+  stream_creator->WriteToPipe({'A'});
 
   tester.WaitUntilSettled();
   ASSERT_TRUE(tester.IsFulfilled());
 
   StreamCreator::Iterator result =
-      stream_creator.IteratorFromReadResult(scope, tester.Value().V8Value());
+      stream_creator->IteratorFromReadResult(scope, tester.Value().V8Value());
   EXPECT_FALSE(result.done);
   EXPECT_THAT(result.value, ElementsAre('A'));
 }
 
 TEST(TCPReadableStreamWrapperTest, TriggerOnAborted) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  EXPECT_FALSE(stream_creator.HasAborted());
 
-  auto* tcp_readable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_readable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
   auto* reader =
       tcp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
@@ -195,13 +196,14 @@
   ScriptPromiseTester tester(script_state, read_promise);
 
   test::RunPendingTasks();
-  stream_creator.WriteToPipe({'A'});
+  stream_creator->WriteToPipe({'A'});
   // Trigger OnAborted() on purpose.
-  stream_creator.ClosePipe();
+  stream_creator->ClosePipe();
   tester.WaitUntilSettled();
 
   ASSERT_TRUE(tester.IsFulfilled());
-  EXPECT_TRUE(stream_creator.HasAborted());
+  ASSERT_EQ(tcp_readable_stream_wrapper->GetState(),
+            StreamWrapper::State::kAborted);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_socket.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_socket.cc
index d21230dd..fcf0a8c 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_socket.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_socket.cc
@@ -159,23 +159,19 @@
                      mojo::ScopedDataPipeConsumerHandle receive_stream,
                      mojo::ScopedDataPipeProducerHandle send_stream) {
   if (result == net::OK && peer_addr) {
-    tcp_readable_stream_wrapper_ =
-        MakeGarbageCollected<TCPReadableStreamWrapper>(
-            script_state_,
-            WTF::Bind(&TCPSocket::OnReadableStreamAbort,
-                      WrapWeakPersistent(this)),
-            std::move(receive_stream));
-    tcp_writable_stream_wrapper_ =
-        MakeGarbageCollected<TCPWritableStreamWrapper>(
-            script_state_,
-            WTF::Bind(&TCPSocket::OnWritableStreamAbort,
-                      WrapWeakPersistent(this)),
-            std::move(send_stream));
+    readable_stream_wrapper_ = MakeGarbageCollected<TCPReadableStreamWrapper>(
+        script_state_,
+        WTF::Bind(&TCPSocket::CloseInternal, WrapWeakPersistent(this)),
+        std::move(receive_stream));
+    writable_stream_wrapper_ = MakeGarbageCollected<TCPWritableStreamWrapper>(
+        script_state_,
+        WTF::Bind(&TCPSocket::CloseInternal, WrapWeakPersistent(this)),
+        std::move(send_stream));
 
     auto* connection = TCPSocketConnection::Create();
 
-    connection->setReadable(tcp_readable_stream_wrapper_->Readable());
-    connection->setWritable(tcp_writable_stream_wrapper_->Writable());
+    connection->setReadable(readable_stream_wrapper_->Readable());
+    connection->setWritable(writable_stream_wrapper_->Writable());
 
     connection->setRemoteAddress(String{peer_addr->ToStringWithoutPort()});
     connection->setRemotePort(peer_addr->port());
@@ -213,10 +209,6 @@
   return pending_remote;
 }
 
-bool TCPSocket::Initialized() const {
-  return tcp_readable_stream_wrapper_ && tcp_writable_stream_wrapper_;
-}
-
 void TCPSocket::OnSocketConnectionError() {
   if (Initialized()) {
     CloseInternal(/*error=*/true);
@@ -236,7 +228,7 @@
     return;
   }
 
-  tcp_readable_stream_wrapper_->Close(/*error=*/true);
+  readable_stream_wrapper_->CloseStream(/*error=*/true);
 }
 
 void TCPSocket::OnWriteError(int32_t net_error) {
@@ -244,13 +236,10 @@
     return;
   }
 
-  tcp_writable_stream_wrapper_->Close(/*error=*/true);
+  writable_stream_wrapper_->CloseStream(/*error=*/true);
 }
 
 void TCPSocket::Trace(Visitor* visitor) const {
-  visitor->Trace(tcp_readable_stream_wrapper_);
-  visitor->Trace(tcp_writable_stream_wrapper_);
-
   visitor->Trace(tcp_socket_);
   visitor->Trace(socket_observer_);
 
@@ -260,15 +249,7 @@
 }
 
 bool TCPSocket::HasPendingActivity() const {
-  return Initialized() && tcp_writable_stream_wrapper_->IsActive();
-}
-
-void TCPSocket::OnReadableStreamAbort() {
-  tcp_writable_stream_wrapper_->Close(/*error=*/true);
-}
-
-void TCPSocket::OnWritableStreamAbort() {
-  tcp_readable_stream_wrapper_->Close(/*error=*/true);
+  return Socket::HasPendingActivity();
 }
 
 void TCPSocket::Close(const SocketCloseOptions* options,
@@ -286,18 +267,16 @@
   }
 
   if (!options->hasForce() || !options->force()) {
-    if (ReadableStream::IsLocked(tcp_readable_stream_wrapper_->Readable())) {
+    if (readable_stream_wrapper_->Locked() ||
+        writable_stream_wrapper_->Locked()) {
       exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        "Close called on locked Readable.");
-      return;
-    }
-    if (WritableStream::IsLocked(tcp_writable_stream_wrapper_->Writable())) {
-      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        "Close called on locked Writable.");
+                                        "Close called on locked streams.");
       return;
     }
   }
+
   CloseInternal(/*error=*/false);
+  DCHECK(Closed());
 }
 
 void TCPSocket::CloseInternal(bool error) {
@@ -307,8 +286,8 @@
   CloseServiceAndResetFeatureHandle();
   ResolveOrRejectClosed(error);
 
-  tcp_readable_stream_wrapper_->Close(error);
-  tcp_writable_stream_wrapper_->Close(error);
+  readable_stream_wrapper_->CloseStream(error);
+  writable_stream_wrapper_->CloseStream(error);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_socket.h b/third_party/blink/renderer/modules/direct_sockets/tcp_socket.h
index 42cbc0e..2110a0d4 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_socket.h
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_socket.h
@@ -104,8 +104,6 @@
   mojo::PendingRemote<network::mojom::blink::SocketObserver>
   GetTCPSocketObserver();
 
-  bool Initialized() const;
-
   void OnServiceConnectionError() override;
   void OnSocketConnectionError();
 
@@ -113,9 +111,6 @@
   void OnReadError(int32_t net_error) override;
   void OnWriteError(int32_t net_error) override;
 
-  void OnReadableStreamAbort();
-  void OnWritableStreamAbort();
-
   void Close(const SocketCloseOptions*, ExceptionState&) override;
   void CloseInternal(bool error);
 
@@ -123,9 +118,6 @@
   HeapMojoReceiver<network::mojom::blink::SocketObserver, TCPSocket>
       socket_observer_;
 
-  Member<TCPReadableStreamWrapper> tcp_readable_stream_wrapper_;
-  Member<TCPWritableStreamWrapper> tcp_writable_stream_wrapper_;
-
   FRIEND_TEST_ALL_PREFIXES(TCPSocketTest, OnSocketObserverConnectionError);
   FRIEND_TEST_ALL_PREFIXES(TCPSocketTest, OnReadError);
 };
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_socket_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_socket_unittest.cc
index 3daafd6..1721384f 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_socket_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_socket_unittest.cc
@@ -147,8 +147,8 @@
 
   tcp_socket->OnReadError(net::ERR_UNEXPECTED);
 
-  ASSERT_EQ(tcp_socket->tcp_readable_stream_wrapper_->GetState(),
-            TCPReadableStreamWrapper::State::kAborted);
+  ASSERT_EQ(tcp_socket->readable_stream_wrapper_->GetState(),
+            StreamWrapper::State::kAborted);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.cc
index 25ad4a6..eb080b7 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.cc
@@ -8,14 +8,18 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
+#include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
+#include "third_party/blink/renderer/modules/direct_sockets/tcp_readable_stream_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -24,65 +28,20 @@
 
 // An implementation of UnderlyingSinkBase that forwards all operations to the
 // TCPWritableStreamWrapper object that created it.
-class TCPWritableStreamWrapper::UnderlyingSink final
-    : public UnderlyingSinkBase {
+class TCPWritableStreamWrapper::TCPUnderlyingSink final
+    : public WritableStreamWrapper::UnderlyingSink {
  public:
-  explicit UnderlyingSink(TCPWritableStreamWrapper* tcp_writable_stream_wrapper)
-      : tcp_writable_stream_wrapper_(tcp_writable_stream_wrapper) {}
-
-  // Implementation of UnderlyingSinkBase
-  ScriptPromise start(ScriptState* script_state,
-                      WritableStreamDefaultController* controller,
-                      ExceptionState&) override {
-    DVLOG(1) << "TCPWritableStreamWrapper::UnderlyinkSink::start() "
-                "tcp_writable_stream_wrapper_="
-             << tcp_writable_stream_wrapper_;
-
-    tcp_writable_stream_wrapper_->controller_ = controller;
-    return ScriptPromise::CastUndefined(script_state);
-  }
-
-  ScriptPromise write(ScriptState* script_state,
-                      ScriptValue chunk,
-                      WritableStreamDefaultController*,
-                      ExceptionState& exception_state) override {
-    DVLOG(1) << "TCPWritableStreamWrapper::UnderlyingSink::write() "
-                "tcp_writable_stream_wrapper_="
-             << tcp_writable_stream_wrapper_;
-
-    return tcp_writable_stream_wrapper_->SinkWrite(script_state, chunk,
-                                                   exception_state);
-  }
+  explicit TCPUnderlyingSink(TCPWritableStreamWrapper* writable_stream_wrapper)
+      : WritableStreamWrapper::UnderlyingSink(writable_stream_wrapper) {}
 
   ScriptPromise close(ScriptState* script_state, ExceptionState&) override {
-    DVLOG(1) << "TCPWritableStreamWrapper::UnderlingSink::close() "
-                "tcp_writable_stream_wrapper_="
-             << tcp_writable_stream_wrapper_;
-
-    DCHECK(!tcp_writable_stream_wrapper_->write_promise_resolver_);
-
-    tcp_writable_stream_wrapper_->Close();
-
+    GetWritableStreamWrapper()->CloseSocket(/*error=*/false);
     return ScriptPromise::CastUndefined(script_state);
   }
 
-  ScriptPromise abort(ScriptState* script_state,
-                      ScriptValue reason,
-                      ExceptionState& exception_state) override {
-    DVLOG(1) << "TCPWritableStreamWrapper::UnderlyingSink::abort() "
-                "tcp_writable_stream_wrapper_="
-             << tcp_writable_stream_wrapper_;
-
-    return close(script_state, exception_state);
-  }
-
   void Trace(Visitor* visitor) const override {
-    visitor->Trace(tcp_writable_stream_wrapper_);
-    UnderlyingSinkBase::Trace(visitor);
+    WritableStreamWrapper::UnderlyingSink::Trace(visitor);
   }
-
- private:
-  const Member<TCPWritableStreamWrapper> tcp_writable_stream_wrapper_;
 };
 
 TCPWritableStreamWrapper::CachedDataBuffer::CachedDataBuffer(
@@ -109,11 +68,10 @@
 
 TCPWritableStreamWrapper::TCPWritableStreamWrapper(
     ScriptState* script_state,
-    base::OnceClosure on_abort,
+    base::OnceCallback<void(bool)> on_close,
     mojo::ScopedDataPipeProducerHandle handle)
-    : ExecutionContextClient(ExecutionContext::From(script_state)),
-      script_state_(script_state),
-      on_abort_(std::move(on_abort)),
+    : WritableStreamWrapper(script_state),
+      on_close_(std::move(on_close)),
       data_pipe_(std::move(handle)),
       write_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       close_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {
@@ -128,42 +86,27 @@
       WTF::BindRepeating(&TCPWritableStreamWrapper::OnPeerClosed,
                          WrapWeakPersistent(this)));
 
-  ScriptState::Scope scope(script_state_);
   // Set the CountQueueingStrategy's high water mark as 1 to make the logic of
   // |WriteOrCacheData| much simpler
-  writable_ = WritableStream::CreateWithCountQueueingStrategy(
-      script_state_, MakeGarbageCollected<UnderlyingSink>(this), 1);
+  InitSinkAndWritable(/*sink=*/MakeGarbageCollected<TCPUnderlyingSink>(this),
+                      /*high_water_mark=*/1);
 }
 
-TCPWritableStreamWrapper::~TCPWritableStreamWrapper() = default;
-
-void TCPWritableStreamWrapper::Close(bool error) {
-  if (GetState() != State::kOpen) {
-    return;
+void TCPWritableStreamWrapper::CloseSocket(bool error) {
+  if (on_close_) {
+    DCHECK_EQ(GetState(), State::kOpen);
+    std::move(on_close_).Run(error);
   }
-
-  // We no longer need to call |on_abort_|.
-  on_abort_.Reset();
-
-  if (error) {
-    state_ = State::kAborted;
-  } else {
-    state_ = State::kClosed;
-  }
-
-  ErrorStreamAbortAndReset(error);
+  DCHECK_NE(GetState(), State::kOpen);
 }
 
-bool TCPWritableStreamWrapper::IsActive() const {
+bool TCPWritableStreamWrapper::HasPendingWrite() const {
   return !!write_promise_resolver_;
 }
 
 void TCPWritableStreamWrapper::Trace(Visitor* visitor) const {
-  visitor->Trace(script_state_);
-  visitor->Trace(writable_);
-  visitor->Trace(controller_);
   visitor->Trace(write_promise_resolver_);
-  ExecutionContextClient::Trace(visitor);
+  WritableStreamWrapper::Trace(visitor);
 }
 
 void TCPWritableStreamWrapper::OnHandleReady(MojoResult result,
@@ -191,18 +134,14 @@
            << " result=" << result;
 
   DCHECK_EQ(result, MOJO_RESULT_OK);
+  DCHECK_EQ(GetState(), State::kOpen);
 
-  DCHECK_EQ(state_, State::kOpen);
-  state_ = State::kAborted;
-
-  ErrorStreamAbortAndReset(/*error=*/true);
+  CloseSocket(/*error=*/true);
 }
 
-ScriptPromise TCPWritableStreamWrapper::SinkWrite(
-    ScriptState* script_state,
-    ScriptValue chunk,
-    ExceptionState& exception_state) {
-  DVLOG(1) << "TCPWritableStreamWrapper::SinkWrite() this=" << this;
+ScriptPromise TCPWritableStreamWrapper::Write(ScriptValue chunk,
+                                              ExceptionState& exception_state) {
+  DVLOG(1) << "TCPWritableStreamWrapper::Write() this=" << this;
 
   // There can only be one call to write() in progress at a time.
   DCHECK(!write_promise_resolver_);
@@ -216,44 +155,44 @@
   }
 
   auto* buffer_source = V8BufferSource::Create(
-      script_state_->GetIsolate(), chunk.V8Value(), exception_state);
+      GetScriptState()->GetIsolate(), chunk.V8Value(), exception_state);
   if (exception_state.HadException())
     return ScriptPromise();
   DCHECK(buffer_source);
 
   DOMArrayPiece array_piece(buffer_source);
-  return WriteOrCacheData(script_state,
-                          {array_piece.Bytes(), array_piece.ByteLength()});
+  return WriteOrCacheData({array_piece.Bytes(), array_piece.ByteLength()});
 }
 
 // Attempt to write |data|. Cache anything that could not be written
 // synchronously. Arrange for the cached data to be written asynchronously.
 ScriptPromise TCPWritableStreamWrapper::WriteOrCacheData(
-    ScriptState* script_state,
     base::span<const uint8_t> data) {
   DVLOG(1) << "TCPWritableStreamWrapper::WriteOrCacheData() this=" << this
            << " data=(" << data.data() << ", " << data.size() << ")";
   size_t written = WriteDataSynchronously(data);
 
   if (written == data.size())
-    return ScriptPromise::CastUndefined(script_state);
+    return ScriptPromise::CastUndefined(GetScriptState());
 
   DCHECK_LT(written, data.size());
 
   if (!data_pipe_) {
+    ScriptState::Scope scope(GetScriptState());
     return ScriptPromise::Reject(
-        script_state,
-        CreateException(script_state_, DOMExceptionCode::kInvalidStateError,
+        GetScriptState(),
+        CreateException(GetScriptState(), DOMExceptionCode::kInvalidStateError,
                         "Pipe is disconnected."));
   }
 
   DCHECK(!cached_data_);
   cached_data_ = std::make_unique<CachedDataBuffer>(
-      script_state->GetIsolate(), data.data() + written, data.size() - written);
+      GetScriptState()->GetIsolate(), data.data() + written,
+      data.size() - written);
   DCHECK_EQ(offset_, 0u);
   write_watcher_.ArmOrNotify();
   write_promise_resolver_ =
-      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+      MakeGarbageCollected<ScriptPromiseResolver>(GetScriptState());
   return write_promise_resolver_->Promise();
 }
 
@@ -318,37 +257,30 @@
   }
 }
 
-ScriptValue TCPWritableStreamWrapper::CreateException(ScriptState* script_state,
-                                                      DOMExceptionCode code,
-                                                      const String& message) {
-  return ScriptValue(script_state->GetIsolate(),
-                     V8ThrowDOMException::CreateOrEmpty(
-                         script_state->GetIsolate(), code, message));
-}
-
-void TCPWritableStreamWrapper::ErrorStreamAbortAndReset(bool error) {
-  DVLOG(1) << "TCPWritableStreamWrapper::ErrorStreamAbortAndReset() this="
-           << this;
+void TCPWritableStreamWrapper::CloseStream(bool error) {
+  if (GetState() != State::kOpen) {
+    return;
+  }
+  SetState(error ? State::kAborted : State::kClosed);
 
   {
-    ScriptState::Scope scope(script_state_);
+    ScriptState::Scope scope(GetScriptState());
     ScriptValue exception =
-        error ? CreateException(script_state_, DOMExceptionCode::kNetworkError,
-                                "Connection aborted by remote")
-              : CreateException(script_state_,
-                                DOMExceptionCode::kInvalidStateError,
-                                "Stream closed.");
+        error
+            ? CreateException(GetScriptState(), DOMExceptionCode::kNetworkError,
+                              "Connection aborted by remote")
+            : CreateException(GetScriptState(),
+                              DOMExceptionCode::kInvalidStateError,
+                              "Stream closed.");
     if (write_promise_resolver_) {
       write_promise_resolver_->Reject(exception);
       write_promise_resolver_ = nullptr;
     }
 
-    controller_->error(script_state_, exception);
+    Controller()->error(GetScriptState(), exception);
   }
 
-  if (on_abort_) {
-    std::move(on_abort_).Run();
-  }
+  on_close_.Reset();
 
   ResetPipe();
 }
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.h b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.h
index ee3fedc66..049a1db 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.h
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/heap/prefinalizer.h"
@@ -22,44 +23,26 @@
 
 namespace blink {
 
-class ScriptState;
-class WritableStream;
-class WritableStreamDefaultController;
-
 // Helper class to write to a mojo producer handle
-class MODULES_EXPORT TCPWritableStreamWrapper final
+class MODULES_EXPORT TCPWritableStreamWrapper
     : public GarbageCollected<TCPWritableStreamWrapper>,
-      public ExecutionContextClient {
+      public WritableStreamWrapper {
   USING_PRE_FINALIZER(TCPWritableStreamWrapper, Dispose);
 
  public:
-  enum class State {
-    kOpen,
-    kAborted,
-    kClosed,
-  };
-
   TCPWritableStreamWrapper(ScriptState*,
-                           base::OnceClosure on_abort,
+                           base::OnceCallback<void(bool)> on_close,
                            mojo::ScopedDataPipeProducerHandle);
-  ~TCPWritableStreamWrapper();
 
-  WritableStream* Writable() const {
-    DVLOG(1) << "TCPWritableStreamWrapper::writable() called";
+  void CloseStream(bool error) override;
+  void CloseSocket(bool error) override;
 
-    return writable_;
-  }
-
-  void Close(bool error = false);
-
-  State GetState() const { return state_; }
-
-  bool IsActive() const;
+  bool HasPendingWrite() const override;
 
   void Trace(Visitor*) const override;
 
  private:
-  class UnderlyingSink;
+  class TCPUnderlyingSink;
 
   // Called when |data_pipe_| becomes writable or errored.
   void OnHandleReady(MojoResult, const mojo::HandleSignalsState&);
@@ -68,11 +51,11 @@
   void OnPeerClosed(MojoResult, const mojo::HandleSignalsState&);
 
   // Implements UnderlyingSink::write().
-  ScriptPromise SinkWrite(ScriptState*, ScriptValue chunk, ExceptionState&);
+  ScriptPromise Write(ScriptValue chunk, ExceptionState&) override;
 
   // Writes |data| to |data_pipe_|, possible saving unwritten data to
   // |cached_data_|.
-  ScriptPromise WriteOrCacheData(ScriptState*, base::span<const uint8_t> data);
+  ScriptPromise WriteOrCacheData(base::span<const uint8_t> data);
 
   // Attempts to write some more of |cached_data_| to |data_pipe_|.
   void WriteCachedData();
@@ -81,11 +64,6 @@
   // returning the number of bytes that were written.
   size_t WriteDataSynchronously(base::span<const uint8_t> data);
 
-  // Creates a DOMException.
-  static ScriptValue CreateException(ScriptState*,
-                                     DOMExceptionCode,
-                                     const String& message);
-
   // Errors |writable_|, resolves |writing_aborted_| and resets |data_pipe_|.
   void ErrorStreamAbortAndReset(bool error);
 
@@ -119,9 +97,7 @@
     std::unique_ptr<uint8_t[], OnFree> buffer_;
   };
 
-  const Member<ScriptState> script_state_;
-
-  base::OnceClosure on_abort_;
+  base::OnceCallback<void(bool)> on_close_;
 
   mojo::ScopedDataPipeProducerHandle data_pipe_;
 
@@ -141,14 +117,9 @@
   // written.
   size_t offset_ = 0;
 
-  Member<WritableStream> writable_;
-  Member<WritableStreamDefaultController> controller_;
-
   // If an asynchronous write() on the underlying sink object is pending, this
   // will be non-null.
   Member<ScriptPromiseResolver> write_promise_resolver_;
-
-  State state_ = State::kOpen;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
index 22d9d126..bab27f1 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 namespace blink {
@@ -22,14 +23,10 @@
 // The purpose of this class is to ensure that the data pipe is reset before the
 // V8TestingScope is destroyed, so that the TCPWritableStreamWrapper object
 // doesn't try to create a DOMException after the ScriptState has gone away.
-class StreamCreator {
-  STACK_ALLOCATED();
-
+class StreamCreator : public GarbageCollected<StreamCreator> {
  public:
   StreamCreator() = default;
   ~StreamCreator() {
-    ClosePipe();
-
     // Let the TCPWritableStreamWrapper object respond to the closure if it
     // needs to.
     test::RunPendingTasks();
@@ -52,12 +49,10 @@
     }
 
     auto* script_state = scope.GetScriptState();
-    auto* tcp_writable_stream_wrapper =
-        MakeGarbageCollected<TCPWritableStreamWrapper>(
-            script_state,
-            base::BindOnce(&StreamCreator::OnAbort, base::Unretained(this)),
-            std::move(data_pipe_producer));
-    return tcp_writable_stream_wrapper;
+    stream_wrapper_ = MakeGarbageCollected<TCPWritableStreamWrapper>(
+        script_state, WTF::Bind(&StreamCreator::Close, WrapPersistent(this)),
+        std::move(data_pipe_producer));
+    return stream_wrapper_;
   }
 
   void ClosePipe() { data_pipe_consumer_.reset(); }
@@ -87,25 +82,27 @@
     return data;
   }
 
-  void OnAbort() { on_abort_called_ = true; }
-  bool HasAborted() const { return on_abort_called_; }
+  void Close(bool error) { stream_wrapper_->CloseStream(error); }
+
+  void Trace(Visitor* visitor) const { visitor->Trace(stream_wrapper_); }
 
  private:
-  bool on_abort_called_ = false;
   mojo::ScopedDataPipeConsumerHandle data_pipe_consumer_;
+  Member<TCPWritableStreamWrapper> stream_wrapper_;
 };
 
 TEST(TCPWritableStreamWrapperTest, Create) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  auto* tcp_writable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_writable_stream_wrapper = stream_creator->Create(scope);
   EXPECT_TRUE(tcp_writable_stream_wrapper->Writable());
 }
 
 TEST(TCPWritableStreamWrapperTest, WriteArrayBuffer) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  auto* tcp_writable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
   auto* writer = tcp_writable_stream_wrapper->Writable()->getWriter(
       script_state, ASSERT_NO_EXCEPTION);
@@ -113,16 +110,17 @@
   ScriptPromise result =
       writer->write(script_state, ScriptValue::From(script_state, chunk),
                     ASSERT_NO_EXCEPTION);
-  ScriptPromiseTester tester(scope.GetScriptState(), result);
+  ScriptPromiseTester tester(script_state, result);
   tester.WaitUntilSettled();
   ASSERT_TRUE(tester.IsFulfilled());
-  EXPECT_THAT(stream_creator.ReadAllPendingData(), ElementsAre('A'));
+  EXPECT_THAT(stream_creator->ReadAllPendingData(), ElementsAre('A'));
 }
 
 TEST(TCPWritableStreamWrapperTest, WriteArrayBufferView) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  auto* tcp_writable_stream_wrapper = stream_creator.Create(scope);
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
   auto* writer = tcp_writable_stream_wrapper->Writable()->getWriter(
       script_state, ASSERT_NO_EXCEPTION);
@@ -132,10 +130,10 @@
   ScriptPromise result =
       writer->write(script_state, ScriptValue::From(script_state, chunk),
                     ASSERT_NO_EXCEPTION);
-  ScriptPromiseTester tester(scope.GetScriptState(), result);
+  ScriptPromiseTester tester(script_state, result);
   tester.WaitUntilSettled();
   ASSERT_TRUE(tester.IsFulfilled());
-  EXPECT_THAT(stream_creator.ReadAllPendingData(), ElementsAre('B'));
+  EXPECT_THAT(stream_creator->ReadAllPendingData(), ElementsAre('B'));
 }
 
 bool IsAllNulls(base::span<const uint8_t> data) {
@@ -144,12 +142,12 @@
 
 TEST(TCPWritableStreamWrapperTest, AsyncWrite) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
   // Set a large pipe capacity, so any platform-specific excess is dwarfed in
   // size.
   constexpr uint32_t kPipeCapacity = 512u * 1024u;
   auto* tcp_writable_stream_wrapper =
-      stream_creator.Create(scope, kPipeCapacity);
+      stream_creator->Create(scope, kPipeCapacity);
 
   auto* script_state = scope.GetScriptState();
   auto* writer = tcp_writable_stream_wrapper->Writable()->getWriter(
@@ -161,7 +159,7 @@
   ScriptPromise result =
       writer->write(script_state, ScriptValue::From(script_state, chunk),
                     ASSERT_NO_EXCEPTION);
-  ScriptPromiseTester tester(scope.GetScriptState(), result);
+  ScriptPromiseTester tester(script_state, result);
 
   // Let the first pipe write complete.
   test::RunPendingTasks();
@@ -171,7 +169,7 @@
   ASSERT_FALSE(tester.IsFulfilled());
 
   // Read the first part of the data.
-  auto data1 = stream_creator.ReadAllPendingData();
+  auto data1 = stream_creator->ReadAllPendingData();
   EXPECT_LT(data1.size(), kChunkSize);
 
   // Verify the data wasn't corrupted.
@@ -181,13 +179,13 @@
   test::RunPendingTasks();
 
   // Read the second part of the data.
-  auto data2 = stream_creator.ReadAllPendingData();
+  auto data2 = stream_creator->ReadAllPendingData();
   EXPECT_TRUE(IsAllNulls(data2));
 
   test::RunPendingTasks();
 
   // Read the final part of the data.
-  auto data3 = stream_creator.ReadAllPendingData();
+  auto data3 = stream_creator->ReadAllPendingData();
   EXPECT_TRUE(IsAllNulls(data3));
   EXPECT_EQ(data1.size() + data2.size() + data3.size(), kChunkSize);
 
@@ -196,15 +194,15 @@
   ASSERT_TRUE(tester.IsFulfilled());
 
   // Nothing should be left to read.
-  EXPECT_THAT(stream_creator.ReadAllPendingData(), ElementsAre());
+  EXPECT_THAT(stream_creator->ReadAllPendingData(), ElementsAre());
 }
 
 // Writing immediately followed by closing should not lose data.
 TEST(TCPWritableStreamWrapperTest, WriteThenClose) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_writable_stream_wrapper = stream_creator->Create(scope);
 
-  auto* tcp_writable_stream_wrapper = stream_creator.Create(scope);
   auto* script_state = scope.GetScriptState();
   auto* writer = tcp_writable_stream_wrapper->Writable()->getWriter(
       script_state, ASSERT_NO_EXCEPTION);
@@ -215,8 +213,8 @@
 
   ScriptPromise close_promise =
       writer->close(script_state, ASSERT_NO_EXCEPTION);
-  ScriptPromiseTester write_tester(scope.GetScriptState(), write_promise);
-  ScriptPromiseTester close_tester(scope.GetScriptState(), close_promise);
+  ScriptPromiseTester write_tester(script_state, write_promise);
+  ScriptPromiseTester close_tester(script_state, close_promise);
 
   // Make sure that write() and close() both run before the event loop is
   // serviced.
@@ -227,15 +225,14 @@
   close_tester.WaitUntilSettled();
   ASSERT_TRUE(close_tester.IsFulfilled());
 
-  EXPECT_THAT(stream_creator.ReadAllPendingData(), ElementsAre('D'));
+  EXPECT_THAT(stream_creator->ReadAllPendingData(), ElementsAre('D'));
 }
 
 TEST(TCPWritableStreamWrapperTest, TriggerHasAborted) {
   V8TestingScope scope;
-  StreamCreator stream_creator;
-  EXPECT_FALSE(stream_creator.HasAborted());
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* tcp_writable_stream_wrapper = stream_creator->Create(scope);
 
-  auto* tcp_writable_stream_wrapper = stream_creator.Create(scope);
   auto* script_state = scope.GetScriptState();
   auto* writer = tcp_writable_stream_wrapper->Writable()->getWriter(
       script_state, ASSERT_NO_EXCEPTION);
@@ -243,12 +240,13 @@
   ScriptPromise write_promise =
       writer->write(script_state, ScriptValue::From(script_state, chunk),
                     ASSERT_NO_EXCEPTION);
-  ScriptPromiseTester write_tester(scope.GetScriptState(), write_promise);
+  ScriptPromiseTester write_tester(script_state, write_promise);
   // Trigger onAborted() on purpose.
-  stream_creator.ClosePipe();
+  stream_creator->ClosePipe();
   write_tester.WaitUntilSettled();
 
-  EXPECT_TRUE(stream_creator.HasAborted());
+  EXPECT_EQ(tcp_writable_stream_wrapper->GetState(),
+            StreamWrapper::State::kAborted);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.cc b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.cc
index a838d1f..dec770fa 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.cc
@@ -11,135 +11,75 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_udp_message.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h"
 #include "third_party/blink/renderer/core/streams/underlying_source_base.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
+#include "third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_deque.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
 namespace blink {
 
-// UDPReadableStreamWrapper::UnderlyingSource declaration
-
-class UDPReadableStreamWrapper::UnderlyingSource final
-    : public UnderlyingSourceBase {
+class UDPReadableStreamWrapper::UDPUnderlyingSource
+    : public ReadableStreamWrapper::UnderlyingSource {
  public:
-  UnderlyingSource(ScriptState* script_state, UDPReadableStreamWrapper* stream)
-      : UnderlyingSourceBase(script_state),
-        udp_readable_stream_wrapper_(stream) {}
+  UDPUnderlyingSource(ScriptState* script_state,
+                      UDPReadableStreamWrapper* readable_stream_wrapper)
+      : ReadableStreamWrapper::UnderlyingSource(script_state,
+                                                readable_stream_wrapper) {}
 
-  // UnderlyingSourceBase overrides.
-  ScriptPromise Start(ScriptState* script_state) override;
-  ScriptPromise pull(ScriptState* script_state) override;
-  // Clears the queue and forwards the request to
-  // UDPReadableStreamWrapper::CloseInternal().
-  // Called from the reader side.
-  ScriptPromise Cancel(ScriptState* script_state, ScriptValue reason) override;
-
-  // Clears the queue and forwards the request to
-  // UDPReadableStreamWrapper::CloseInternal().
-  // Called from the socket side.
-  void Close(bool error);
-
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(udp_readable_stream_wrapper_);
-    visitor->Trace(queue_);
-    UnderlyingSourceBase::Trace(visitor);
-  }
-
-  // Implementation of UDPReadableStreamWrapper::AcceptDatagram.
-  void AcceptDatagram(base::span<const uint8_t> data,
-                      const absl::optional<net::IPEndPoint>& src_addr);
-
- private:
-  struct QueueEntry : GarbageCollected<QueueEntry> {
-    QueueEntry(UDPMessage* message, base::TimeTicks received_at)
-        : message(message), received_at(std::move(received_at)) {}
-
-    void Trace(Visitor* visitor) const { visitor->Trace(message); }
-
-    const Member<UDPMessage> message;
-    const base::TimeTicks received_at;
-  };
-
-  // Performs local cleanup: clears the queue and sets closed_ to true.
-  void Cleanup();
-
-  // Pops stale datagrams from the front of the queue (that have been
-  // received more than kQueueDatagramAge time ago).
-  // ensure_free_slot guarantees the following condition after the call:
-  // |queue_| <= kQueueSizeLimit - 1.
-  void DiscardStaleDatagrams(const base::TimeTicks& now, bool ensure_free_slot);
-
-  constexpr static const uint32_t kQueueSizeLimit = 10;
-  constexpr static const base::TimeDelta kQueueDatagramAgeLimit =
-      base::Seconds(60);
-
-  const Member<UDPReadableStreamWrapper> udp_readable_stream_wrapper_;
-  HeapDeque<Member<const QueueEntry>> queue_;
-  bool pending_read_ = false;
-};
-
-// UDPReadableStreamWrapper::UnderlyingSource definition
-
-ScriptPromise UDPReadableStreamWrapper::UnderlyingSource::Start(
-    ScriptState* script_state) {
-  return ScriptPromise::CastUndefined(script_state);
-}
-
-ScriptPromise UDPReadableStreamWrapper::UnderlyingSource::pull(
-    ScriptState* script_state) {
-  if (pending_read_) {
-    DCHECK(queue_.empty());
+  ScriptPromise Cancel(ScriptState* script_state, ScriptValue reason) override {
+    GetReadableStreamWrapper()->CloseSocket(/*error=*/false);
     return ScriptPromise::CastUndefined(script_state);
   }
 
-  DiscardStaleDatagrams(base::TimeTicks::Now(),
-                        /*ensure_free_slot=*/false);
-
-  if (!queue_.empty()) {
-    const QueueEntry* entry = queue_.TakeFirst();
-    Controller()->Enqueue(entry->message);
-  } else {
-    pending_read_ = true;
+  void Trace(Visitor* visitor) const override {
+    ReadableStreamWrapper::UnderlyingSource::Trace(visitor);
   }
+};
 
-  return ScriptPromise::CastUndefined(script_state);
-}
+// UDPReadableStreamWrapper definition
 
-ScriptPromise UDPReadableStreamWrapper::UnderlyingSource::Cancel(
+UDPReadableStreamWrapper::UDPReadableStreamWrapper(
     ScriptState* script_state,
-    ScriptValue reason) {
-  Cleanup();
-  udp_readable_stream_wrapper_->CloseInternal(/*error=*/false);
-  return ScriptPromise::CastUndefined(script_state);
+    const Member<UDPSocketMojoRemote> udp_socket,
+    base::OnceCallback<void(bool)> on_close,
+    uint32_t high_water_mark)
+    : ReadableStreamWrapper(script_state),
+      udp_socket_(udp_socket),
+      on_close_(std::move(on_close)) {
+  InitSourceAndReadable(
+      /*source=*/MakeGarbageCollected<UDPUnderlyingSource>(GetScriptState(),
+                                                           this),
+      high_water_mark);
 }
 
-void UDPReadableStreamWrapper::UnderlyingSource::Close(bool error) {
-  if (error) {
-    Controller()->Error(
-        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
-  } else {
-    Controller()->Close();
+void UDPReadableStreamWrapper::Pull() {
+  // Keep pending_receive_requests_ equal to desired_size.
+  DCHECK(udp_socket_->get().is_bound());
+  int32_t desired_size = static_cast<int32_t>(Controller()->DesiredSize());
+  if (desired_size > pending_receive_requests_) {
+    uint32_t receive_more = desired_size - pending_receive_requests_;
+    udp_socket_->get()->ReceiveMore(receive_more);
+    pending_receive_requests_ += receive_more;
   }
-  Cleanup();
-  udp_readable_stream_wrapper_->CloseInternal(error);
 }
 
-void UDPReadableStreamWrapper::UnderlyingSource::Cleanup() {
-  queue_.clear();
-}
-
-void UDPReadableStreamWrapper::UnderlyingSource::AcceptDatagram(
+bool UDPReadableStreamWrapper::Push(
     base::span<const uint8_t> data,
     const absl::optional<net::IPEndPoint>& src_addr) {
-  // Copies |data|.
+  DCHECK_GT(pending_receive_requests_, 0);
+  pending_receive_requests_--;
+
   auto* buffer = DOMUint8Array::Create(data.data(), data.size_bytes());
 
   auto* message = UDPMessage::Create();
-
   message->setData(MakeGarbageCollected<V8UnionArrayBufferOrArrayBufferView>(
       NotShared<DOMUint8Array>(buffer)));
   if (src_addr) {
@@ -147,78 +87,36 @@
     message->setRemotePort(src_addr->port());
   }
 
-  if (pending_read_) {
-    pending_read_ = false;
-    Controller()->Enqueue(message);
-    return;
-  }
+  Controller()->Enqueue(message);
 
-  const auto now = base::TimeTicks::Now();
-
-  DiscardStaleDatagrams(now, /*ensure_free_slot=*/
-                        true);
-  queue_.push_back(
-      MakeGarbageCollected<const QueueEntry>(message, std::move(now)));
+  return true;
 }
 
-void UDPReadableStreamWrapper::UnderlyingSource::DiscardStaleDatagrams(
-    const base::TimeTicks& now,
-    bool ensure_free_slot) {
-  while (!queue_.empty() &&
-         (queue_.size() > kQueueSizeLimit ||
-          now - queue_.front()->received_at > kQueueDatagramAgeLimit)) {
-    queue_.pop_front();
-  }
-
-  if (ensure_free_slot && !queue_.empty() && queue_.size() == kQueueSizeLimit) {
-    queue_.pop_front();
-  }
-}
-
-// UDPReadableStreamWrapper definition
-
-UDPReadableStreamWrapper::UDPReadableStreamWrapper(
-    ScriptState* script_state,
-    const Member<UDPSocketMojoRemote> udp_socket,
-    base::OnceCallback<void(bool)> on_close)
-    : script_state_(script_state),
-      udp_socket_(udp_socket),
-      on_close_(std::move(on_close)) {
-  if (udp_socket_->get().is_bound()) {
-    udp_socket_->get()->ReceiveMore(kNumAdditionalDatagrams);
-  }
-  ScriptState::Scope scope(script_state_);
-  source_ = MakeGarbageCollected<UDPReadableStreamWrapper::UnderlyingSource>(
-      script_state_, this);
-  readable_ = ReadableStream::CreateWithCountQueueingStrategy(script_state_,
-                                                              source_, 1);
-}
-
-UDPReadableStreamWrapper::~UDPReadableStreamWrapper() = default;
-
 void UDPReadableStreamWrapper::Trace(Visitor* visitor) const {
-  visitor->Trace(script_state_);
   visitor->Trace(udp_socket_);
-  visitor->Trace(source_);
-  visitor->Trace(readable_);
+  ReadableStreamWrapper::Trace(visitor);
 }
 
-void UDPReadableStreamWrapper::Close(bool error) {
-  source_->Close(error);
-}
-
-void UDPReadableStreamWrapper::CloseInternal(bool error) {
-  // Only called once.
+void UDPReadableStreamWrapper::CloseSocket(bool error) {
+  DCHECK_EQ(GetState(), State::kOpen);
   std::move(on_close_).Run(error);
+  DCHECK_NE(GetState(), State::kOpen);
 }
 
-void UDPReadableStreamWrapper::AcceptDatagram(
-    base::span<const uint8_t> data,
-    const absl::optional<net::IPEndPoint>& src_addr) {
-  source_->AcceptDatagram(data, src_addr);
-  if (udp_socket_->get().is_bound()) {
-    udp_socket_->get()->ReceiveMore(kNumAdditionalDatagrams);
+void UDPReadableStreamWrapper::CloseStream(bool error) {
+  if (GetState() != State::kOpen) {
+    return;
   }
+  SetState(error ? State::kAborted : State::kClosed);
+
+  if (error) {
+    Controller()->Error(
+        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
+  } else {
+    Controller()->Close();
+  }
+
+  on_close_.Reset();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.h b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.h
index 30c60b1..32fd921d 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.h
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper.h
@@ -8,6 +8,7 @@
 #include "base/callback_forward.h"
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/direct_sockets/udp_socket_mojo_remote.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -18,47 +19,32 @@
 
 namespace blink {
 
-class ReadableStream;
-
-class MODULES_EXPORT UDPReadableStreamWrapper final
-    : public GarbageCollected<UDPReadableStreamWrapper> {
+class MODULES_EXPORT UDPReadableStreamWrapper
+    : public GarbageCollected<UDPReadableStreamWrapper>,
+      public ReadableStreamWrapper {
  public:
   UDPReadableStreamWrapper(ScriptState* script_state,
                            const Member<UDPSocketMojoRemote> udp_socket,
-                           base::OnceCallback<void(bool)> on_close);
-  ~UDPReadableStreamWrapper();
+                           base::OnceCallback<void(bool)> on_close,
+                           uint32_t high_water_mark);
 
-  ReadableStream* Readable() const { return readable_; }
+  void Pull() override;
 
-  // Forwards incoming datagrams to UnderlyingSource::AcceptDatagram.
-  void AcceptDatagram(base::span<const uint8_t> data,
-                      const absl::optional<net::IPEndPoint>& src_addr);
+  bool Push(base::span<const uint8_t> data,
+            const absl::optional<net::IPEndPoint>& src_addr) override;
 
-  // Called by UDPSocket::DoClose(). Forwards close request to
-  // UnderlyingSource::Close().
-  void Close(bool error = false);
+  void CloseStream(bool error) override;
+  void CloseSocket(bool error) override;
 
-  void Trace(Visitor* visitor) const;
+  void Trace(Visitor* visitor) const override;
 
  private:
-  class UnderlyingSource;
-  friend class UnderlyingSource;
-
-  // Called by UnderlyingSource::Close() or UnderlyingSource::cancel()
-  // (depending on whether the close request came from the reader or from the
-  // socket itself). Executes close callback (on_close_).
-  void CloseInternal(bool error);
-
-  // Fetch kNumAdditionalDiagrams on every AcceptDatagram call.
-  constexpr static const uint32_t kNumAdditionalDatagrams = 5;
-
-  const Member<ScriptState> script_state_;
+  class UDPUnderlyingSource;
 
   const Member<UDPSocketMojoRemote> udp_socket_;
   base::OnceCallback<void(bool)> on_close_;
 
-  Member<UDPReadableStreamWrapper::UnderlyingSource> source_;
-  Member<ReadableStream> readable_;
+  int32_t pending_receive_requests_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper_unittest.cc
index eacdeac..40d9f1a 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_readable_stream_wrapper_unittest.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
@@ -43,9 +44,8 @@
 
   void ProvideRequestedDatagrams(UDPReadableStreamWrapper* stream) {
     while (num_requested_datagrams > 0) {
-      stream->AcceptDatagram(
-          datagram_.Span8(),
-          net::IPEndPoint{net::IPAddress::IPv4Localhost(), 0U});
+      stream->Push(datagram_.Span8(),
+                   net::IPEndPoint{net::IPAddress::IPv4Localhost(), 0U});
       num_requested_datagrams--;
     }
   }
@@ -58,33 +58,38 @@
   String datagram_{"abcde"};
 };
 
-class StreamCreator {
-  STACK_ALLOCATED();
-
+class StreamCreator : public GarbageCollected<StreamCreator> {
  public:
-  explicit StreamCreator(const V8TestingScope& scope,
-                         FakeDirectUDPSocket* fake_udp_socket)
-      : scope_(scope), receiver_{fake_udp_socket} {}
+  StreamCreator() : receiver_{&fake_udp_socket_} {}
 
   ~StreamCreator() { test::RunPendingTasks(); }
 
-  UDPReadableStreamWrapper* Create() {
+  UDPReadableStreamWrapper* Create(const V8TestingScope& scope) {
     auto* udp_socket =
-        MakeGarbageCollected<UDPSocketMojoRemote>(scope_.GetExecutionContext());
+        MakeGarbageCollected<UDPSocketMojoRemote>(scope.GetExecutionContext());
     udp_socket->get().Bind(
         receiver_.BindNewPipeAndPassRemote(),
-        scope_.GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
+        scope.GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
 
-    auto* script_state = scope_.GetScriptState();
-    auto* udp_readable_stream_wrapper =
-        MakeGarbageCollected<UDPReadableStreamWrapper>(script_state, udp_socket,
-                                                       base::DoNothing());
-    return udp_readable_stream_wrapper;
+    auto* script_state = scope.GetScriptState();
+    stream_wrapper_ = MakeGarbageCollected<UDPReadableStreamWrapper>(
+        script_state, udp_socket,
+        WTF::Bind(&StreamCreator::Close, WrapPersistent(this)),
+        /*high_water_mark=*/1);
+    return stream_wrapper_;
   }
 
+  void Close(bool error) { stream_wrapper_->CloseStream(error); }
+
+  void Trace(Visitor* visitor) const { visitor->Trace(stream_wrapper_); }
+
+  FakeDirectUDPSocket& fake_udp_socket() { return fake_udp_socket_; }
+
  private:
-  const V8TestingScope& scope_;
   mojo::Receiver<blink::mojom::blink::DirectUDPSocket> receiver_;
+
+  FakeDirectUDPSocket fake_udp_socket_;
+  Member<UDPReadableStreamWrapper> stream_wrapper_;
 };
 
 std::pair<UDPMessage*, bool> UnpackPromiseResult(const V8TestingScope& scope,
@@ -115,19 +120,21 @@
 
 TEST(UDPReadableStreamWrapperTest, Create) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
   EXPECT_TRUE(udp_readable_stream_wrapper->Readable());
 }
 
 TEST(UDPReadableStreamWrapperTest, ReadUdpMessage) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* script_state = scope.GetScriptState();
 
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
   // Ensure that udp_socket_->ReceiveMore(...) call from
   // UDPReadableStreamWrapper constructor lands before calling
   // fake_udp_socket.ProvideRequestedDiagrams().
@@ -135,6 +142,7 @@
 
   fake_udp_socket.ProvideRequestedDatagrams(udp_readable_stream_wrapper);
 
+  auto* script_state = scope.GetScriptState();
   auto* reader =
       udp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
@@ -153,16 +161,18 @@
 
 TEST(UDPReadableStreamWrapperTest, ReadDelayedUdpMessage) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* script_state = scope.GetScriptState();
 
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
+
   // Ensure that udp_socket_->ReceiveMore(...) call from
   // UDPReadableStreamWrapper constructor lands before calling
   // fake_udp_socket.ProvideRequestedDiagrams().
   test::RunPendingTasks();
 
+  auto* script_state = scope.GetScriptState();
   auto* reader =
       udp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
@@ -183,11 +193,11 @@
 
 TEST(UDPReadableStreamWrapperTest, ReadEmptyUdpMessage) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* script_state = scope.GetScriptState();
 
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
   // Ensure that udp_socket_->ReceiveMore(...) call from
   // UDPReadableStreamWrapper constructor lands before calling
   // fake_udp_socket.ProvideRequestedDiagrams().
@@ -197,6 +207,7 @@
   fake_udp_socket.SetTestingDatagram({});
   fake_udp_socket.ProvideRequestedDatagrams(udp_readable_stream_wrapper);
 
+  auto* script_state = scope.GetScriptState();
   auto* reader =
       udp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
@@ -215,16 +226,16 @@
 
 TEST(UDPReadableStreamWrapperTest, CancelStreamFromReader) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* script_state = scope.GetScriptState();
 
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
   // Ensure that udp_socket_->ReceiveMore(...) call from
   // UDPReadableStreamWrapper constructor lands before calling
   // fake_udp_socket.ProvideRequestedDiagrams().
   test::RunPendingTasks();
 
+  auto* script_state = scope.GetScriptState();
   auto* reader =
       udp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
@@ -248,21 +259,21 @@
 
 TEST(UDPReadableStreamWrapperTest, CancelStreamFromWrapper) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* script_state = scope.GetScriptState();
 
-  auto* udp_readable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_readable_stream_wrapper = stream_creator->Create(scope);
+
   // Ensure that udp_socket_->ReceiveMore(...) call from
   // UDPReadableStreamWrapper constructor lands before calling
   // fake_udp_socket.ProvideRequestedDiagrams().
   test::RunPendingTasks();
 
+  auto* script_state = scope.GetScriptState();
   auto* reader =
       udp_readable_stream_wrapper->Readable()->GetDefaultReaderForTesting(
           script_state, ASSERT_NO_EXCEPTION);
 
-  udp_readable_stream_wrapper->Close();
+  udp_readable_stream_wrapper->CloseStream(/*error=*/false);
 
   ScriptPromiseTester read_tester(
       script_state, reader->read(script_state, ASSERT_NO_EXCEPTION));
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_socket.cc b/third_party/blink/renderer/modules/direct_sockets/udp_socket.cc
index f363d61..1376688 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_socket.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_socket.cc
@@ -36,6 +36,8 @@
 constexpr char kUDPNetworkFailuresHistogramName[] =
     "DirectSockets.UDPNetworkFailures";
 
+constexpr uint32_t readableStreamDefaultBufferSize = 32;
+
 mojom::blink::DirectSocketOptionsPtr CreateUDPSocketOptions(
     const String& address,
     const uint16_t port,
@@ -46,6 +48,13 @@
   socket_options->remote_hostname = address;
   socket_options->remote_port = port;
 
+  if (options->hasReadableStreamBufferSize() &&
+      options->readableStreamBufferSize() == 0) {
+    exception_state.ThrowTypeError(
+        "readableStreamBufferSize must be greater than zero.");
+    return {};
+  }
+
   if (options->hasSendBufferSize()) {
     socket_options->send_buffer_size = options->sendBufferSize();
   }
@@ -94,6 +103,10 @@
     return false;
   }
 
+  if (options->hasReadableStreamBufferSize()) {
+    readable_stream_buffer_size_ = options->readableStreamBufferSize();
+  }
+
   ConnectService();
 
   service_->get()->OpenUdpSocket(
@@ -108,18 +121,17 @@
                      const absl::optional<net::IPEndPoint>& local_addr,
                      const absl::optional<net::IPEndPoint>& peer_addr) {
   if (result == net::OK && peer_addr) {
-    udp_readable_stream_wrapper_ =
-        MakeGarbageCollected<UDPReadableStreamWrapper>(
-            script_state_, udp_socket_,
-            WTF::Bind(&UDPSocket::CloseInternal, WrapWeakPersistent(this)));
-    udp_writable_stream_wrapper_ =
-        MakeGarbageCollected<UDPWritableStreamWrapper>(script_state_,
-                                                       udp_socket_);
+    readable_stream_wrapper_ = MakeGarbageCollected<UDPReadableStreamWrapper>(
+        script_state_, udp_socket_,
+        WTF::Bind(&UDPSocket::CloseInternal, WrapWeakPersistent(this)),
+        readable_stream_buffer_size_.value_or(readableStreamDefaultBufferSize));
+    writable_stream_wrapper_ = MakeGarbageCollected<UDPWritableStreamWrapper>(
+        script_state_, udp_socket_);
 
     auto* connection = UDPSocketConnection::Create();
 
-    connection->setReadable(udp_readable_stream_wrapper_->Readable());
-    connection->setWritable(udp_writable_stream_wrapper_->Writable());
+    connection->setReadable(readable_stream_wrapper_->Readable());
+    connection->setWritable(writable_stream_wrapper_->Writable());
 
     connection->setRemoteAddress(String{peer_addr->ToStringWithoutPort()});
     connection->setRemotePort(peer_addr->port());
@@ -182,21 +194,14 @@
     return;
   }
 
-  udp_readable_stream_wrapper_->AcceptDatagram(*data, src_addr);
-}
-
-bool UDPSocket::Initialized() const {
-  return udp_readable_stream_wrapper_ && udp_writable_stream_wrapper_;
+  readable_stream_wrapper_->Push(*data, src_addr);
 }
 
 bool UDPSocket::HasPendingActivity() const {
-  return Initialized() && udp_writable_stream_wrapper_->IsActive();
+  return Socket::HasPendingActivity();
 }
 
 void UDPSocket::Trace(Visitor* visitor) const {
-  visitor->Trace(udp_readable_stream_wrapper_);
-  visitor->Trace(udp_writable_stream_wrapper_);
-
   visitor->Trace(udp_socket_);
   visitor->Trace(socket_listener_);
 
@@ -217,7 +222,7 @@
 
 void UDPSocket::CloseOnError() {
   if (Initialized()) {
-    udp_readable_stream_wrapper_->Close(/*error=*/true);
+    CloseInternal(/*error=*/true);
     DCHECK(Closed());
   }
 }
@@ -237,19 +242,15 @@
   }
 
   if (!options->hasForce() || !options->force()) {
-    if (ReadableStream::IsLocked(udp_readable_stream_wrapper_->Readable())) {
+    if (readable_stream_wrapper_->Locked() ||
+        writable_stream_wrapper_->Locked()) {
       exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        "Close called on locked Readable.");
-      return;
-    }
-    if (WritableStream::IsLocked(udp_writable_stream_wrapper_->Writable())) {
-      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        "Close called on locked Writable.");
+                                        "Close called on locked streams.");
       return;
     }
   }
 
-  udp_readable_stream_wrapper_->Close(/*error=*/false);
+  CloseInternal(/*error=*/false);
   DCHECK(Closed());
 }
 
@@ -259,8 +260,9 @@
 
   socket_listener_.reset();
 
-  // Reject pending write promises.
-  udp_writable_stream_wrapper_->Close(error);
+  // Reject pending read/write promises.
+  readable_stream_wrapper_->CloseStream(error);
+  writable_stream_wrapper_->CloseStream(error);
 
   // Close the socket.
   udp_socket_->Close();
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_socket.h b/third_party/blink/renderer/modules/direct_sockets/udp_socket.h
index 7861d96..11655fe 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_socket.h
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_socket.h
@@ -107,8 +107,6 @@
                   const absl::optional<::net::IPEndPoint>& src_addr,
                   absl::optional<::base::span<const ::uint8_t>> data) override;
 
-  bool Initialized() const;
-
   void OnServiceConnectionError() override;
   void OnSocketConnectionError();
 
@@ -120,8 +118,7 @@
   HeapMojoReceiver<network::mojom::blink::UDPSocketListener, UDPSocket>
       socket_listener_;
 
-  Member<UDPReadableStreamWrapper> udp_readable_stream_wrapper_;
-  Member<UDPWritableStreamWrapper> udp_writable_stream_wrapper_;
+  absl::optional<uint32_t> readable_stream_buffer_size_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.cc b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.cc
index f587c499..56fffda 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.cc
@@ -19,130 +19,59 @@
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/direct_sockets/udp_socket_mojo_remote.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
 // UDPWritableStreamWrapper::UnderlyingSink declaration
 
-class UDPWritableStreamWrapper::UnderlyingSink final
-    : public UnderlyingSinkBase {
+class UDPWritableStreamWrapper::UDPUnderlyingSink final
+    : public WritableStreamWrapper::UnderlyingSink {
  public:
-  explicit UnderlyingSink(UDPWritableStreamWrapper* udp_writable_stream_wrapper)
-      : udp_writable_stream_wrapper_(udp_writable_stream_wrapper) {}
+  explicit UDPUnderlyingSink(UDPWritableStreamWrapper* writable_stream_wrapper)
+      : WritableStreamWrapper::UnderlyingSink(writable_stream_wrapper) {}
 
-  ScriptPromise start(ScriptState* script_state,
-                      WritableStreamDefaultController* controller,
-                      ExceptionState&) override;
-  ScriptPromise write(ScriptState* script_state,
-                      ScriptValue chunk,
-                      WritableStreamDefaultController*,
-                      ExceptionState& exception_state) override;
-  ScriptPromise close(ScriptState* script_state, ExceptionState&) override;
-  ScriptPromise abort(ScriptState* script_state,
-                      ScriptValue reason,
-                      ExceptionState& exception_state) override;
-
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(udp_writable_stream_wrapper_);
-    UnderlyingSinkBase::Trace(visitor);
+  ScriptPromise close(ScriptState* script_state, ExceptionState&) override {
+    GetWritableStreamWrapper()->CloseStream(/*error=*/false);
+    return ScriptPromise::CastUndefined(script_state);
   }
 
- private:
-  const Member<UDPWritableStreamWrapper> udp_writable_stream_wrapper_;
+  void Trace(Visitor* visitor) const override {
+    WritableStreamWrapper::UnderlyingSink::Trace(visitor);
+  }
 };
 
-// UDPWritableStreamWrapper::UnderlyingSink definition
-
-ScriptPromise UDPWritableStreamWrapper::UnderlyingSink::start(
-    ScriptState* script_state,
-    WritableStreamDefaultController* controller,
-    ExceptionState&) {
-  udp_writable_stream_wrapper_->controller_ = controller;
-  return ScriptPromise::CastUndefined(script_state);
-}
-
-ScriptPromise UDPWritableStreamWrapper::UnderlyingSink::write(
-    ScriptState* script_state,
-    ScriptValue chunk,
-    WritableStreamDefaultController*,
-    ExceptionState& exception_state) {
-  return udp_writable_stream_wrapper_->SinkWrite(script_state, chunk,
-                                                 exception_state);
-}
-
-ScriptPromise UDPWritableStreamWrapper::UnderlyingSink::close(
-    ScriptState* script_state,
-    ExceptionState&) {
-  // The specification guarantees that this will only be called after all
-  // pending writes have been completed.
-  DCHECK(!udp_writable_stream_wrapper_->send_resolver_);
-
-  // It's not possible to close the writable side of the UDP socket, therefore
-  // no action is taken.
-  return ScriptPromise::CastUndefined(script_state);
-}
-
-ScriptPromise UDPWritableStreamWrapper::UnderlyingSink::abort(
-    ScriptState* script_state,
-    ScriptValue reason,
-    ExceptionState& exception_state) {
-  // The specification guarantees that this will only be called after all
-  // pending writes have been completed.
-  DCHECK(!udp_writable_stream_wrapper_->send_resolver_);
-
-  // It's not possible to close the writable side of the UDP socket, therefore
-  // no action is taken.
-  return ScriptPromise::CastUndefined(script_state);
-}
-
 // UDPWritableStreamWrapper definition
 
 UDPWritableStreamWrapper::UDPWritableStreamWrapper(
     ScriptState* script_state,
     const Member<UDPSocketMojoRemote> udp_socket)
-    : ExecutionContextClient(ExecutionContext::From(script_state)),
-      script_state_(script_state),
-      udp_socket_(udp_socket) {
-  ScriptState::Scope scope(script_state);
-  writable_ = WritableStream::CreateWithCountQueueingStrategy(
-      script_state_,
-      MakeGarbageCollected<UDPWritableStreamWrapper::UnderlyingSink>(this), 1);
+    : WritableStreamWrapper(script_state), udp_socket_(udp_socket) {
+  InitSinkAndWritable(/*sink=*/MakeGarbageCollected<UDPUnderlyingSink>(this),
+                      /*high_water_mark=*/1);
 }
 
-UDPWritableStreamWrapper::~UDPWritableStreamWrapper() = default;
-
-bool UDPWritableStreamWrapper::IsActive() const {
+bool UDPWritableStreamWrapper::HasPendingWrite() const {
   return !!send_resolver_;
 }
 
 void UDPWritableStreamWrapper::Trace(Visitor* visitor) const {
-  visitor->Trace(script_state_);
   visitor->Trace(udp_socket_);
   visitor->Trace(send_resolver_);
-  visitor->Trace(writable_);
-  visitor->Trace(controller_);
-  ExecutionContextClient::Trace(visitor);
+  WritableStreamWrapper::Trace(visitor);
 }
 
-ScriptPromise UDPWritableStreamWrapper::SinkWrite(
-    ScriptState* script_state,
-    ScriptValue chunk,
-    ExceptionState& exception_state) {
-  // If socket has been closed.
-  if (!udp_socket_->get().is_bound()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Socket is disconnected.");
-    return ScriptPromise();
-  }
+ScriptPromise UDPWritableStreamWrapper::Write(ScriptValue chunk,
+                                              ExceptionState& exception_state) {
+  DCHECK(udp_socket_->get().is_bound());
 
-  UDPMessage* message = UDPMessage::Create(script_state->GetIsolate(),
+  UDPMessage* message = UDPMessage::Create(GetScriptState()->GetIsolate(),
                                            chunk.V8Value(), exception_state);
   if (exception_state.HadException()) {
     return ScriptPromise();
@@ -158,7 +87,8 @@
   base::span<const uint8_t> data{array_piece.Bytes(), array_piece.ByteLength()};
 
   DCHECK(!send_resolver_);
-  send_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  send_resolver_ =
+      MakeGarbageCollected<ScriptPromiseResolver>(GetScriptState());
 
   // Why not just return send_resolver_->Promise()?
   // In view of the async nature of the write handler, the callback might get
@@ -186,30 +116,27 @@
   }
 }
 
-ScriptValue UDPWritableStreamWrapper::CreateException(ScriptState* script_state,
-                                                      DOMExceptionCode code,
-                                                      const String& message) {
-  return ScriptValue(script_state->GetIsolate(),
-                     V8ThrowDOMException::CreateOrEmpty(
-                         script_state->GetIsolate(), code, message));
-}
+void UDPWritableStreamWrapper::CloseStream(bool error) {
+  if (GetState() != State::kOpen) {
+    return;
+  }
+  SetState(error ? State::kAborted : State::kClosed);
 
-void UDPWritableStreamWrapper::Close(bool error) {
-  ScriptState::Scope scope(script_state_);
+  ScriptState::Scope scope(GetScriptState());
 
   ScriptValue exception =
-      error
-          ? CreateException(script_state_, DOMExceptionCode::kNetworkError,
-                            "Connection aborted by remote")
-          : CreateException(script_state_, DOMExceptionCode::kInvalidStateError,
-                            "Stream closed.");
+      error ? CreateException(GetScriptState(), DOMExceptionCode::kNetworkError,
+                              "Connection aborted by remote")
+            : CreateException(GetScriptState(),
+                              DOMExceptionCode::kInvalidStateError,
+                              "Stream closed.");
 
   if (send_resolver_) {
     send_resolver_->Reject(exception);
     send_resolver_ = nullptr;
   }
 
-  controller_->error(script_state_, exception);
+  Controller()->error(GetScriptState(), exception);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h
index 24248525..929f2d1 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/direct_sockets/stream_wrapper.h"
 #include "third_party/blink/renderer/modules/direct_sockets/udp_socket_mojo_remote.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
@@ -20,49 +21,30 @@
 namespace blink {
 
 class ScriptState;
-class WritableStream;
-class WritableStreamDefaultController;
 
 class MODULES_EXPORT UDPWritableStreamWrapper final
     : public GarbageCollected<UDPWritableStreamWrapper>,
-      public ExecutionContextClient {
+      public WritableStreamWrapper {
  public:
   UDPWritableStreamWrapper(ScriptState* script_state,
                            const Member<UDPSocketMojoRemote> udp_socket_);
-  ~UDPWritableStreamWrapper();
 
-  WritableStream* Writable() const { return writable_; }
-
-  bool IsActive() const;
+  void CloseStream(bool error) override;
+  bool HasPendingWrite() const override;
 
   void Trace(Visitor*) const override;
 
-  // Called before destruction of the StreamWrapper.
-  void Close(bool error = false);
-
  private:
-  class UnderlyingSink;
+  class UDPUnderlyingSink;
 
-  // Implements UnderlyingSink::write().
-  ScriptPromise SinkWrite(ScriptState* script_state,
-                          ScriptValue chunk,
-                          ExceptionState& exception_state);
+  ScriptPromise Write(ScriptValue chunk,
+                      ExceptionState& exception_state) override;
 
   // Callback for DirectUDPSocket::Send().
   void OnSend(int32_t result);
 
-  // Creates a DOMException.
-  static ScriptValue CreateException(ScriptState*,
-                                     DOMExceptionCode,
-                                     const String& message);
-
-  const Member<ScriptState> script_state_;
-
   const Member<UDPSocketMojoRemote> udp_socket_;
   Member<ScriptPromiseResolver> send_resolver_;
-
-  Member<WritableStream> writable_;
-  Member<WritableStreamDefaultController> controller_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper_unittest.cc
index 46772821..60be7a39 100644
--- a/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/udp_writable_stream_wrapper_unittest.cc
@@ -51,49 +51,49 @@
   Vector<uint8_t> data_;
 };
 
-class StreamCreator {
-  STACK_ALLOCATED();
-
+class StreamCreator : public GarbageCollected<StreamCreator> {
  public:
-  explicit StreamCreator(const V8TestingScope& scope,
-                         FakeDirectUDPSocket* fake_udp_socket)
-      : scope_(scope), receiver_{fake_udp_socket} {}
-
+  StreamCreator() : receiver_{&fake_udp_socket_} {}
   ~StreamCreator() { test::RunPendingTasks(); }
 
-  UDPWritableStreamWrapper* Create() {
+  UDPWritableStreamWrapper* Create(const V8TestingScope& scope) {
     auto* udp_socket =
-        MakeGarbageCollected<UDPSocketMojoRemote>(scope_.GetExecutionContext());
+        MakeGarbageCollected<UDPSocketMojoRemote>(scope.GetExecutionContext());
     udp_socket->get().Bind(
         receiver_.BindNewPipeAndPassRemote(),
-        scope_.GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
+        scope.GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
 
-    auto* script_state = scope_.GetScriptState();
-    auto* udp_writable_stream_wrapper =
-        MakeGarbageCollected<UDPWritableStreamWrapper>(script_state,
-                                                       udp_socket);
-    return udp_writable_stream_wrapper;
+    auto* script_state = scope.GetScriptState();
+    stream_wrapper_ = MakeGarbageCollected<UDPWritableStreamWrapper>(
+        script_state, udp_socket);
+    return stream_wrapper_;
   }
 
+  void Trace(Visitor* visitor) const { visitor->Trace(stream_wrapper_); }
+
+  FakeDirectUDPSocket& fake_udp_socket() { return fake_udp_socket_; }
+
  private:
-  const V8TestingScope& scope_;
+  FakeDirectUDPSocket fake_udp_socket_;
   mojo::Receiver<blink::mojom::blink::DirectUDPSocket> receiver_;
+  Member<UDPWritableStreamWrapper> stream_wrapper_;
 };
 
 TEST(UDPWritableStreamWrapperTest, Create) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   EXPECT_TRUE(udp_writable_stream_wrapper->Writable());
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteUdpMessage) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
 
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -113,15 +113,16 @@
 
   ASSERT_TRUE(tester.IsFulfilled());
 
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
   EXPECT_THAT(fake_udp_socket.GetReceivedData(), ::testing::ElementsAre('A'));
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteUdpMessageFromTypedArray) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
 
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -143,16 +144,17 @@
 
   ASSERT_TRUE(tester.IsFulfilled());
 
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
   EXPECT_THAT(fake_udp_socket.GetReceivedData(),
               ::testing::ElementsAre('A', 'B', 'C'));
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteUdpMessageWithEmptyDataField) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
 
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -175,14 +177,16 @@
   ASSERT_TRUE(tester.IsFulfilled());
 
   // Nothing should have been written from the empty DOMArrayBuffer.
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
   EXPECT_THAT(fake_udp_socket.GetReceivedData(), ::testing::ElementsAre());
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteUdpMessageWithoutDataField) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -206,16 +210,15 @@
 
   ASSERT_TRUE(exception);
   ASSERT_EQ(exception->name(), "DataError");
-  ASSERT_EQ(exception->message(),
-            "Failed to execute 'write' on 'UnderlyingSinkBase': UDPMessage: "
-            "missing 'data' field.");
+  ASSERT_TRUE(exception->message().Contains("missing 'data' field"));
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteAfterFinishedWrite) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -237,15 +240,17 @@
     ASSERT_TRUE(tester.IsFulfilled());
   }
 
+  auto& fake_udp_socket = stream_creator->fake_udp_socket();
   EXPECT_THAT(fake_udp_socket.GetReceivedData(),
               ::testing::ElementsAre('A', 'B'));
 }
 
 TEST(UDPWritableStreamWrapperTest, WriteAfterClose) {
   V8TestingScope scope;
-  FakeDirectUDPSocket fake_udp_socket;
-  StreamCreator stream_creator{scope, &fake_udp_socket};
-  auto* udp_writable_stream_wrapper = stream_creator.Create();
+
+  auto* stream_creator = MakeGarbageCollected<StreamCreator>();
+  auto* udp_writable_stream_wrapper = stream_creator->Create(scope);
+
   auto* script_state = scope.GetScriptState();
 
   auto* writer = udp_writable_stream_wrapper->Writable()->getWriter(
@@ -270,6 +275,9 @@
 
   ASSERT_TRUE(write_tester.IsFulfilled());
 
+  ASSERT_EQ(udp_writable_stream_wrapper->GetState(),
+            StreamWrapper::State::kClosed);
+
   ScriptPromise write_after_close_result =
       writer->write(script_state, ScriptValue::From(script_state, message),
                     ASSERT_NO_EXCEPTION);
diff --git a/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc b/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
index c185f73..786c3b9 100644
--- a/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
@@ -279,6 +279,9 @@
     bool report_unknown_names,
     MediaTrackConstraintSetPlatform& result,
     MediaErrorState& error_state) {
+  if (old_names.size() > 0) {
+    UseCounter::Count(context, WebFeature::kOldConstraintsParsed);
+  }
   for (const NameValueStringConstraint& constraint : old_names) {
     if (constraint.name_.Equals(kMinAspectRatio)) {
       result.aspect_ratio.SetMin(atof(constraint.value_.Utf8().c_str()));
@@ -374,9 +377,11 @@
             context, WebFeature::kRTCConstraintEnableDtlsSrtpFalse);
       }
 #if BUILDFLAG(IS_FUCHSIA)
-      // Special dispensation for Fuchsia to run SDES in 2002
+      // Special dispensation for Fuchsia to run SDES in 2022
       // TODO(crbug.com/804275): Delete when Fuchsia no longer depends on it.
       result.enable_dtls_srtp.SetExact(ToBoolean(constraint.value_));
+#else
+      UseCounter::Count(context, WebFeature::kOldConstraintIgnored);
 #endif
     } else if (constraint.name_.Equals(kEnableRtpDataChannels)) {
       // This constraint does not turn on RTP data channels, but we do not
@@ -389,6 +394,7 @@
         Deprecation::CountDeprecation(
             context, WebFeature::kRTCConstraintEnableRtpDataChannelsFalse);
       }
+      UseCounter::Count(context, WebFeature::kOldConstraintIgnored);
     } else if (constraint.name_.Equals(kEnableDscp)) {
       result.enable_dscp.SetExact(ToBoolean(constraint.value_));
     } else if (constraint.name_.Equals(kEnableIPv6)) {
@@ -432,6 +438,7 @@
           mojom::ConsoleMessageLevel::kWarning,
           "Obsolete constraint named " + String(constraint.name_) +
               " is ignored. Please stop using it."));
+      UseCounter::Count(context, WebFeature::kOldConstraintIgnored);
     } else if (constraint.name_.Equals(kTestConstraint1) ||
                constraint.name_.Equals(kTestConstraint2)) {
       // These constraints are only for testing parsing.
@@ -442,16 +449,16 @@
       }
     } else {
       if (report_unknown_names) {
-        // TODO(hta): UMA stats for unknown constraints passed.
-        // https://crbug.com/576613
+        UseCounter::Count(context, WebFeature::kOldConstraintRejected);
         context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
             mojom::ConsoleMessageSource::kDeprecation,
             mojom::ConsoleMessageLevel::kWarning,
             "Unknown constraint named " + String(constraint.name_) +
                 " rejected"));
-        // TODO(crbug.com/856176): Don't throw an error.
         error_state.ThrowConstraintError("Unknown name of constraint detected",
                                          constraint.name_);
+      } else {
+        UseCounter::Count(context, WebFeature::kOldConstraintNotReported);
       }
     }
   }
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index b3f3224f..d4b648c 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -127,6 +127,8 @@
     "rtc_void_request_promise_impl.h",
     "rtc_void_request_script_promise_resolver_impl.cc",
     "rtc_void_request_script_promise_resolver_impl.h",
+    "rtp_contributing_source_cache.cc",
+    "rtp_contributing_source_cache.h",
     "speed_limit_uma_listener.cc",
     "speed_limit_uma_listener.h",
     "thermal_resource.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
index 0d2a9b2..f917dd8 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
@@ -207,6 +207,7 @@
   rtc::Thread* GetWorkerThread() { return worker_thread_; }
   rtc::Thread* GetNetworkThread() { return network_thread_; }
   base::Thread& GetChromeSignalingThread() { return chrome_signaling_thread_; }
+  base::Thread& GetChromeWorkerThread() { return chrome_worker_thread_; }
   base::Thread& GetChromeNetworkThread() { return chrome_network_thread_; }
 
  private:
@@ -296,6 +297,9 @@
 base::Thread& GetChromeSignalingThread() {
   return StaticDeps().GetChromeSignalingThread();
 }
+base::Thread& GetChromeWorkerThread() {
+  return StaticDeps().GetChromeWorkerThread();
+}
 base::Thread& GetChromeNetworkThread() {
   return StaticDeps().GetChromeNetworkThread();
 }
@@ -925,6 +929,14 @@
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
+PeerConnectionDependencyFactory::GetWebRtcWorkerTaskRunner() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return GetChromeWorkerThread().IsRunning()
+             ? GetChromeWorkerThread().task_runner()
+             : nullptr;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
 PeerConnectionDependencyFactory::GetWebRtcSignalingTaskRunner() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   EnsureInitialized();
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h
index d893921..7285f5e1 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h
@@ -133,8 +133,10 @@
 
   void EnsureInitialized();
 
-  // Returns the SingleThreadTaskRunner suitable for running WebRTC networking.
-  // An rtc::Thread will have already been created.
+  // Returns the SingleThreadTaskRunner corresponding to the WebRTC worker or
+  // network threads (rtc::Thread), if they exist. These threads are ensured to
+  // exist after an RTCPeerConnectionHandler has been Initialized().
+  scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcWorkerTaskRunner();
   scoped_refptr<base::SingleThreadTaskRunner> GetWebRtcNetworkTaskRunner();
 
   virtual scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index fbaf1bf..2df84de7 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -39,6 +39,7 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -828,16 +829,16 @@
   }
 
   // Tests might need a custom RtcPeerConnectionHandler implementation.
+  PeerConnectionDependencyFactory& dependency_factory =
+      PeerConnectionDependencyFactory::From(*context);
   if (!g_create_rpc_peer_connection_handler_callback_.Get().is_null()) {
     peer_handler_ =
         std::move(g_create_rpc_peer_connection_handler_callback_.Get()).Run();
   } else {
-    peer_handler_ =
-        PeerConnectionDependencyFactory::From(*context)
-            .CreateRTCPeerConnectionHandler(
-                this, window->GetTaskRunner(TaskType::kInternalMedia),
-                force_encoded_audio_insertable_streams_,
-                force_encoded_video_insertable_streams_);
+    peer_handler_ = dependency_factory.CreateRTCPeerConnectionHandler(
+        this, window->GetTaskRunner(TaskType::kInternalMedia),
+        force_encoded_audio_insertable_streams_,
+        force_encoded_video_insertable_streams_);
   }
 
   if (!peer_handler_) {
@@ -854,6 +855,16 @@
     DCHECK(exception_state.HadException());
     return;
   }
+  // After Initialize() with a real `peer_handler_`, WebRTC threads exist.
+  scoped_refptr<base::SingleThreadTaskRunner> worker_thread =
+      dependency_factory.GetWebRtcWorkerTaskRunner();
+  if (!worker_thread) {
+    // This path is only used in some unit test environments with a fake
+    // `peer_handler_` that does not ensure WebRTC threads exist.
+    worker_thread =
+        base::ThreadPool::CreateSingleThreadTaskRunner({base::MayBlock()});
+  }
+  rtp_contributing_source_cache_.emplace(this, std::move(worker_thread));
   // The RTCPeerConnection was successfully constructed.
   closed_ = false;
   peer_handler_unregistered_ = false;
@@ -2373,6 +2384,11 @@
   return rtp_receivers_;
 }
 
+RtpContributingSourceCache& RTCPeerConnection::GetRtpContributingSourceCache() {
+  DCHECK(rtp_contributing_source_cache_.has_value());
+  return rtp_contributing_source_cache_.value();
+}
+
 RTCRtpTransceiver* RTCPeerConnection::addTransceiver(
     const V8UnionMediaStreamTrackOrString* track_or_kind,
     const RTCRtpTransceiverInit* init,
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 010545d..0c1711c0 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -36,6 +36,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -50,6 +51,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_enums.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
@@ -276,6 +278,7 @@
   const HeapVector<Member<RTCRtpTransceiver>>& getTransceivers() const;
   const HeapVector<Member<RTCRtpSender>>& getSenders() const;
   const HeapVector<Member<RTCRtpReceiver>>& getReceivers() const;
+  RtpContributingSourceCache& GetRtpContributingSourceCache();
   RTCRtpTransceiver* addTransceiver(
       const V8UnionMediaStreamTrackOrString* track_or_kind,
       const RTCRtpTransceiverInit* init,
@@ -613,6 +616,9 @@
   HeapVector<Member<RTCRtpSender>> rtp_senders_;
   HeapVector<Member<RTCRtpReceiver>> rtp_receivers_;
   HeapVector<Member<RTCRtpTransceiver>> transceivers_;
+  // Always has a value if initialization was successful (the constructor did
+  // not throw an exception).
+  absl::optional<RtpContributingSourceCache> rtp_contributing_source_cache_;
 
   // A map of all webrtc::DtlsTransports that have a corresponding
   // RTCDtlsTransport object. Garbage collection will remove map entries
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index 4ab13c2..4e7d7919f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -16,9 +16,6 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_decoding_parameters.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_capability.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_parameters.h"
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/modules/peerconnection/identifiability_metrics.h"
@@ -34,7 +31,6 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h"
 #include "third_party/blink/renderer/modules/peerconnection/web_rtc_stats_report_callback_resolver.h"
-#include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
@@ -63,12 +59,12 @@
   DCHECK(pc_);
   DCHECK(receiver_);
   DCHECK(track_);
-  if (force_encoded_audio_insertable_streams_ && track_->kind() == "audio") {
+  if (force_encoded_audio_insertable_streams_ && kind() == MediaKind::kAudio) {
     encoded_audio_transformer_ =
         receiver_->GetEncodedAudioStreamTransformer()->GetBroker();
     RegisterEncodedAudioStreamCallback();
   }
-  if (force_encoded_video_insertable_streams_ && track_->kind() == "video")
+  if (force_encoded_video_insertable_streams_ && kind() == MediaKind::kVideo)
     RegisterEncodedVideoStreamCallback();
 }
 
@@ -108,86 +104,16 @@
 RTCRtpReceiver::getSynchronizationSources(ScriptState* script_state,
                                           ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Window is detached");
-    return HeapVector<Member<RTCRtpSynchronizationSource>>();
-  }
-
-  UpdateSourcesIfNeeded();
-
-  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
-  DocumentLoadTiming& time_converter =
-      window->GetFrame()->Loader().GetDocumentLoader()->GetTiming();
-
-  HeapVector<Member<RTCRtpSynchronizationSource>> synchronization_sources;
-  for (const auto& web_source : web_sources_) {
-    if (web_source->SourceType() != RTCRtpSource::Type::kSSRC)
-      continue;
-    RTCRtpSynchronizationSource* synchronization_source =
-        MakeGarbageCollected<RTCRtpSynchronizationSource>();
-    synchronization_source->setTimestamp(
-        time_converter.MonotonicTimeToPseudoWallTime(web_source->Timestamp())
-            .InMilliseconds());
-    synchronization_source->setSource(web_source->Source());
-    if (web_source->AudioLevel().has_value()) {
-      synchronization_source->setAudioLevel(web_source->AudioLevel().value());
-    }
-    if (web_source->CaptureTimestamp().has_value()) {
-      synchronization_source->setCaptureTimestamp(
-          web_source->CaptureTimestamp().value());
-    }
-    if (web_source->SenderCaptureTimeOffset().has_value()) {
-      synchronization_source->setSenderCaptureTimeOffset(
-          web_source->SenderCaptureTimeOffset().value());
-    }
-    synchronization_source->setRtpTimestamp(web_source->RtpTimestamp());
-    synchronization_sources.push_back(synchronization_source);
-  }
-  return synchronization_sources;
+  return pc_->GetRtpContributingSourceCache().getSynchronizationSources(
+      script_state, exception_state, this);
 }
 
 HeapVector<Member<RTCRtpContributingSource>>
 RTCRtpReceiver::getContributingSources(ScriptState* script_state,
                                        ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Window is detached");
-    return HeapVector<Member<RTCRtpContributingSource>>();
-  }
-
-  UpdateSourcesIfNeeded();
-
-  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
-  DocumentLoadTiming& time_converter =
-      window->GetFrame()->Loader().GetDocumentLoader()->GetTiming();
-
-  HeapVector<Member<RTCRtpContributingSource>> contributing_sources;
-  for (const auto& web_source : web_sources_) {
-    if (web_source->SourceType() != RTCRtpSource::Type::kCSRC)
-      continue;
-    RTCRtpContributingSource* contributing_source =
-        MakeGarbageCollected<RTCRtpContributingSource>();
-    contributing_source->setTimestamp(
-        time_converter.MonotonicTimeToPseudoWallTime(web_source->Timestamp())
-            .InMilliseconds());
-    contributing_source->setSource(web_source->Source());
-    if (web_source->AudioLevel().has_value()) {
-      contributing_source->setAudioLevel(web_source->AudioLevel().value());
-    }
-    if (web_source->CaptureTimestamp().has_value()) {
-      contributing_source->setCaptureTimestamp(
-          web_source->CaptureTimestamp().value());
-    }
-    if (web_source->SenderCaptureTimeOffset().has_value()) {
-      contributing_source->setSenderCaptureTimeOffset(
-          web_source->SenderCaptureTimeOffset().value());
-    }
-    contributing_source->setRtpTimestamp(web_source->RtpTimestamp());
-    contributing_sources.push_back(contributing_source);
-  }
-  return contributing_sources;
+  return pc_->GetRtpContributingSourceCache().getContributingSources(
+      script_state, exception_state, this);
 }
 
 ScriptPromise RTCRtpReceiver::getStats(ScriptState* script_state) {
@@ -204,9 +130,9 @@
     ScriptState* script_state,
     ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (track_->kind() == "audio")
+  if (kind() == MediaKind::kAudio)
     return createEncodedAudioStreams(script_state, exception_state);
-  DCHECK_EQ(track_->kind(), "video");
+  DCHECK_EQ(kind(), MediaKind::kVideo);
   return createEncodedVideoStreams(script_state, exception_state);
 }
 
@@ -255,6 +181,14 @@
   return receiver_.get();
 }
 
+RTCRtpReceiver::MediaKind RTCRtpReceiver::kind() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (track_->kind() == "audio")
+    return MediaKind::kAudio;
+  DCHECK_EQ(track_->kind(), "video");
+  return MediaKind::kVideo;
+}
+
 MediaStreamVector RTCRtpReceiver::streams() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return streams_;
@@ -275,22 +209,6 @@
   transport_ = transport;
 }
 
-void RTCRtpReceiver::UpdateSourcesIfNeeded() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!web_sources_needs_updating_)
-    return;
-  web_sources_ = receiver_->GetSources();
-  // Clear the flag and schedule a microtask to reset it to true. This makes
-  // the cache valid until the next microtask checkpoint. As such, sources
-  // represent a snapshot and can be compared reliably in .js code, no risk of
-  // being updated due to an RTP packet arriving. E.g.
-  // "source.timestamp == source.timestamp" will always be true.
-  web_sources_needs_updating_ = false;
-  Microtask::EnqueueMicrotask(
-      WTF::Bind(&RTCRtpReceiver::SetContributingSourcesNeedsUpdating,
-                WrapWeakPersistent(this)));
-}
-
 void RTCRtpReceiver::ContextDestroyed() {
   {
     WTF::MutexLocker locker(audio_underlying_source_mutex_);
@@ -302,11 +220,6 @@
   }
 }
 
-void RTCRtpReceiver::SetContributingSourcesNeedsUpdating() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  web_sources_needs_updating_ = true;
-}
-
 void RTCRtpReceiver::Trace(Visitor* visitor) const {
   visitor->Trace(pc_);
   visitor->Trace(track_);
@@ -547,7 +460,7 @@
   DCHECK(!platform_receiver()
               ->GetEncodedVideoStreamTransformer()
               ->HasTransformerCallback());
-  DCHECK_EQ(track_->kind(), "video");
+  DCHECK_EQ(kind(), MediaKind::kVideo);
   platform_receiver()
       ->GetEncodedVideoStreamTransformer()
       ->SetTransformerCallback(
@@ -557,7 +470,7 @@
 
 void RTCRtpReceiver::UnregisterEncodedVideoStreamCallback() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(track_->kind(), "video");
+  DCHECK_EQ(kind(), MediaKind::kVideo);
   platform_receiver()
       ->GetEncodedVideoStreamTransformer()
       ->ResetTransformerCallback();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
index 966bf933..e179c6c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h"
+#include "third_party/webrtc/api/media_types.h"
 
 namespace blink {
 class RTCDtlsTransport;
@@ -39,6 +40,8 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  enum class MediaKind { kAudio, kVideo };
+
   // Takes ownership of the receiver.
   RTCRtpReceiver(RTCPeerConnection*,
                  std::unique_ptr<RTCRtpReceiverPlatform>,
@@ -71,11 +74,11 @@
                                                   ExceptionState&);
 
   RTCRtpReceiverPlatform* platform_receiver();
+  MediaKind kind() const;
   MediaStreamVector streams() const;
   void set_streams(MediaStreamVector streams);
   void set_transceiver(RTCRtpTransceiver*);
   void set_transport(RTCDtlsTransport*);
-  void UpdateSourcesIfNeeded();
 
   // ExecutionContextLifecycleObserver
   void ContextDestroyed() override;
@@ -83,7 +86,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  void SetContributingSourcesNeedsUpdating();
   void RegisterEncodedAudioStreamCallback();
   void UnregisterEncodedAudioStreamCallback();
   void InitializeEncodedAudioStreams(ScriptState*);
@@ -110,7 +112,6 @@
   // The current SSRCs and CSRCs. getSynchronizationSources() returns the SSRCs
   // and getContributingSources() returns the CSRCs.
   Vector<std::unique_ptr<RTCRtpSource>> web_sources_;
-  bool web_sources_needs_updating_ = true;
   Member<RTCRtpTransceiver> transceiver_;
 
   // Hint to the WebRTC Jitter Buffer about desired playout delay. Actual
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
index 12deae23..300c6bcb 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
@@ -180,8 +180,8 @@
   }
 
   Vector<std::unique_ptr<RTCRtpSource>> GetSources() {
-    // The webrtc_recever_ is a proxy, so this is a blocking call to the webrtc
-    // signalling thread.
+    // The `webrtc_recever_` is a PROXY and GetSources block-invokes to its
+    // secondary thread, which is the WebRTC worker thread.
     auto webrtc_sources = webrtc_receiver_->GetSources();
     Vector<std::unique_ptr<RTCRtpSource>> sources(
         static_cast<WTF::wtf_size_t>(webrtc_sources.size()));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.cc b/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.cc
new file mode 100644
index 0000000..f926f4c0
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.cc
@@ -0,0 +1,208 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.h"
+
+#include "base/check.h"
+#include "third_party/blink/renderer/core/loader/document_loader.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
+#include "third_party/blink/renderer/platform/bindings/microtask.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+namespace {
+
+HeapVector<Member<RTCRtpSynchronizationSource>>
+RTCRtpSynchronizationSourcesFromRTCRtpSources(
+    ScriptState* script_state,
+    const RtpContributingSourceCache::RTCRtpSources* rtp_sources) {
+  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+  DocumentLoadTiming& time_converter =
+      window->GetFrame()->Loader().GetDocumentLoader()->GetTiming();
+
+  HeapVector<Member<RTCRtpSynchronizationSource>> synchronization_sources;
+  for (const auto& rtp_source : *rtp_sources) {
+    if (rtp_source->SourceType() != RTCRtpSource::Type::kSSRC)
+      continue;
+    RTCRtpSynchronizationSource* synchronization_source =
+        MakeGarbageCollected<RTCRtpSynchronizationSource>();
+    synchronization_source->setTimestamp(
+        time_converter.MonotonicTimeToPseudoWallTime(rtp_source->Timestamp())
+            .InMilliseconds());
+    synchronization_source->setSource(rtp_source->Source());
+    if (rtp_source->AudioLevel().has_value()) {
+      synchronization_source->setAudioLevel(rtp_source->AudioLevel().value());
+    }
+    if (rtp_source->CaptureTimestamp().has_value()) {
+      synchronization_source->setCaptureTimestamp(
+          rtp_source->CaptureTimestamp().value());
+    }
+    if (rtp_source->SenderCaptureTimeOffset().has_value()) {
+      synchronization_source->setSenderCaptureTimeOffset(
+          rtp_source->SenderCaptureTimeOffset().value());
+    }
+    synchronization_source->setRtpTimestamp(rtp_source->RtpTimestamp());
+    synchronization_sources.push_back(synchronization_source);
+  }
+  return synchronization_sources;
+}
+
+HeapVector<Member<RTCRtpContributingSource>>
+RTCRtpContributingSourcesFromRTCRtpSources(
+    ScriptState* script_state,
+    const RtpContributingSourceCache::RTCRtpSources* rtp_sources) {
+  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+  DocumentLoadTiming& time_converter =
+      window->GetFrame()->Loader().GetDocumentLoader()->GetTiming();
+
+  HeapVector<Member<RTCRtpContributingSource>> contributing_sources;
+  for (const auto& rtp_source : *rtp_sources) {
+    if (rtp_source->SourceType() != RTCRtpSource::Type::kCSRC)
+      continue;
+    RTCRtpContributingSource* contributing_source =
+        MakeGarbageCollected<RTCRtpContributingSource>();
+    contributing_source->setTimestamp(
+        time_converter.MonotonicTimeToPseudoWallTime(rtp_source->Timestamp())
+            .InMilliseconds());
+    contributing_source->setSource(rtp_source->Source());
+    if (rtp_source->AudioLevel().has_value()) {
+      contributing_source->setAudioLevel(rtp_source->AudioLevel().value());
+    }
+    if (rtp_source->CaptureTimestamp().has_value()) {
+      contributing_source->setCaptureTimestamp(
+          rtp_source->CaptureTimestamp().value());
+    }
+    if (rtp_source->SenderCaptureTimeOffset().has_value()) {
+      contributing_source->setSenderCaptureTimeOffset(
+          rtp_source->SenderCaptureTimeOffset().value());
+    }
+    contributing_source->setRtpTimestamp(rtp_source->RtpTimestamp());
+    contributing_sources.push_back(contributing_source);
+  }
+  return contributing_sources;
+}
+
+}  // namespace
+
+RtpContributingSourceCache::RtpContributingSourceCache(
+    RTCPeerConnection* pc,
+    scoped_refptr<base::SingleThreadTaskRunner> worker_thread_runner)
+    : pc_(pc), worker_thread_runner_(worker_thread_runner) {
+  DCHECK(pc_);
+  DCHECK(worker_thread_runner_);
+}
+
+HeapVector<Member<RTCRtpSynchronizationSource>>
+RtpContributingSourceCache::getSynchronizationSources(
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    RTCRtpReceiver* receiver) {
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Window is detached");
+    return HeapVector<Member<RTCRtpSynchronizationSource>>();
+  }
+  MaybeUpdateRtpSources(receiver->kind());
+  return RTCRtpSynchronizationSourcesFromRTCRtpSources(script_state,
+                                                       GetRtpSources(receiver));
+}
+
+HeapVector<Member<RTCRtpContributingSource>>
+RtpContributingSourceCache::getContributingSources(
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    RTCRtpReceiver* receiver) {
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Window is detached");
+    return HeapVector<Member<RTCRtpContributingSource>>();
+  }
+  MaybeUpdateRtpSources(receiver->kind());
+  return RTCRtpContributingSourcesFromRTCRtpSources(script_state,
+                                                    GetRtpSources(receiver));
+}
+
+void RtpContributingSourceCache::MaybeUpdateRtpSources(
+    RTCRtpReceiver::MediaKind kind) {
+  bool* cache_is_obsolete;
+  HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>* cached_sources_by_receiver;
+  switch (kind) {
+    case RTCRtpReceiver::MediaKind::kAudio:
+      cache_is_obsolete = &audio_cache_is_obsolete_;
+      cached_sources_by_receiver = &cached_sources_by_audio_receiver_;
+      break;
+    case RTCRtpReceiver::MediaKind::kVideo:
+      cache_is_obsolete = &video_cache_is_obsolete_;
+      cached_sources_by_receiver = &cached_sources_by_video_receiver_;
+      break;
+  }
+  if (!*cache_is_obsolete || !pc_) {
+    return;
+  }
+
+  // Clear and refresh the cache for all RTCRtpReceiver objects in a single
+  // block-invoke to the WebRTC worker thread. This increases the overhead of a
+  // single getSynchronizationSources/getContributingSources call, but we expect
+  // an application that calls these methods on one RTCRtpReceiver to do it on
+  // every RTCRtpReceiver of the same kind. In that case, this heuristic brings
+  // down the number of block-invokes from 1 per receiver to 1 per kind.
+  cached_sources_by_receiver->clear();
+  Vector<RTCRtpReceiverPlatform*> receivers;
+  for (const Member<RTCRtpReceiver>& receiver : pc_->getReceivers()) {
+    if (receiver->kind() != kind)
+      continue;
+    receivers.push_back(receiver->platform_receiver());
+  }
+  base::WaitableEvent event;
+  // Unretained is safe because we're waiting for the operation to complete.
+  PostCrossThreadTask(
+      *worker_thread_runner_, FROM_HERE,
+      WTF::CrossThreadBindOnce(
+          &RtpContributingSourceCache::UpdateRtpSourcesOnWorkerThread,
+          WTF::CrossThreadUnretained(this),
+          WTF::CrossThreadUnretained(&receivers),
+          WTF::CrossThreadUnretained(cached_sources_by_receiver),
+          WTF::CrossThreadUnretained(&event)));
+  event.Wait();
+
+  *cache_is_obsolete = false;
+  Microtask::EnqueueMicrotask(
+      WTF::Bind(&RtpContributingSourceCache::MakeCacheObsolete,
+                weak_factory_.GetWeakPtr()));
+}
+
+void RtpContributingSourceCache::UpdateRtpSourcesOnWorkerThread(
+    Vector<RTCRtpReceiverPlatform*>* receivers,
+    HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>* cached_sources_by_receiver,
+    base::WaitableEvent* event) {
+  // Calling GetSources() while on the worker thread avoids a per-receiver
+  // block-invoke inside the webrtc::RtpReceiverInterface PROXY.
+  for (RTCRtpReceiverPlatform* receiver : *receivers) {
+    cached_sources_by_receiver->insert(receiver, receiver->GetSources());
+  }
+  event->Signal();
+}
+
+void RtpContributingSourceCache::MakeCacheObsolete() {
+  audio_cache_is_obsolete_ = true;
+  video_cache_is_obsolete_ = true;
+}
+
+const RtpContributingSourceCache::RTCRtpSources*
+RtpContributingSourceCache::GetRtpSources(RTCRtpReceiver* receiver) const {
+  const HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>*
+      cached_sources_by_receiver =
+          receiver->kind() == RTCRtpReceiver::MediaKind::kAudio
+              ? &cached_sources_by_audio_receiver_
+              : &cached_sources_by_video_receiver_;
+  auto it = cached_sources_by_receiver->find(receiver->platform_receiver());
+  if (it == cached_sources_by_receiver->end())
+    return nullptr;
+  return &it->value;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.h b/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.h
new file mode 100644
index 0000000..37deddd
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtp_contributing_source_cache.h
@@ -0,0 +1,84 @@
+// 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_RENDERER_MODULES_PEERCONNECTION_RTP_CONTRIBUTING_SOURCE_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTP_CONTRIBUTING_SOURCE_CACHE_H_
+
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_contributing_source.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_synchronization_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class RTCPeerConnection;
+
+// Implements RTCRtpReceiver.getSynchronizationSources/getContributingSources as
+// well as a cache of the result. The cache serves two purposes:
+// 1. According to spec, calling the getters multiple times inside the same task
+//    execution cycle must return the same values. The cache is cleared in the
+//    next microtask.
+// 2. Getting the SSRC/CSRC values involves a block-invoke to the WebRTC worker
+//    thread. This class updates the cache for all RTCRtpReceiver objects inside
+//    the same block-invoke, reducing the total number of block-invokes if the
+//    getters are called on every RTCRtpReceiver.
+class RtpContributingSourceCache {
+ public:
+  typedef Vector<std::unique_ptr<RTCRtpSource>> RTCRtpSources;
+
+  RtpContributingSourceCache(
+      RTCPeerConnection* pc,
+      scoped_refptr<base::SingleThreadTaskRunner> worker_thread_runner);
+
+  HeapVector<Member<RTCRtpSynchronizationSource>> getSynchronizationSources(
+      ScriptState* script_state,
+      ExceptionState& exception_state,
+      RTCRtpReceiver* receiver);
+  HeapVector<Member<RTCRtpContributingSource>> getContributingSources(
+      ScriptState* script_state,
+      ExceptionState& exception_state,
+      RTCRtpReceiver* receiver);
+
+ private:
+  void MaybeUpdateRtpSources(RTCRtpReceiver::MediaKind kind);
+  void UpdateRtpSourcesOnWorkerThread(
+      Vector<RTCRtpReceiverPlatform*>* receivers,
+      HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>*
+          cached_sources_by_receiver,
+      base::WaitableEvent* event);
+  void MakeCacheObsolete();
+  const RTCRtpSources* GetRtpSources(RTCRtpReceiver* receiver) const;
+
+  // Owner of all RTCRtpReceiver objects that this cache is concerned with.
+  const WeakPersistent<RTCPeerConnection> pc_;
+  const scoped_refptr<base::SingleThreadTaskRunner> worker_thread_runner_;
+  // We cache audio and video receivers separately in case the app is only
+  // interested in one of the kinds. Having a small fixed number of audio
+  // receivers where audio levels are polled and a large number of video
+  // receivers that are not being polled is a common setup.
+  HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>
+      cached_sources_by_audio_receiver_;
+  HashMap<RTCRtpReceiverPlatform*, RTCRtpSources>
+      cached_sources_by_video_receiver_;
+  bool audio_cache_is_obsolete_ = true;
+  bool video_cache_is_obsolete_ = true;
+
+  base::WeakPtrFactory<RtpContributingSourceCache> weak_factory_{this};
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTP_CONTRIBUTING_SOURCE_CACHE_H_
diff --git a/third_party/blink/renderer/modules/webgpu/DEPS b/third_party/blink/renderer/modules/webgpu/DEPS
index ded05aa6..c7774f83 100644
--- a/third_party/blink/renderer/modules/webgpu/DEPS
+++ b/third_party/blink/renderer/modules/webgpu/DEPS
@@ -10,6 +10,7 @@
     "+gpu/command_buffer/client/shared_image_interface.h",
     "+gpu/command_buffer/client/webgpu_interface.h",
     "+media/base/video_frame.h",
+    "+media/base/wait_and_replace_sync_token_client.h",
     "+media/renderers/paint_canvas_video_renderer.h",
     "+services/metrics/public/cpp/ukm_builders.h",
 ]
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index 5f56df54..911ad16a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -154,6 +154,9 @@
   if (adapter_properties_.depth32FloatStencil8) {
     features_->AddFeatureName("depth32float-stencil8");
   }
+  if (adapter_properties_.multiPlanarFormats) {
+    features_->AddFeatureName("multi-planar-formats");
+  }
 }
 
 ScriptPromise GPUAdapter::requestDevice(ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
index becc649..adbdf242f 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_external_texture.h"
 
 #include "media/base/video_frame.h"
+#include "media/base/wait_and_replace_sync_token_client.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_external_texture_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_view_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_htmlvideoelement_videoframe.h"
@@ -85,6 +86,49 @@
     return nullptr;
   }
 
+  // TODO(crbug.com/1306753): Use SharedImageProducer and CompositeSharedImage
+  // rather than check 'is_webgpu_compatible'.
+  if (media_video_frame->HasTextures() &&
+      (media_video_frame->format() == media::PIXEL_FORMAT_NV12) &&
+      media_video_frame->metadata().is_webgpu_compatible) {
+    scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
+        WebGPUMailboxTexture::FromVideoFrame(
+            device->GetDawnControlClient(), device->GetHandle(),
+            WGPUTextureUsage::WGPUTextureUsage_TextureBinding,
+            media_video_frame);
+
+    WGPUTextureViewDescriptor view_desc = {
+        .format = WGPUTextureFormat_R8Unorm,
+        .mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
+        .arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
+        .aspect = WGPUTextureAspect_Plane0Only};
+    WGPUTextureView plane0 = device->GetProcs().textureCreateView(
+        mailbox_texture->GetTexture(), &view_desc);
+    view_desc.format = WGPUTextureFormat_RG8Unorm;
+    view_desc.aspect = WGPUTextureAspect_Plane1Only;
+    WGPUTextureView plane1 = device->GetProcs().textureCreateView(
+        mailbox_texture->GetTexture(), &view_desc);
+
+    WGPUExternalTextureDescriptor external_texture_desc = {};
+    external_texture_desc.plane0 = plane0;
+    external_texture_desc.plane1 = plane1;
+    external_texture_desc.colorSpace = WGPUPredefinedColorSpace_Srgb;
+
+    GPUExternalTexture* external_texture =
+        MakeGarbageCollected<GPUExternalTexture>(
+            device,
+            device->GetProcs().deviceCreateExternalTexture(
+                device->GetHandle(), &external_texture_desc),
+            std::move(mailbox_texture));
+
+    // The texture view will be referenced during external texture creation, so
+    // by calling release here we ensure this texture view will be destructed
+    // when the external texture is destructed.
+    device->GetProcs().textureViewRelease(plane0);
+    device->GetProcs().textureViewRelease(plane1);
+
+    return external_texture;
+  }
   // If the context is lost, the resource provider would be invalid.
   auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
   if (!context_provider_wrapper ||
@@ -135,16 +179,16 @@
           WGPUTextureUsage::WGPUTextureUsage_TextureBinding,
           std::move(recyclable_canvas_resource));
 
-  WGPUTextureViewDescriptor viewDesc = {};
-  viewDesc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
-  viewDesc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
+  WGPUTextureViewDescriptor view_desc = {};
+  view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
+  view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
   WGPUTextureView plane0 = device->GetProcs().textureCreateView(
-      mailbox_texture->GetTexture(), &viewDesc);
+      mailbox_texture->GetTexture(), &view_desc);
 
   WGPUExternalTextureDescriptor dawn_desc = {};
   dawn_desc.plane0 = plane0;
 
-  GPUExternalTexture* externalTexture =
+  GPUExternalTexture* external_texture =
       MakeGarbageCollected<GPUExternalTexture>(
           device,
           device->GetProcs().deviceCreateExternalTexture(device->GetHandle(),
@@ -156,14 +200,14 @@
   // the external texture is destructed.
   device->GetProcs().textureViewRelease(plane0);
 
-  return externalTexture;
+  return external_texture;
 }
 
 GPUExternalTexture::GPUExternalTexture(
     GPUDevice* device,
-    WGPUExternalTexture externalTexture,
+    WGPUExternalTexture external_texture,
     scoped_refptr<WebGPUMailboxTexture> mailbox_texture)
-    : DawnObject<WGPUExternalTexture>(device, externalTexture),
+    : DawnObject<WGPUExternalTexture>(device, external_texture),
       mailbox_texture_(mailbox_texture) {}
 
 void GPUExternalTexture::Destroy() {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h
index f353f39..e559cd1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h
@@ -24,7 +24,7 @@
       ExceptionState& exception_state);
   explicit GPUExternalTexture(
       GPUDevice* device,
-      WGPUExternalTexture externalTexture,
+      WGPUExternalTexture external_texture,
       scoped_refptr<WebGPUMailboxTexture> mailbox_texture);
 
   GPUExternalTexture(const GPUExternalTexture&) = delete;
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.cc b/third_party/blink/renderer/platform/fonts/font_selector.cc
index 8f6c5047b..dedf794 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.cc
+++ b/third_party/blink/renderer/platform/fonts/font_selector.cc
@@ -32,7 +32,7 @@
       generic_family_name != font_family_names::kWebkitStandard)
     return g_empty_atom;
 
-  if (font_description.GenericFamily() == FontDescription::kWebkitBodyFamily) {
+  if (IsWebkitBodyFamily(font_description)) {
     // TODO(crbug.com/1065468): Remove this counter when it's no longer
     // necessary.
     UseCounter::Count(use_counter,
@@ -89,6 +89,11 @@
   return g_empty_atom;
 }
 
+// static
+bool FontSelector::IsWebkitBodyFamily(const FontDescription& font_description) {
+  return font_description.GenericFamily() == FontDescription::kWebkitBodyFamily;
+}
+
 void FontSelector::Trace(Visitor* visitor) const {
   visitor->Trace(font_fallback_map_);
   FontCacheClient::Trace(visitor);
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.h b/third_party/blink/renderer/platform/fonts/font_selector.h
index ac0ebee2..fc2d7c85 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.h
+++ b/third_party/blink/renderer/platform/fonts/font_selector.h
@@ -148,6 +148,8 @@
       const FontFamily& generic_family_name,
       UseCounter*);
 
+  static bool IsWebkitBodyFamily(const FontDescription& font_description);
+
  private:
   Member<FontFallbackMap> font_fallback_map_;
 };
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.cc b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
index 9814ade..7f92ef3 100644
--- a/third_party/blink/renderer/platform/fonts/simple_font_data.cc
+++ b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
@@ -55,19 +55,16 @@
 
 namespace blink {
 
-const float kSmallCapsFontSizeMultiplier = 0.7f;
-const float kEmphasisMarkFontSizeMultiplier = 0.5f;
+constexpr float kSmallCapsFontSizeMultiplier = 0.7f;
+constexpr float kEmphasisMarkFontSizeMultiplier = 0.5f;
 
 SimpleFontData::SimpleFontData(const FontPlatformData& platform_data,
                                scoped_refptr<CustomFontData> custom_data,
                                bool subpixel_ascent_descent,
                                const FontMetricsOverride& metrics_override)
-    : max_char_width_(-1),
-      avg_char_width_(-1),
-      platform_data_(platform_data),
-      custom_font_data_(std::move(custom_data)),
-      visual_overflow_inflation_for_ascent_(0),
-      visual_overflow_inflation_for_descent_(0) {
+    : platform_data_(platform_data),
+      font_(platform_data_.size() ? platform_data.CreateSkFont() : SkFont()),
+      custom_font_data_(std::move(custom_data)) {
   PlatformInit(subpixel_ascent_descent, metrics_override);
   PlatformGlyphInit();
 }
@@ -83,7 +80,6 @@
 
   SkFontMetrics metrics;
 
-  font_ = platform_data_.CreateSkFont();
   font_.getMetrics(&metrics);
 
   float ascent;
@@ -227,9 +223,10 @@
     const FontDescription& font_description) const {
   if (!derived_font_data_)
     derived_font_data_ = std::make_unique<DerivedFontData>();
-  if (!derived_font_data_->small_caps)
+  if (!derived_font_data_->small_caps) {
     derived_font_data_->small_caps =
         CreateScaledFontData(font_description, kSmallCapsFontSizeMultiplier);
+  }
 
   return derived_font_data_->small_caps;
 }
@@ -238,9 +235,10 @@
     const FontDescription& font_description) const {
   if (!derived_font_data_)
     derived_font_data_ = std::make_unique<DerivedFontData>();
-  if (!derived_font_data_->emphasis_mark)
+  if (!derived_font_data_->emphasis_mark) {
     derived_font_data_->emphasis_mark =
         CreateScaledFontData(font_description, kEmphasisMarkFontSizeMultiplier);
+  }
 
   return derived_font_data_->emphasis_mark;
 }
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.h b/third_party/blink/renderer/platform/fonts/simple_font_data.h
index 88406857..6cd49fc0 100644
--- a/third_party/blink/renderer/platform/fonts/simple_font_data.h
+++ b/third_party/blink/renderer/platform/fonts/simple_font_data.h
@@ -69,7 +69,7 @@
 
 class FontDescription;
 
-class PLATFORM_EXPORT SimpleFontData : public FontData {
+class PLATFORM_EXPORT SimpleFontData final : public FontData {
  public:
   // Used to create platform fonts.
   static scoped_refptr<SimpleFontData> Create(
@@ -80,10 +80,16 @@
         platform_data, std::move(custom_data), subpixel_ascent_descent));
   }
 
+  SimpleFontData(const SimpleFontData&) = delete;
+  SimpleFontData(SimpleFontData&&) = delete;
+  SimpleFontData& operator=(const SimpleFontData&) = delete;
+  SimpleFontData& operator=(const SimpleFontData&&) = delete;
+
   const FontPlatformData& PlatformData() const { return platform_data_; }
 
   scoped_refptr<SimpleFontData> SmallCapsFontData(const FontDescription&) const;
-  scoped_refptr<SimpleFontData> EmphasisMarkFontData(const FontDescription&) const;
+  scoped_refptr<SimpleFontData> EmphasisMarkFontData(
+      const FontDescription&) const;
   scoped_refptr<SimpleFontData> MetricsOverriddenFontData(
       const FontMetricsOverride&) const;
 
@@ -160,14 +166,13 @@
     return visual_overflow_inflation_for_descent_;
   }
 
- protected:
+ private:
   SimpleFontData(
       const FontPlatformData&,
       scoped_refptr<CustomFontData> custom_data,
       bool subpixel_ascent_descent = false,
       const FontMetricsOverride& metrics_override = FontMetricsOverride());
 
- private:
   void PlatformInit(bool subpixel_ascent_descent, const FontMetricsOverride&);
   void PlatformGlyphInit();
 
@@ -178,23 +183,25 @@
   bool TrySetNormalizedTypoAscentAndDescent(float ascent, float descent) const;
 
   FontMetrics font_metrics_;
-  float max_char_width_;
-  float avg_char_width_;
+  float max_char_width_ = -1;
+  float avg_char_width_ = -1;
 
-  FontPlatformData platform_data_;
-  SkFont font_;
+  const FontPlatformData platform_data_;
+  const SkFont font_;
 
-  Glyph space_glyph_;
-  float space_width_;
-  Glyph zero_glyph_;
+  Glyph space_glyph_ = 0;
+  float space_width_ = 0;
+  Glyph zero_glyph_ = 0;
 
-  struct DerivedFontData {
+  struct DerivedFontData final {
     USING_FAST_MALLOC(DerivedFontData);
 
    public:
     DerivedFontData() = default;
     DerivedFontData(const DerivedFontData&) = delete;
+    DerivedFontData(DerivedFontData&&) = delete;
     DerivedFontData& operator=(const DerivedFontData&) = delete;
+    DerivedFontData& operator=(DerivedFontData&&) = delete;
 
     scoped_refptr<SimpleFontData> small_caps;
     scoped_refptr<SimpleFontData> emphasis_mark;
@@ -202,13 +209,13 @@
 
   mutable std::unique_ptr<DerivedFontData> derived_font_data_;
 
-  scoped_refptr<CustomFontData> custom_font_data_;
+  const scoped_refptr<CustomFontData> custom_font_data_;
 
   // These are set to non-zero when ascent or descent is rounded or shifted
   // to be smaller than the actual ascent or descent. When calculating visual
   // overflows, we should add the inflations.
-  unsigned visual_overflow_inflation_for_ascent_;
-  unsigned visual_overflow_inflation_for_descent_;
+  unsigned visual_overflow_inflation_for_ascent_ = 0;
+  unsigned visual_overflow_inflation_for_descent_ = 0;
 
   mutable FontHeight normalized_typo_ascent_descent_;
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
index 80405c5..dba1499 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h"
 
 #include "gpu/command_buffer/client/webgpu_interface.h"
+#include "media/base/video_frame.h"
+#include "media/base/wait_and_replace_sync_token_client.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
@@ -101,6 +103,32 @@
       nullptr));
 }
 
+//  static
+scoped_refptr<WebGPUMailboxTexture> WebGPUMailboxTexture::FromVideoFrame(
+    scoped_refptr<DawnControlClientHolder> dawn_control_client,
+    WGPUDevice device,
+    WGPUTextureUsage usage,
+    scoped_refptr<media::VideoFrame> video_frame) {
+  auto finished_access_callback = base::BindOnce(
+      [](base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider,
+         media::VideoFrame* frame, const gpu::SyncToken& sync_token) {
+        if (context_provider) {
+          // Update the sync token before unreferencing the video frame.
+          media::WaitAndReplaceSyncTokenClient client(
+              context_provider->ContextProvider()->WebGPUInterface());
+          frame->UpdateReleaseSyncToken(&client);
+        }
+      },
+      dawn_control_client->GetContextProviderWeakPtr(),
+      base::RetainedRef(video_frame));
+  return base::AdoptRef(new WebGPUMailboxTexture(
+      std::move(dawn_control_client), device, WGPUTextureUsage_TextureBinding,
+      video_frame->mailbox_holder(0).mailbox,
+      video_frame->mailbox_holder(0).sync_token,
+      gpu::webgpu::WEBGPU_MAILBOX_NONE, std::move(finished_access_callback),
+      nullptr));
+}
+
 WebGPUMailboxTexture::WebGPUMailboxTexture(
     scoped_refptr<DawnControlClientHolder> dawn_control_client,
     WGPUDevice device,
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
index 522b98c..a84bdf33 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
@@ -17,6 +17,10 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 
+namespace media {
+class VideoFrame;
+}  // namespace media
+
 namespace blink {
 
 class DawnControlClientHolder;
@@ -47,6 +51,12 @@
       gpu::webgpu::MailboxFlags mailbox_flags =
           gpu::webgpu::WEBGPU_MAILBOX_NONE);
 
+  static scoped_refptr<WebGPUMailboxTexture> FromVideoFrame(
+      scoped_refptr<DawnControlClientHolder> dawn_control_client,
+      WGPUDevice device,
+      WGPUTextureUsage usage,
+      scoped_refptr<media::VideoFrame> video_frame);
+
   ~WebGPUMailboxTexture();
 
   WGPUTexture GetTexture() { return texture_; }
diff --git a/third_party/blink/renderer/platform/p2p/empty_network_manager.cc b/third_party/blink/renderer/platform/p2p/empty_network_manager.cc
index b49d3cd..edc86a7 100644
--- a/third_party/blink/renderer/platform/p2p/empty_network_manager.cc
+++ b/third_party/blink/renderer/platform/p2p/empty_network_manager.cc
@@ -58,6 +58,11 @@
   networks->clear();
 }
 
+std::vector<const rtc::Network*> EmptyNetworkManager::GetNetworks() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return {};
+}
+
 bool EmptyNetworkManager::GetDefaultLocalAddress(
     int family,
     rtc::IPAddress* ipaddress) const {
diff --git a/third_party/blink/renderer/platform/p2p/empty_network_manager.h b/third_party/blink/renderer/platform/p2p/empty_network_manager.h
index 9378ef1..03a1e6d 100644
--- a/third_party/blink/renderer/platform/p2p/empty_network_manager.h
+++ b/third_party/blink/renderer/platform/p2p/empty_network_manager.h
@@ -38,6 +38,7 @@
   void StartUpdating() override;
   void StopUpdating() override;
   void GetNetworks(NetworkList* networks) const override;
+  std::vector<const rtc::Network*> GetNetworks() const override;
   bool GetDefaultLocalAddress(int family,
                               rtc::IPAddress* ipaddress) const override;
 
diff --git a/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc b/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
index 728eaee6..3bdf19062 100644
--- a/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
+++ b/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
@@ -97,13 +97,24 @@
 }
 
 void FilteringNetworkManager::GetNetworks(NetworkList* networks) const {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   networks->clear();
+  for (const rtc::Network* network : GetNetworks()) {
+    networks->push_back(const_cast<rtc::Network*>(network));
+  }
+}
 
-  if (enumeration_permission() == ENUMERATION_ALLOWED)
-    NetworkManagerBase::GetNetworks(networks);
+std::vector<const rtc::Network*> FilteringNetworkManager::GetNetworks() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  std::vector<const rtc::Network*> networks;
 
-  VLOG(3) << "GetNetworks() returns " << networks->size() << " networks.";
+  if (enumeration_permission() == ENUMERATION_ALLOWED) {
+    for (const rtc::Network* network : GetNetworksInternal()) {
+      networks.push_back(const_cast<rtc::Network*>(network));
+    }
+  }
+
+  VLOG(3) << "GetNetworks() returns " << networks.size() << " networks.";
+  return networks;
 }
 
 webrtc::MdnsResponderInterface* FilteringNetworkManager::GetMdnsResponder()
@@ -175,11 +186,11 @@
 
   // Copy and merge the networks. Fire a signal if the permission status is
   // known.
-  NetworkList networks;
-  network_manager_for_signaling_thread_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks =
+      network_manager_for_signaling_thread_->GetNetworks();
   NetworkList copied_networks;
   copied_networks.reserve(networks.size());
-  for (rtc::Network* network : networks) {
+  for (const rtc::Network* network : networks) {
     auto copied_network = std::make_unique<rtc::Network>(*network);
     copied_network->set_default_local_address_provider(this);
     copied_network->set_mdns_responder_provider(this);
diff --git a/third_party/blink/renderer/platform/p2p/filtering_network_manager.h b/third_party/blink/renderer/platform/p2p/filtering_network_manager.h
index 4e7d1a46..591b4664 100644
--- a/third_party/blink/renderer/platform/p2p/filtering_network_manager.h
+++ b/third_party/blink/renderer/platform/p2p/filtering_network_manager.h
@@ -54,6 +54,7 @@
   void StartUpdating() override;
   void StopUpdating() override;
   void GetNetworks(NetworkList* networks) const override;
+  std::vector<const rtc::Network*> GetNetworks() const override;
 
   webrtc::MdnsResponderInterface* GetMdnsResponder() const override;
 
diff --git a/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc b/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
index 50fb6e8..6b459e0 100644
--- a/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
+++ b/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
@@ -82,6 +82,9 @@
   void GetNetworks(NetworkList* networks) const override {
     networks->push_back(network_.get());
   }
+  std::vector<const rtc::Network*> GetNetworks() const override {
+    return {network_.get()};
+  }
 
   void SendNetworksChanged() {
     sent_first_update_ = true;
@@ -241,9 +244,8 @@
   }
 
  protected:
-  const NetworkList& GetP2PNetworkList() {
-    network_list_.clear();
-    network_manager_->GetNetworks(&network_list_);
+  const std::vector<const rtc::Network*>& GetP2PNetworkList() {
+    network_list_ = network_manager_->GetNetworks();
     return network_list_;
   }
 
@@ -261,7 +263,7 @@
   std::vector<rtc::Network> networks_;
   int next_new_network_id_ = 0;
 
-  NetworkList network_list_;
+  std::vector<const rtc::Network*> network_list_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
 };
@@ -460,15 +462,13 @@
   };
   RunTests(setup_steps, std::size(setup_steps));
 
-  NetworkList networks;
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
   EXPECT_THAT(networks, SizeIs(1u));
   for (const rtc::Network* network : networks) {
     EXPECT_EQ(nullptr, network->GetMdnsResponder());
   }
 
-  networks.clear();
-  network_manager_->GetAnyAddressNetworks(&networks);
+  networks = network_manager_->GetAnyAddressNetworks();
   EXPECT_THAT(networks, SizeIs(2u));
   for (const rtc::Network* network : networks) {
     EXPECT_EQ(nullptr, network->GetMdnsResponder());
@@ -485,11 +485,10 @@
   EXPECT_EQ(rtc::NetworkManager::ENUMERATION_BLOCKED,
             network_manager_->enumeration_permission());
 
-  NetworkList networks;
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
   EXPECT_TRUE(networks.empty());
 
-  network_manager_->GetAnyAddressNetworks(&networks);
+  networks = network_manager_->GetAnyAddressNetworks();
   EXPECT_THAT(networks, SizeIs(2u));
   EXPECT_NE(nullptr, network_manager_->GetMdnsResponder());
   for (const rtc::Network* network : networks) {
@@ -509,11 +508,10 @@
   EXPECT_EQ(rtc::NetworkManager::ENUMERATION_BLOCKED,
             network_manager_->enumeration_permission());
 
-  NetworkList networks;
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
   EXPECT_TRUE(networks.empty());
 
-  network_manager_->GetAnyAddressNetworks(&networks);
+  networks = network_manager_->GetAnyAddressNetworks();
   EXPECT_THAT(networks, SizeIs(2u));
   for (const rtc::Network* network : networks) {
     EXPECT_EQ(nullptr, network->GetMdnsResponder());
diff --git a/third_party/blink/renderer/platform/p2p/ipc_network_manager_test.cc b/third_party/blink/renderer/platform/p2p/ipc_network_manager_test.cc
index 33e540e..745f36d 100644
--- a/third_party/blink/renderer/platform/p2p/ipc_network_manager_test.cc
+++ b/third_party/blink/renderer/platform/p2p/ipc_network_manager_test.cc
@@ -75,7 +75,6 @@
 TEST_F(IpcNetworkManagerTest, TestMergeNetworkList) {
   net::NetworkInterfaceList list;
   net::IPAddress ip;
-  std::vector<rtc::Network*> networks;
   rtc::IPAddress ip_address;
 
   // Add 2 networks with the same prefix and prefix length.
@@ -91,13 +90,12 @@
 
   network_manager_->OnNetworkListChanged(list, net::IPAddress(),
                                          net::IPAddress());
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
   EXPECT_EQ(1uL, networks.size());
   EXPECT_EQ(2uL, networks[0]->GetIPs().size());
 
   // Add another network with different prefix length, should result in
   // a different network.
-  networks.clear();
   list.push_back(net::NetworkInterface(
       "em1", "em1", 0, net::NetworkChangeNotifier::CONNECTION_UNKNOWN, ip, 48,
       net::IP_ADDRESS_ATTRIBUTE_NONE));
@@ -109,14 +107,15 @@
   // The unknown default address should be ignored.
   EXPECT_FALSE(network_manager_->GetDefaultLocalAddress(AF_INET6, &ip_address));
 
-  network_manager_->GetNetworks(&networks);
+  networks = network_manager_->GetNetworks();
 
   // Verify we have 2 networks now.
   EXPECT_EQ(2uL, networks.size());
   // Verify the network with prefix length of 64 has 2 IP addresses.
   auto network_with_two_ips = std::find_if(
-      networks.begin(), networks.end(),
-      [](rtc::Network* network) { return network->prefix_length() == 64; });
+      networks.begin(), networks.end(), [](const rtc::Network* network) {
+        return network->prefix_length() == 64;
+      });
   ASSERT_NE(networks.end(), network_with_two_ips);
   EXPECT_EQ(2uL, (*network_with_two_ips)->GetIPs().size());
   // IPs should be in the same order as the list passed into
@@ -129,8 +128,9 @@
             rtc::InterfaceAddress(ip_address));
   // Verify the network with prefix length of 48 has 1 IP address.
   auto network_with_one_ip = std::find_if(
-      networks.begin(), networks.end(),
-      [](rtc::Network* network) { return network->prefix_length() == 48; });
+      networks.begin(), networks.end(), [](const rtc::Network* network) {
+        return network->prefix_length() == 48;
+      });
   ASSERT_NE(networks.end(), network_with_one_ip);
   EXPECT_EQ(1uL, (*network_with_one_ip)->GetIPs().size());
   EXPECT_TRUE(rtc::IPFromString(kIPv6PublicAddrString2, &ip_address));
@@ -143,7 +143,6 @@
 TEST_F(IpcNetworkManagerTest, DeterminesNetworkTypeFromNameIfUnknown) {
   net::NetworkInterfaceList list;
   net::IPAddress ip;
-  std::vector<rtc::Network*> networks;
   rtc::IPAddress ip_address;
 
   // Add a "tun1" entry of type "unknown" and "tun2" entry of type Wi-Fi. The
@@ -161,16 +160,16 @@
 
   network_manager_->OnNetworkListChanged(list, net::IPAddress(),
                                          net::IPAddress());
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
   EXPECT_EQ(2uL, networks.size());
 
   auto tun1 = std::find_if(
       networks.begin(), networks.end(),
-      [](rtc::Network* network) { return network->name() == "tun1"; });
+      [](const rtc::Network* network) { return network->name() == "tun1"; });
   ASSERT_NE(networks.end(), tun1);
   auto tun2 = std::find_if(
       networks.begin(), networks.end(),
-      [](rtc::Network* network) { return network->name() == "tun2"; });
+      [](const rtc::Network* network) { return network->name() == "tun2"; });
   ASSERT_NE(networks.end(), tun1);
 
   EXPECT_EQ(rtc::ADAPTER_TYPE_VPN, (*tun1)->type());
@@ -191,15 +190,13 @@
 
   network_manager_->OnNetworkListChanged(list, net::IPAddress(),
                                          net::IPAddress());
-  std::vector<rtc::Network*> networks;
-  network_manager_->GetNetworks(&networks);
+  std::vector<const rtc::Network*> networks = network_manager_->GetNetworks();
 
   ASSERT_EQ(1u, networks.size());
   webrtc::MdnsResponderInterface* const mdns_responder =
       network_manager_->GetMdnsResponder();
   EXPECT_EQ(mdns_responder, networks[0]->GetMdnsResponder());
-  networks.clear();
-  network_manager_->GetAnyAddressNetworks(&networks);
+  networks = network_manager_->GetAnyAddressNetworks();
   ASSERT_EQ(2u, networks.size());
   EXPECT_EQ(mdns_responder, networks[0]->GetMdnsResponder());
   EXPECT_EQ(mdns_responder, networks[1]->GetMdnsResponder());
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
index 42214f08..1398889 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
@@ -42,6 +42,8 @@
   virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0;
   virtual MediaStreamComponent* Track() const = 0;
   virtual Vector<String> StreamIds() const = 0;
+  // If called from any other thread than the WebRTC worker thread, this causes
+  // a block-invoke by the PROXY.
   virtual Vector<std::unique_ptr<RTCRtpSource>> GetSources() = 0;
   virtual void GetStats(RTCStatsReportCallback,
                         const Vector<webrtc::NonStandardGroupId>&) = 0;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8f97754..f2fbc6e2 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -290,11 +290,6 @@
       status: "stable",
     },
     {
-      name: "BiddingAndScoringDebugReportingAPI",
-      origin_trial_feature_name: "BiddingAndScoringDebugReportingAPI",
-      implied_by: ["Fledge", "Parakeet"],
-    },
-    {
       name: "BidiCaretAffinity",
     },
     {
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
index 0bde4af..3b0021d 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
@@ -62,8 +62,6 @@
     # pylint: disable=import-error
     # pylint: disable=invalid-name
     # pylint: disable=redefined-outer-name
-    global aemu_target
-    import aemu_target
     global ConnectPortForwardingTask
     from common import ConnectPortForwardingTask
     global _GetPathToBuiltinTarget, _LoadTargetClass, InitializeTargetArgs
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
index 563005526..57ed1c1 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
@@ -152,10 +152,10 @@
                              default=True,
                              help=('Do not log Zircon debug messages.')),
         optparse.make_option('--device',
-                             choices=['aemu', 'qemu', 'device', 'fvdl'],
+                             choices=['qemu', 'device', 'fvdl'],
                              default='fvdl',
                              help=('Choose device to launch Fuchsia with. '
-                                   'Defaults to AEMU.')),
+                                   'Defaults to fvdl.')),
         optparse.make_option('--fuchsia-target-cpu',
                              choices=['x64', 'arm64'],
                              default='x64',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a60fa35b..29fcfb7 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7730,3 +7730,7 @@
 crbug.com/1313396 [ Linux ] external/wpt/dom/events/Event-dispatch-on-disabled-elements.html [ Failure Pass ]
 crbug.com/1194945 external/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html [ Failure Pass ]
 crbug.com/1244896 fast/mediacapturefromelement/CanvasCaptureMediaStream-set-size-too-large.html [ Failure Pass Timeout ]
+
+# Sheriff 2022-04-06
+crbug.com/1290670 [ Mac10.14 ] external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https.html [ Failure Pass ]
+crbug.com/1313894 [ Mac10.14 ] rootscroller/root-scroller-paint-order.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-000.html b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-000.html
new file mode 100644
index 0000000..8d1d395
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-000.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1312790">
+<div style="columns:2;">
+  <span style="filter:blur(1px);">
+    <div style="position:relative;">
+      <div style="position:absolute;">
+        <div style="position:fixed;">
+          <div style="position:fixed;"></div>
+        </div>
+      </div>
+    </div>
+  </span>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-001.html
new file mode 100644
index 0000000..dc4e8fb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-001.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1312790">
+<div style="columns:2;">
+  <div style="columns:2;">
+    <span style="filter:blur(1px);">
+      <div style="position:relative;">
+        <div style="position:absolute;">
+          <div style="position:fixed;"></div>
+        </div>
+      </div>
+    </span>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-002.html b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-002.html
new file mode 100644
index 0000000..15a5a96
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/nested-fixedpos-in-inline-crash-002.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1312790">
+<div style="columns:2;">
+  <span style="filter:blur(1px);">
+    <div style="columns:2;">
+      <div style="position:relative;">
+        <div style="position:absolute;">
+          <div style="position:fixed;">
+            <div style="position:fixed;"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </span>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/relpos-inline-with-abspos-multicol-gets-block-child.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/relpos-inline-with-abspos-multicol-gets-block-child.html
new file mode 100644
index 0000000..19a9bdd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/relpos-inline-with-abspos-multicol-gets-block-child.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1308071">
+<div style="columns:2;">
+  <span style="position:relative;">
+    <div style="position:absolute;"></div>
+  </span>
+  <div id="surprise" style="display:none;"></div>
+  <span></span>
+</div>
+<script>
+  document.body.offsetTop;
+  surprise.style.display = "block";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/app-can-make-payment.js b/third_party/blink/web_tests/external/wpt/payment-handler/app-can-make-payment.js
index 0bb9490..14fea9c 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/app-can-make-payment.js
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/app-can-make-payment.js
@@ -53,13 +53,6 @@
   }
 
   const [methodName] = method.supportedMethods;
-  if (methodName === 'basic-card') {
-    const msg =
-      '"basic-card" payment method must never be checked in CanMakePaymentEvent';
-    event.respondWith(Promise.reject(new Error(msg)));
-    return;
-  }
-
   const [modifierMethodName] = modifier.supportedMethods;
   if (modifierMethodName !== methodName) {
     const msg = `Unexpected modifier method name: "${modifierMethodName}". Expected "${methodName}".`;
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/basic-card.js b/third_party/blink/web_tests/external/wpt/payment-handler/app-simple.js
similarity index 77%
rename from third_party/blink/web_tests/external/wpt/payment-handler/basic-card.js
rename to third_party/blink/web_tests/external/wpt/payment-handler/app-simple.js
index 2db5d4b7..833a01f47 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/basic-card.js
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/app-simple.js
@@ -17,7 +17,7 @@
   }
 
   const methodData = event.methodData[0];
-  const expectedMethodName = 'basic-card';
+  const expectedMethodName = window.location.origin + '/payment-handler/payment-app/';
   if (methodData.supportedMethods !== expectedMethodName) {
     const msg = `Expected payment method name "${expectedMethodName}", but got "${
       methodData.supportedMethods
@@ -69,25 +69,6 @@
   }
 
   event.respondWith({
-    methodName: 'basic-card',
-    details: {
-      billingAddress: {
-        addressLine: ['1875 Explorer St #1000'],
-        city: 'Reston',
-        country: 'US',
-        dependentLocality: '',
-        organization: 'Google',
-        phone: '+15555555555',
-        postalCode: '20190',
-        recipient: 'Jon Doe',
-        region: 'VA',
-        sortingCode: '',
-      },
-      cardNumber: '4111111111111111',
-      cardSecurityCode: '123',
-      cardholderName: 'Jon Doe',
-      expiryMonth: '12',
-      expiryYear: '2028',
-    },
+    methodName: expectedMethodName,
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/basic-card.json b/third_party/blink/web_tests/external/wpt/payment-handler/basic-card.json
deleted file mode 100644
index 002dd875..0000000
--- a/third_party/blink/web_tests/external/wpt/payment-handler/basic-card.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": "Test Basic Card Payment Handler",
-  "icons": [
-    {
-      "src": "/images/rgrg-256x256.png",
-      "sizes": "256x256",
-      "type": "image/png"
-    }
-  ]
-}
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt
index 37e87e9..032a53a 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https-expected.txt
@@ -5,9 +5,5 @@
 FAIL If CanMakePaymentEvent.respondWith(true) is called, then the payment method is supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
 FAIL If CanMakePaymentEvent.respondWith(Promise.resolve(true)) is called, then the payment method is supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
 FAIL If CanMakePaymentEvent.respondWith(Promise.reject(error)) is called, then the payment method is not supported. promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
-FAIL If an app supports "basic-card" in general and that's what merchant requests as well, then capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
-FAIL If an app has less specific "basic-card" capabilites than merchant's request, capability filtering should not make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".  promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
-FAIL If an app has the exact "basic-card" capabilities that a merchant requested, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
-FAIL If an app has more specific "basic-card" capabilities than merchant's request, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". promise_test: Unhandled rejection with value: object "NotAllowedError: Not allowed to install this payment handler"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html
index c4e5453..b2016a05 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html
@@ -5,8 +5,6 @@
 <link rel="manifest" href="manifest.json">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<p>The "basic-card" test requires that you don't have a prepaid MIR card stored
-in the browser. If you do, please remove it for the duration of the test.</p>
 <script>
 const instrumentKey = 'instrument-key';
 
@@ -239,141 +237,4 @@
   );
   await promise_rejects_dom(t, 'NotSupportedError', request.show());
 }, 'If CanMakePaymentEvent.respondWith(Promise.reject(error)) is called, then the payment method is not supported.');
-
-promise_test(async t => {
-  const methodName = 'basic-card';
-  await registerApp(methodName);
-  const request = buildPaymentRequest(methodName);
-  assert_not_equals(request, undefined);
-  let paymentRequestCanMakePaymentResult;
-  try {
-    paymentRequestCanMakePaymentResult = await request.canMakePayment();
-  } catch (err) {
-    assert_equals(
-      err.name,
-      'NotAllowedError',
-      'If it throws, then it must be NotAllowedError',
-    );
-  }
-  assert_true(
-    paymentRequestCanMakePaymentResult,
-    'canMakePayment() must return true due to capability matching in the browser.',
-  );
-}, 'If an app supports "basic-card" in general and that\'s what merchant requests as well, then capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
-
-promise_test(async t => {
-  const methodName = 'basic-card';
-  await registerApp(methodName);
-  const cardNetwork = 'mir';
-  const request = new PaymentRequest(
-    [
-      {
-        supportedMethods: methodName,
-        data: {
-          supportedNetworks: [cardNetwork],
-        },
-      },
-    ],
-    {
-      total: {
-        label: 'Total',
-        amount: {
-          currency: 'USD',
-          value: '0',
-        },
-      },
-    },
-  );
-  assert_not_equals(request, undefined);
-  let paymentRequestCanMakePaymentResult;
-  try {
-    paymentRequestCanMakePaymentResult = await request.canMakePayment();
-  } catch (err) {
-    assert_equals(
-      err.name,
-      'NotAllowedError',
-      'If it throws, then it must be NotAllowedError',
-    );
-  }
-  assert_false(
-    paymentRequestCanMakePaymentResult,
-    'canMakePayment() must return false due to capability matching in the browser.',
-  );
-}, 'If an app has less specific "basic-card" capabilites than merchant\'s request, capability filtering should not make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". ');
-
-promise_test(async t => {
-  const methodName = 'basic-card';
-  const cardNetwork = 'mir';
-  const registration = await registerApp(methodName);
-  await registration.paymentManager.instruments.set(instrumentKey, {
-    name: 'Test Payment Method',
-    method: methodName,
-    capabilities: {
-      supportedNetworks: [cardNetwork],
-    },
-  });
-  const request = new PaymentRequest(
-    [
-      {
-        supportedMethods: methodName,
-        data: {
-          supportedNetworks: [cardNetwork],
-        },
-      },
-    ],
-    {
-      total: {
-        label: 'Total',
-        amount: {
-          currency: 'USD',
-          value: '0',
-        },
-      },
-    },
-  );
-  assert_not_equals(request, undefined);
-  let paymentRequestCanMakePaymentResult;
-  try {
-    paymentRequestCanMakePaymentResult = await request.canMakePayment();
-  } catch (err) {
-    assert_equals(
-      err.name,
-      'NotAllowedError',
-      'If it throws, then it must be NotAllowedError',
-    );
-  }
-  assert_true(
-    paymentRequestCanMakePaymentResult,
-    'canMakePayment() must return true due to capability matching in the browser.',
-  );
-}, 'If an app has the exact "basic-card" capabilities that a merchant requested, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
-
-promise_test(async t => {
-  const methodName = 'basic-card';
-  const cardNetwork = 'mir';
-  const registration = await registerApp(methodName);
-  await registration.paymentManager.instruments.set(instrumentKey, {
-    name: 'Test Payment Method',
-    method: methodName,
-    capabilities: {
-      supportedNetworks: [cardNetwork],
-    },
-  });
-  const request = buildPaymentRequest(methodName);
-  assert_not_equals(request, undefined);
-  let paymentRequestCanMakePaymentResult;
-  try {
-    paymentRequestCanMakePaymentResult = await request.canMakePayment();
-  } catch (err) {
-    assert_equals(
-      err.name,
-      'NotAllowedError',
-      'If it throws, then it must be NotAllowedError',
-    );
-  }
-  assert_true(
-    paymentRequestCanMakePaymentResult,
-    'canMakePayment() must return true due to capability matching in the browser.',
-  );
-}, 'If an app has more specific "basic-card" capabilities than merchant\'s request, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html.ini b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html.ini
index 24fa9255..dcbbfd60 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html.ini
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html.ini
@@ -2,9 +2,6 @@
   [If CanMakePaymentEvent.respondWith(Promise.resolve(true)) is called, then the payment method is supported.]
     expected: FAIL
 
-  [If an app supports "basic-card" in general and that's what merchant requests as well, then capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".]
-    expected: FAIL
-
   [If CanMakePaymentEvent.respondWith(false) is called, then the payment method is not supported.]
     expected: FAIL
 
@@ -14,18 +11,8 @@
   [If CanMakePaymentEvent.respondWith(true) is called, then the payment method is supported.]
     expected: FAIL
 
-  [If an app has the exact "basic-card" capabilities that a merchant requested, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".]
-    expected: FAIL
-
   [If a payment handler is not installed, then the payment method is not supported.]
     expected: FAIL
 
   [If CanMakePaymentEvent.respondWith(Promise.resolve(false)) is called, then the payment method is not supported.]
     expected: FAIL
-
-  [If an app has less specific "basic-card" capabilites than merchant's request, capability filtering should not make the app available for use. CanMakePaymentEvent should not be fired for "basic-card". ]
-    expected: FAIL
-
-  [If an app has more specific "basic-card" capabilities than merchant's request, capability filtering should make the app available for use. CanMakePaymentEvent should not be fired for "basic-card".]
-    expected: FAIL
-
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html
index abdb3d7..1640420 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html
@@ -5,7 +5,7 @@
   rel="help"
   href="https://w3c.github.io/payment-handler/#changepaymentmethod-method"
 />
-<link rel="manifest" href="/payment-handler/basic-card.json" />
+<link rel="manifest" href="/payment-handler/manifest.json" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/change-shipping-option-manual.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/change-shipping-option-manual.https.html
index 00d1aee..2511fc5e 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/change-shipping-option-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/change-shipping-option-manual.https.html
@@ -2,7 +2,7 @@
 <meta charset="utf-8" />
 <title>Tests for PaymentRequestEvent.changeShippingOption()</title>
 
-<link rel="manifest" href="/payment-handler/basic-card.json" />
+<link rel="manifest" href="/payment-handler/manifest.json" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/payment-instruments.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/payment-instruments.https.html
index 605d3312..121c1315 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/payment-instruments.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/payment-instruments.https.html
@@ -2,12 +2,14 @@
 <meta charset="utf-8">
 <title>Tests for PaymentInstruments interface</title>
 <link rel="help" href="https://w3c.github.io/payment-handler/#paymentinstruments-interface">
-<link rel="manifest" href="basic-card.json">
+<link rel="manifest" href="manifest.json">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="register-and-activate-service-worker.js"></script>
 <script>
 function runTests(registration) {
+  const methodName = window.location.origin + '/payment-handler/payment-app/';
+
   promise_test(async t => {
     await registration.paymentManager.instruments.clear();
     await registration.paymentManager.instruments.set('instrument-key-1', {
@@ -76,8 +78,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
-        capabilities: {supportedNetworks: ['mir']},
+        method: methodName,
       },
     );
     const result = await registration.paymentManager.instruments.get(
@@ -103,8 +104,7 @@
         icons: [
           {src: '/images/green-16x16.png', sizes: '16x16', type: 'image/png'},
         ],
-        method: 'basic-card',
-        capabilities: {supportedNetworks: ['mir']},
+        method: methodName,
       },
     );
     let result = await registration.paymentManager.instruments.get(
@@ -118,7 +118,7 @@
     );
     assert_equals(result.icons[0].sizes, '16x16');
     assert_equals(result.icons[0].type, 'image/png');
-    assert_equals(result.method, 'basic-card');
+    assert_equals(result.method, methodName);
     assert_array_equals(result.capabilities.supportedNetworks, ['mir']);
     await registration.paymentManager.instruments.set(
       'existing-instrument-key',
@@ -131,8 +131,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
-        capabilities: {supportedNetworks: ['visa']},
+        method: methodName,
       },
     );
     result = await registration.paymentManager.instruments.get(
@@ -146,8 +145,7 @@
     );
     assert_equals(result.icons[0].sizes, '256x256');
     assert_equals(result.icons[0].type, 'image/png');
-    assert_equals(result.method, 'basic-card');
-    assert_array_equals(result.capabilities.supportedNetworks, ['visa']);
+    assert_equals(result.method, methodName);
   }, 'Resetting an existing instrument updates the instrument');
 
   promise_test(async t => {
@@ -163,8 +161,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
-        capabilities: {supportedNetworks: ['mir']},
+        method: methodName,
       },
     );
     await registration.paymentManager.instruments.clear();
@@ -187,7 +184,7 @@
             type: 'image/jif',
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -206,7 +203,7 @@
             type: 'image/pn' + 'g'.repeat(100000),
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -223,7 +220,7 @@
           type: 'image/png',
         },
       ],
-      method: 'basic-card',
+      method: methodName,
     });
   }, "Don't crash when registering an instrument with a very long icon size 888...x888...");
 
@@ -237,7 +234,7 @@
           type: 'image/png',
         },
       ],
-      method: 'basic-card',
+      method: methodName,
     });
   }, "Don't crash when 'sizes' missing from icon definition");
 
@@ -251,7 +248,7 @@
           sizes: '256x256',
         },
       ],
-      method: 'basic-card',
+      method: methodName,
     });
   }, "Don't crash when 'type' missing from icon definition");
 
@@ -268,7 +265,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -287,7 +284,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -306,7 +303,7 @@
             type: 'image/png',
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -326,7 +323,7 @@
             type: 'image/gif',
           },
         ],
-        method: 'basic-card',
+        method: methodName,
       },
     );
     return promise_rejects_js(t, TypeError, setPromise);
@@ -378,5 +375,5 @@
   }, "Don't crash on null characters in key, name, method, and capability strings.");
 }
 
-registerAndActiveServiceWorker('basic-card.js', 'payment-app/', runTests);
+registerAndActiveServiceWorker('app-simple.js', 'payment-app/', runTests);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html
index 3c8deb3..e595dd21 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html
@@ -9,13 +9,14 @@
 <p>When the payment sheet is shown, please authorize the mock payment.</p>
 <script>
 async function setInstrumentsAndRunTests(registration) {
+  const methodName = window.location.origin + '/payment-handler/payment-app/';
   await registration.paymentManager.instruments.clear();
   await registration.paymentManager.instruments.set('instrument-key', {
     name: 'Instrument Name',
     icons: [
       {src: '/images/rgrg-256x256.png', sizes: '256x256', type: 'image/png'},
     ],
-    method: 'basic-card',
+    method: methodName,
     capabilities: {supportedNetworks: ['mir']},
   });
   runTests();
@@ -25,7 +26,7 @@
   promise_test(async t => {
     const response = await new PaymentRequest(
       [
-        {supportedMethods: 'basic-card', data: {}},
+        {supportedMethods: methodName, data: {}},
         {supportedMethods: 'interledger', data: {supportedNetworks: ['mir']}},
       ],
       {
@@ -37,7 +38,7 @@
         ],
         modifiers: [
           {
-            supportedMethods: 'basic-card',
+            supportedMethods: methodName,
             data: {supportedNetworks: ['mir']},
             total: {
               label: 'MIR total',
@@ -48,7 +49,7 @@
             ],
           },
           {
-            supportedMethods: 'basic-card',
+            supportedMethods: methodName,
             data: {supportedNetworks: ['visa']},
             total: {
               label: 'VISA total',
@@ -74,30 +75,13 @@
     ).show();
     const promise = response.complete('success');
     assert_equals(response.requestId, 'test-payment-request-identifier');
-    assert_equals(response.methodName, 'basic-card');
-    assert_array_equals(response.details.billingAddress.addressLine, [
-      '1875 Explorer St #1000',
-    ]);
-    assert_equals(response.details.billingAddress.city, 'Reston');
-    assert_equals(response.details.billingAddress.country, 'US');
-    assert_equals(response.details.billingAddress.dependentLocality, '');
-    assert_equals(response.details.billingAddress.organization, 'Google');
-    assert_equals(response.details.billingAddress.phone, '+15555555555');
-    assert_equals(response.details.billingAddress.postalCode, '20190');
-    assert_equals(response.details.billingAddress.recipient, 'Jon Doe');
-    assert_equals(response.details.billingAddress.region, 'VA');
-    assert_equals(response.details.billingAddress.sortingCode, '');
-    assert_equals(response.details.cardNumber, '4111111111111111');
-    assert_equals(response.details.cardSecurityCode, '123');
-    assert_equals(response.details.cardholderName, 'Jon Doe');
-    assert_equals(response.details.expiryMonth, '12');
-    assert_equals(response.details.expiryYear, '2028');
+    assert_equals(response.methodName, methodName);
     return promise;
   }, 'Can perform payment');
 }
 
 registerAndActiveServiceWorker(
-  'basic-card.js',
+  'app-simple.js',
   'payment-app/',
   setInstrumentsAndRunTests,
 );
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/same-object-attributes.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/same-object-attributes.https.html
index b9a9dd8..2e5dea3a 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/same-object-attributes.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/same-object-attributes.https.html
@@ -9,7 +9,7 @@
 
 promise_test(async t => {
   const registration = await service_worker_unregister_and_register(
-      t, 'basic-card.js', 'payment-app/');
+      t, 'app-simple.js', 'payment-app/');
   await wait_for_state(t, registration.installing, 'activated');
 
   assert_equals(registration.paymentManager, registration.paymentManager);
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/supports-shipping-contact-delegation-manual.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/supports-shipping-contact-delegation-manual.https.html
index 75b3668..939e5429 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/supports-shipping-contact-delegation-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/supports-shipping-contact-delegation-manual.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8" />
 <title>Tests for Delegation of shipping and contact collection to PH</title>
-<link rel="manifest" href="/payment-handler/basic-card.json" />
+<link rel="manifest" href="/payment-handler/manifest.json" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/untrusted-event.js b/third_party/blink/web_tests/external/wpt/payment-handler/untrusted-event.js
index 2407029..e067952c 100644
--- a/third_party/blink/web_tests/external/wpt/payment-handler/untrusted-event.js
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/untrusted-event.js
@@ -6,23 +6,23 @@
   if (e.data == 'paymentrequest') {
     self.dispatchEvent(new PaymentRequestEvent('paymentrequest', {
       methodData: [{
-        supportedMethods: 'basic-card'
+        supportedMethods: 'https://example.com/pay'
       }],
       total: {
         currency: 'USD',
         value: '100'
       },
       modifiers: [{
-        supportedMethods: 'basic-card'
+        supportedMethods: 'https://example.com/pay'
       }]
     }));
   } else if (e.data == 'canmakepayment') {
     self.dispatchEvent(new CanMakePaymentEvent('canmakepayment', {
       methodData: [{
-        supportedMethods: 'basic-card'
+        supportedMethods: 'https://example.com/pay'
       }],
       modifiers: [{
-        supportedMethods: 'basic-card'
+        supportedMethods: 'https://example.com/pay'
       }]
     }));
   }
diff --git a/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag-expected.txt b/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag-expected.txt
deleted file mode 100644
index a32c14d..0000000
--- a/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-PASS container.scrollTop is within 0.02 of 0.57
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Tests scroll behavior when a subpixel scrollbar thumb is dragged by mouse.
diff --git a/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag.html b/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag.html
index 2dbe723..0b9088e 100644
--- a/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag.html
+++ b/third_party/blink/web_tests/fast/scrolling/subpixel-overflow-mouse-drag.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/gesture-util.js"></script>
 <style>
 body {
@@ -17,26 +18,25 @@
   height: 200.3px;
 }
 </style>
-Tests scroll behavior when a subpixel scrollbar thumb is dragged by mouse.
 <div id="container">
   <div id="content"></div>
 </div>
 <script>
-window.jsTestIsAsync = true;
-if (window.eventSender) {
   onload = function() {
-    waitForCompositorCommit().then(() => {
+    promise_test(async t => {
+      await waitForCompositorCommit();
+
       var container = document.getElementById('container');
       eventSender.mouseMoveTo(340, 200);
       eventSender.mouseDown();
       eventSender.mouseMoveTo(340, 250);
       eventSender.mouseUp();
 
-      requestAnimationFrame(() => {
-          shouldBeCloseTo("container.scrollTop", "0.57", 0.02);
-          finishJSTest();
+      var expectedScrollTop = 0.57;
+      var tolerance = 0.02;
+      await waitFor(() => {
+        return Math.abs(container.scrollTop - expectedScrollTop) <= tolerance;
       });
-    });
+    }, "Subpixel scrollbar thumb is dragged by mouse.");
   };
-}
 </script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals-expected.txt
index 068fa58..adba69d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals-expected.txt
@@ -36,10 +36,6 @@
 /^regex$/
 testOverriddenToString(/^regex$/, false)
 /^regex$/
-testOverriddenToString(new Date, true)
-Date
-testOverriddenToString(new Date, false)
-Date
 testOverriddenToString({}, true)
 {toString: ƒ, valueOf: ƒ}
 testOverriddenToString({}, false)
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals.js b/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals.js
index 4c96b993..05b85676 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-tainted-globals.js
@@ -136,8 +136,6 @@
         'testOverriddenToString(new Function, false)',
         'testOverriddenToString(/^regex$/, true)',
         'testOverriddenToString(/^regex$/, false)',
-        'testOverriddenToString(new Date, true)',
-        'testOverriddenToString(new Date, false)',
         'testOverriddenToString({}, true)',
         'testOverriddenToString({}, false)',
         'testOverriddenToString(new Number(1), true)',
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt
new file mode 100644
index 0000000..26e6d2184
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt
@@ -0,0 +1,9 @@
+Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.
+
+Setting break on all exceptions.
+Call stack:
+    0) divergingFunctionWithThrow (diverge-without-breakpoint-throw-on-load.html:6)
+    1)  (:1)
+Reloading page...
+Page reloaded.
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js
new file mode 100644
index 0000000..5f6a4d0c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.\n`);
+  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
+  await SourcesTestRunner.startDebuggerTestPromise(true);
+  TestRunner.addResult('Setting break on all exceptions.');
+  TestRunner.DebuggerAgent.setPauseOnExceptions(
+      SDK.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions);
+  SourcesTestRunner.waitUntilPaused(onPaused);
+  await TestRunner.navigatePromise(
+      '../../sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html');
+  TestRunner.evaluateInPageWithTimeout(`divergingFunctionWithThrow()`);
+
+  async function onPaused(callFrames) {
+    await SourcesTestRunner.captureStackTrace(callFrames);
+    TestRunner.addResult('Reloading page...');
+    TestRunner.reloadPage(onPageReloaded);
+  }
+
+  function onPageReloaded() {
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt
new file mode 100644
index 0000000..999ae44d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt
@@ -0,0 +1,13 @@
+Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.
+
+
+Running: testFetchBreakpoint
+Waiting for breakpoint.
+Script execution paused.
+Call stack:
+    0) divergingFunction (reload-on-breakpoint.js:11)
+    1)  (:1)
+Reloading page...
+Script execution resumed.
+Page reloaded.
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js
new file mode 100644
index 0000000..605557c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js
@@ -0,0 +1,31 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.\n`);
+  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
+  await TestRunner.evaluateInPagePromise(`
+      function divergingFunction() {
+          debugger;
+          while(true) {};
+      }
+  `);
+
+  SourcesTestRunner.runDebuggerTestSuite([function testFetchBreakpoint(next) {
+    SourcesTestRunner.waitUntilPaused(onPaused);
+    TestRunner.addResult('Waiting for breakpoint.');
+    TestRunner.evaluateInPageWithTimeout('divergingFunction()');
+
+    async function onPaused(callFrames) {
+      await SourcesTestRunner.captureStackTrace(callFrames);
+      TestRunner.addResult('Reloading page...');
+      TestRunner.reloadPage(onPageReloaded);
+    }
+
+    function onPageReloaded() {
+      next();
+    }
+  }]);
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html
new file mode 100644
index 0000000..f0e33df
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script>
+function divergingFunctionWithThrow() {
+  try {
+    throw new Error();
+  } catch (e) {}
+  while (true) {};
+}
+</script>
+</head>
+</html>
diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c
index 87d8e3b4..5c7c718 100644
--- a/third_party/zlib/deflate.c
+++ b/third_party/zlib/deflate.c
@@ -539,7 +539,7 @@
 #ifdef GZIP
         s->wrap == 2 ? GZIP_STATE :
 #endif
-        s->wrap ? INIT_STATE : BUSY_STATE;
+        INIT_STATE;
     strm->adler =
 #ifdef GZIP
         s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
@@ -600,7 +600,8 @@
 
     if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
     s = strm->state;
-    if (s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
+    if (bits < 0 || bits > 16 ||
+        s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
         return Z_BUF_ERROR;
     do {
         put = Buf_size - s->bi_valid;
@@ -862,6 +863,8 @@
     }
 
     /* Write the header */
+    if (s->status == INIT_STATE && s->wrap == 0)
+        s->status = BUSY_STATE;
     if (s->status == INIT_STATE) {
         /* zlib header */
         uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
diff --git a/third_party/zlib/zlib.h b/third_party/zlib/zlib.h
index 044b4c0..589f865e 100644
--- a/third_party/zlib/zlib.h
+++ b/third_party/zlib/zlib.h
@@ -543,8 +543,7 @@
                                      int  strategy));
 
      This is another version of deflateInit with more compression options.  The
-   fields next_in, zalloc, zfree and opaque must be initialized before by the
-   caller.
+   fields zalloc, zfree and opaque must be initialized before by the caller.
 
      The method parameter is the compression method.  It must be Z_DEFLATED in
    this version of the library.
@@ -866,9 +865,11 @@
    detection, or add 16 to decode only the gzip format (the zlib format will
    return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
    CRC-32 instead of an Adler-32.  Unlike the gunzip utility and gzread() (see
-   below), inflate() will not automatically decode concatenated gzip streams.
-   inflate() will return Z_STREAM_END at the end of the gzip stream.  The state
-   would need to be reset to continue decoding a subsequent gzip stream.
+   below), inflate() will *not* automatically decode concatenated gzip members.
+   inflate() will return Z_STREAM_END at the end of the gzip member.  The state
+   would need to be reset to continue decoding a subsequent gzip member.  This
+   *must* be done if there is more data after a gzip member, in order for the
+   decompression to be compliant with the gzip standard (RFC 1952).
 
      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
    memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 775979ab..b036986 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -272,7 +272,7 @@
     "includes": [2000],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/support_tool/resources.grd": {
-    "META": {"sizes": {"includes": [10]}},
+    "META": {"sizes": {"includes": [20]}},
     "includes": [2010],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/tab_search/tab_search_resources.grd": {
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 23bc524..f7ded341 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1769,7 +1769,16 @@
       if clang_coverage or java_coverage:
         cmdline += ['--coverage-dir', '${ISOLATED_OUTDIR}']
     elif is_fuchsia and test_type != 'script':
+      # On Fuchsia, the generated bin/run_* test scripts are used both in
+      # infrastructure and by developers. test_env.py is intended to establish a
+      # predictable environment for automated testing. In particular, it adds
+      # CHROME_HEADLESS=1 to the environment for child processes. This variable
+      # is a signal to both test and production code that it is running in the
+      # context of automated an testing environment, and should not be present
+      # for normal developer workflows.
       cmdline += [
+          vpython_exe,
+          '../../testing/test_env.py',
           os.path.join('bin', 'run_%s' % target),
           '--test-launcher-bot-mode',
           '--logs-dir=${ISOLATED_OUTDIR}',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 366dedfc..5564c95 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -37786,6 +37786,10 @@
   <int value="4194" label="NavigatorUAData_Mobile"/>
   <int value="4195" label="NavigatorUAData_Platform"/>
   <int value="4196" label="NavigatorUAData_Brands"/>
+  <int value="4197" label="OldConstraintsParsed"/>
+  <int value="4198" label="OldConstraintNotReported"/>
+  <int value="4199" label="OldConstraintRejected"/>
+  <int value="4200" label="OldConstraintIgnored"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -53095,6 +53099,8 @@
   <int value="-1346722635" label="gesture-selection"/>
   <int value="-1345931804" label="TabbedAppOverflowMenuIcons:disabled"/>
   <int value="-1345471133" label="JourneysOmniboxAction:enabled"/>
+  <int value="-1344775211"
+      label="SyncChromeOSExplicitPassphraseSharing:disabled"/>
   <int value="-1344375439" label="ServiceWorkerPaymentApps:disabled"/>
   <int value="-1343259222" label="RegionalLocalesAsDisplayUI:disabled"/>
   <int value="-1342961844" label="InlineUpdateFlow:disabled"/>
@@ -56770,6 +56776,8 @@
   <int value="1179407596" label="PreemptiveLinkToTextGeneration:enabled"/>
   <int value="1179587141" label="CommerceMerchantViewer:enabled"/>
   <int value="1179936481" label="enable-android-pay-integration-v1"/>
+  <int value="1180692900"
+      label="SyncChromeOSExplicitPassphraseSharing:enabled"/>
   <int value="1180722846" label="OculusVR:disabled"/>
   <int value="1181056275" label="enable-cloud-backup"/>
   <int value="1181825084" label="NewDragSpecInLauncher:enabled"/>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index eb6815a..b12f94e 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -34,7 +34,7 @@
 </histogram>
 
 <histogram name="Cras.A2dp20msFailureOverStream" units="units"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -45,7 +45,7 @@
 </histogram>
 
 <histogram name="Cras.A2dpExitCode" enum="CrasA2dpExitCode"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -55,7 +55,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.Busyloop" units="units" expires_after="2022-04-17">
+<histogram name="Cras.Busyloop" units="units" expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.TimePeriod" -->
 
@@ -80,7 +80,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.DeviceGain" units="level" expires_after="2022-04-17">
+<histogram name="Cras.DeviceGain" units="level" expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.DeviceType" -->
 
@@ -107,7 +107,7 @@
 </histogram>
 
 <histogram name="Cras.DeviceTypeInput" enum="CrasDeviceType"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -118,7 +118,7 @@
 </histogram>
 
 <histogram name="Cras.DeviceTypeOutput" enum="CrasDeviceType"
-    expires_after="2022-04-03">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -140,7 +140,7 @@
 </histogram>
 
 <histogram name="Cras.FetchDelayMilliSeconds" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.ClientType" and
      name="Cras.StreamType" -->
@@ -187,7 +187,7 @@
 </histogram>
 
 <histogram name="Cras.HfpWidebandSpeechPacketLoss" units="units"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>enshuo@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -198,7 +198,7 @@
 </histogram>
 
 <histogram name="Cras.HfpWidebandSpeechSupported" units="BooleanSupported"
-    expires_after="2022-04-24">
+    expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -234,7 +234,7 @@
 </histogram>
 
 <histogram name="Cras.HighestInputHardwareLevel" units="frames"
-    expires_after="2022-04-24">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -278,7 +278,7 @@
 </histogram>
 
 <histogram name="Cras.InputDeviceBluetoothNarrowBandMicRuntime" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -301,7 +301,7 @@
 </histogram>
 
 <histogram name="Cras.InputDeviceBluetoothWideBandMicRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -312,7 +312,7 @@
 </histogram>
 
 <histogram name="Cras.InputDeviceFrontMicRuntime" units="seconds"
-    expires_after="2022-12-01">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -378,7 +378,7 @@
 </histogram>
 
 <histogram name="Cras.InputDeviceMicRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -455,7 +455,7 @@
 </histogram>
 
 <histogram name="Cras.InputDeviceUSBRuntime" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -476,7 +476,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFirstTimeInput" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -490,7 +490,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFirstTimeOutput" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -504,7 +504,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFrequencyAfterReschedulingInput"
-    units="count" expires_after="2022-04-17">
+    units="count" expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -515,7 +515,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFrequencyAfterReschedulingOutput"
-    units="count" expires_after="2022-04-17">
+    units="count" expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -527,7 +527,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFrequencyInput" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -538,7 +538,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFrequencyOutput" units="count"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -549,7 +549,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackSecondTimeInput" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -560,7 +560,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackSecondTimeOutput" units="seconds"
-    expires_after="2022-04-24">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -571,7 +571,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceA2DPRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -616,7 +616,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceHDMIRuntime" units="seconds"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -627,7 +627,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceHeadphoneRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -638,7 +638,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceHFPRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -660,7 +660,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceInternalSpeakerRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -671,7 +671,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceLineoutRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -704,7 +704,7 @@
 </histogram>
 
 <histogram name="Cras.OutputDeviceUSBRuntime" units="seconds"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -715,7 +715,7 @@
 </histogram>
 
 <histogram name="Cras.RtcDevicePair" enum="CrasDevicePair"
-    expires_after="2022-04-03">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -802,7 +802,7 @@
 </histogram>
 
 <histogram name="Cras.StreamCallbackThreshold" units="frames"
-    expires_after="2022-04-24">
+    expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
@@ -817,7 +817,7 @@
 </histogram>
 
 <histogram name="Cras.StreamClientTypeInput" enum="CrasClientType"
-    expires_after="2022-04-24">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -827,7 +827,7 @@
 </histogram>
 
 <histogram name="Cras.StreamClientTypeOutput" enum="CrasClientType"
-    expires_after="2022-04-10">
+    expires_after="2022-09-20">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -836,7 +836,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.StreamEffects" units="value" expires_after="2022-04-24">
+<histogram name="Cras.StreamEffects" units="value" expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
@@ -849,7 +849,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.StreamFlags" units="value" expires_after="2022-04-17">
+<histogram name="Cras.StreamFlags" units="value" expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
@@ -862,7 +862,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.StreamRuntime" units="seconds" expires_after="2022-04-17">
+<histogram name="Cras.StreamRuntime" units="seconds" expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" and
@@ -878,7 +878,7 @@
 </histogram>
 
 <histogram name="Cras.StreamSamplingFormat" enum="AlsaSampleFormatType"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
@@ -892,7 +892,7 @@
 </histogram>
 
 <histogram name="Cras.StreamSamplingRate" units="bps"
-    expires_after="2022-04-17">
+    expires_after="2022-09-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
@@ -917,7 +917,7 @@
 </histogram>
 
 <histogram name="Cras.WebRTC.Audio.ApmCaptureInputLevelAverageRms"
-    units="dBFS (negated)" expires_after="2022-04-24">
+    units="dBFS (negated)" expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -931,7 +931,7 @@
 </histogram>
 
 <histogram name="Cras.WebRTC.Audio.ApmCaptureInputLevelPeakRms"
-    units="dBFS (negated)" expires_after="2022-04-24">
+    units="dBFS (negated)" expires_after="2022-09-20">
   <owner>hychao@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index d456f60a..e9d970c3 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -244,8 +244,8 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.BadMessageFunctionName" enum="ExtensionFunctions"
-    expires_after="never">
+<histogram name="Extensions.BadMessageFunctionName{IsKiosk}"
+    enum="ExtensionFunctions" expires_after="never">
 <!-- expires-never: Monitoring core extension system health. -->
 
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -254,8 +254,14 @@
     The number of times each Extension function call sends a bad message,
     killing the renderer. This may indicate a bug in that API's implementation
     on the renderer. Note a similar, aggregate metric is BadMessageTerminate_EFD
-    which counts the number of bad messages that are sent overall.
+    which counts the number of bad messages that are sent overall. In M-102 this
+    histogram was split and events from kiosk extensions are recorded in the
+    separate Extensions.BadMessageFunctionName.Kiosk histogram.
   </summary>
+  <token key="IsKiosk">
+    <variant name="" summary="from non-kiosk extension"/>
+    <variant name=".Kiosk" summary="from kiosk extension"/>
+  </token>
 </histogram>
 
 <histogram name="Extensions.BadSyncDataReason" enum="BadSyncDataReason"
@@ -1859,8 +1865,8 @@
   </token>
 </histogram>
 
-<histogram name="Extensions.Functions.FailedTotalExecutionTime" units="ms"
-    expires_after="2022-08-21">
+<histogram name="Extensions.Functions.FailedTotalExecutionTime{IsKiosk}"
+    units="ms" expires_after="2022-08-21">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -1868,8 +1874,14 @@
     failed from the time the extension function is called to the time the
     function responds. Note that since some extension functions are inherently
     slow (anything that requires user interaction, for instance), this is not a
-    definitive source for function performance.
+    definitive source for function performance. In M-102 this histogram was
+    split and events from kiosk extensions are recorded in the separate
+    Extensions.Functions.FailedTotalExecutionTime.Kiosk histogram.
   </summary>
+  <token key="IsKiosk">
+    <variant name="" summary="from non-kiosk extension"/>
+    <variant name=".Kiosk" summary="from kiosk extension"/>
+  </token>
 </histogram>
 
 <histogram name="Extensions.Functions.HandleResponseElapsedTime" units="ms"
diff --git a/tools/metrics/histograms/metadata/login/histograms.xml b/tools/metrics/histograms/metadata/login/histograms.xml
index 11b70ce5..e26e78a 100644
--- a/tools/metrics/histograms/metadata/login/histograms.xml
+++ b/tools/metrics/histograms/metadata/login/histograms.xml
@@ -118,8 +118,10 @@
 </histogram>
 
 <histogram name="Login.FailureReason" enum="LoginFailureReason"
-    expires_after="2022-08-28">
+    expires_after="2023-04-05">
   <owner>achuith@chromium.org</owner>
+  <owner>antrim@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>Chrome OS login failure reason.</summary>
 </histogram>
 
@@ -179,12 +181,12 @@
 </histogram>
 
 <histogram name="Login.OfflineSuccess.Attempts" units="units"
-    expires_after="2022-04-17">
+    expires_after="2023-04-05">
   <obsolete>
     Removed 03/2022
   </obsolete>
-  <owner>rsorokin@chromium.org</owner>
-  <owner>cros-oac@google.com</owner>
+  <owner>antrim@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>
     On offline login success, records number of attempts, including success.
   </summary>
@@ -201,9 +203,9 @@
 </histogram>
 
 <histogram name="Login.PasswordChanged.ReauthReason" enum="LoginReauthReasons"
-    expires_after="2023-02-24">
-  <owner>rsorokin@chromium.org</owner>
-  <owner>cros-oac@google.com</owner>
+    expires_after="2023-04-05">
+  <owner>antrim@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>
     Tracks the reason why a user was sent through the GAIA re-auth flow which
     caused the local password change.
@@ -211,9 +213,9 @@
 </histogram>
 
 <histogram name="Login.PasswordNotChanged.ReauthReason"
-    enum="LoginReauthReasons" expires_after="2023-02-24">
-  <owner>rsorokin@chromium.org</owner>
-  <owner>cros-oac@google.com</owner>
+    enum="LoginReauthReasons" expires_after="2023-04-05">
+  <owner>antrim@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>
     Tracks the reason why a user was sent through the GAIA re-auth flow which
     did not cause the local password change.
@@ -239,17 +241,17 @@
 </histogram>
 
 <histogram name="Login.PromptToCompleteLoginTime" units="ms"
-    expires_after="2022-04-24">
-  <owner>rsorokin@chromium.org</owner>
+    expires_after="2023-04-05">
+  <owner>antrim@chromium.org</owner>
   <owner>achuith@chromium.org</owner>
-  <owner>cros-oac@google.com</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>
     Time from first display of the login prompt until the user completes signing
     in.
   </summary>
 </histogram>
 
-<histogram name="Login.PromptToLoginTime" units="ms" expires_after="2022-07-10">
+<histogram name="Login.PromptToLoginTime" units="ms" expires_after="2023-04-05">
   <owner>elijahtaylor@google.com</owner>
   <owner>yusukes@chromium.org</owner>
   <owner>antrim@chromium.org</owner>
@@ -291,8 +293,10 @@
 </histogram>
 
 <histogram name="Login.SuccessReason" enum="LoginSuccessReason"
-    expires_after="2022-04-17">
+    expires_after="2023-04-05">
   <owner>achuith@chromium.org</owner>
+  <owner>antrim@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
   <summary>Chrome OS login success reason.</summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index c3460cd..8f4772d 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -818,6 +818,18 @@
   </summary>
 </histogram>
 
+<histogram
+    name="OptimizationGuide.PredictionManager.FirstModelFetchSinceServiceInit"
+    units="ms" expires_after="M106">
+  <owner>rajendrant@chromium.org</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records the time duration it took for the first model fetch to start from
+    the time the optimization guide service was initialized. Recorded once at
+    startup when the first model fetch happens.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.PredictionManager.HostModelFeaturesMapSize"
     units="total host count" expires_after="2022-01-28">
   <obsolete>
@@ -889,19 +901,7 @@
 </histogram>
 
 <histogram
-    name="OptimizationGuide.PredictionModel.FirstModelFetchSinceServiceInit"
-    units="ms" expires_after="M106">
-  <owner>rajendrant@chromium.org</owner>
-  <owner>chrome-intelligence-core@google.com</owner>
-  <summary>
-    Records the time duration it took for the first model fetch to start from
-    the time the optimization guide service was initialized. Recorded once at
-    startup when the first model fetch happens.
-  </summary>
-</histogram>
-
-<histogram
-    name="OptimizationGuide.PredictionModel.RegistrationTimeSinceServiceInit.{OptimizationTarget}"
+    name="OptimizationGuide.PredictionManager.RegistrationTimeSinceServiceInit.{OptimizationTarget}"
     units="ms" expires_after="M106">
   <owner>rajendrant@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index 420370d..4e27295 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -1869,6 +1869,53 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.PasswordCheckup.GetIntent.Error"
+    enum="CredentialManagerError" expires_after="M105">
+  <owner>ioanap@chromium.org</owner>
+  <owner>vsemeniuk@google.com</owner>
+  <summary>
+    Records the error encountered while attempting to fetch the Password Checkup
+    launch intent from Google Play Services. This is recorded either before
+    making the actual request (if the preconditions are not met) or after the
+    asynchronous call comes back with an error.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.PasswordCheckup.GetIntent.Latency" units="ms"
+    expires_after="M105">
+  <owner>ioanap@chromium.org</owner>
+  <owner>vsemeniuk@google.com</owner>
+  <summary>
+    Records the time(ms) elapsed between asking Google Play Services for the
+    intent used to open the Password Checkup and receiving it. It includes
+    synchronous calls made to get the PasswordCheckupClient. Recorded when the
+    asynchronous call comes back and only on success.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.PasswordCheckup.GetIntent.Success"
+    enum="BooleanSuccess" expires_after="M105">
+  <owner>ioanap@chromium.org</owner>
+  <owner>vsemeniuk@google.com</owner>
+  <summary>
+    Records whether fetching the Password Checkup launch intent from Google Play
+    Services was successful or not. Recorded when the asynchronous call comes
+    back.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.PasswordCheckup.Launch.Success"
+    enum="BooleanSuccess" expires_after="M105">
+  <owner>ioanap@chromium.org</owner>
+  <owner>vsemeniuk@google.com</owner>
+  <summary>
+    Records whether the provided intent to launch the Password Checkup could be
+    used successfully. This is recorded right after calling send() on the intent
+    which happens when the async call to get the intent from Google Play
+    Services returns.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.PasswordDropdownItemSelected"
     enum="PasswordDropdownSelectedOption" expires_after="2022-08-28">
   <owner>kazinova@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index dbbaa4b..5ba1b36 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -13,8 +13,8 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "2197ca6d61efb145098d26ab288c6737159e62a2",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/91ae5d4e524b667ad9f6b3e29e835977fb72b7af/trace_processor_shell"
+            "hash": "d6dc50655a324e5b11342605e0ace65c76659ac3",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/f3337aeb229bb23d401c344644077be5949de4f0/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "a66f8459d66a0741b1707dd40ab51865c1f1962e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/341b4249f0262485d55003dc533942d0bb5cc0ff/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/f3337aeb229bb23d401c344644077be5949de4f0/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/aura/client/drag_drop_delegate.cc b/ui/aura/client/drag_drop_delegate.cc
index cded1bf..f00c09c 100644
--- a/ui/aura/client/drag_drop_delegate.cc
+++ b/ui/aura/client/drag_drop_delegate.cc
@@ -25,12 +25,6 @@
 
 DEFINE_UI_CLASS_PROPERTY_KEY(DragDropDelegate*, kDragDropDelegateKey, nullptr)
 
-ui::mojom::DragOperation DragDropDelegate::OnPerformDrop(
-    const ui::DropTargetEvent& event,
-    std::unique_ptr<ui::OSExchangeData> data) {
-  return ui::mojom::DragOperation::kNone;
-}
-
 void SetDragDropDelegate(Window* window, DragDropDelegate* delegate) {
   window->SetProperty(kDragDropDelegateKey, delegate);
 }
diff --git a/ui/aura/client/drag_drop_delegate.h b/ui/aura/client/drag_drop_delegate.h
index e19d72d..fca177a 100644
--- a/ui/aura/client/drag_drop_delegate.h
+++ b/ui/aura/client/drag_drop_delegate.h
@@ -61,18 +61,6 @@
   // when the drag session was canceled and the mouse was over the window.
   virtual void OnDragExited() = 0;
 
-  // Invoked during a drag and drop session when OnDragUpdated returns a valid
-  // operation and the user release the mouse. This function gets the ownership
-  // of underlying OSExchangeData. A reference to this same OSExchangeData is
-  // also stored in the DropTargetEvent. Implementor of this function should be
-  // aware of keeping the OSExchageData alive until it wants to access it
-  // through the parameter or the stored reference in DropTargetEvent.
-  // TODO(crbug.com/1175682): Remove OnPerformDrop and switch to GetDropCallback
-  // instead.
-  virtual ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event,
-      std::unique_ptr<ui::OSExchangeData> data);
-
   // Invoked during a drag and drop session when the user release the mouse, but
   // the drop is held because of the DataTransferPolicyController.
   // The returned callback may be NullCallback if there's nothing to do and the
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index 93a5b56..b9af8933 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -188,7 +188,7 @@
   host_frame_sink_manager.SetLocalManager(&frame_sink_manager);
   frame_sink_manager.SetLocalClient(&host_frame_sink_manager);
   auto context_factory = std::make_unique<ui::InProcessContextFactory>(
-      &host_frame_sink_manager, &frame_sink_manager);
+      &host_frame_sink_manager, &frame_sink_manager, /*output_to_window=*/true);
 
   base::PowerMonitor::Initialize(
       std::make_unique<base::PowerMonitorDeviceSource>());
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index 0d8ea298..4531db5 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -160,8 +160,10 @@
 
 InProcessContextFactory::InProcessContextFactory(
     viz::HostFrameSinkManager* host_frame_sink_manager,
-    viz::FrameSinkManagerImpl* frame_sink_manager)
+    viz::FrameSinkManagerImpl* frame_sink_manager,
+    bool output_to_window)
     : frame_sink_id_allocator_(kDefaultClientId),
+      output_to_window_(output_to_window),
       disable_vsync_(base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableVsyncForTests)),
       host_frame_sink_manager_(host_frame_sink_manager),
@@ -211,7 +213,7 @@
 
   auto skia_deps = std::make_unique<viz::SkiaOutputSurfaceDependencyImpl>(
       viz::TestGpuServiceHolder::GetInstance()->gpu_service(),
-      gpu::kNullSurfaceHandle);
+      output_to_window_ ? data->surface_handle() : gpu::kNullSurfaceHandle);
   auto display_dependency =
       std::make_unique<viz::DisplayCompositorMemoryAndTaskController>(
           std::move(skia_deps));
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index b33e266..2299fd2 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -36,8 +36,11 @@
   // ContextFactory.
   // TODO(crbug.com/657959): |frame_sink_manager| should go away and we should
   // use the LayerTreeFrameSink from the HostFrameSinkManager.
+  // The default for |output_to_window| will create an OutputSurface that does
+  // not display anything. Set to true if you want to see results on the screen.
   InProcessContextFactory(viz::HostFrameSinkManager* host_frame_sink_manager,
-                          viz::FrameSinkManagerImpl* frame_sink_manager);
+                          viz::FrameSinkManagerImpl* frame_sink_manager,
+                          bool output_to_window = false);
 
   InProcessContextFactory(const InProcessContextFactory&) = delete;
   InProcessContextFactory& operator=(const InProcessContextFactory&) = delete;
@@ -86,6 +89,7 @@
   cc::TestTaskGraphRunner task_graph_runner_;
   viz::FrameSinkIdAllocator frame_sink_id_allocator_;
   viz::SubtreeCaptureIdAllocator subtree_capture_id_allocator_;
+  bool output_to_window_ = false;
   bool disable_vsync_ = false;
   double refresh_rate_ = 60.0;
   const raw_ptr<viz::HostFrameSinkManager> host_frame_sink_manager_;
diff --git a/ui/compositor/test/test_context_factories.cc b/ui/compositor/test/test_context_factories.cc
index 60aec31..b9a8126f 100644
--- a/ui/compositor/test/test_context_factories.cc
+++ b/ui/compositor/test/test_context_factories.cc
@@ -15,7 +15,8 @@
 
 namespace ui {
 
-TestContextFactories::TestContextFactories(bool enable_pixel_output) {
+TestContextFactories::TestContextFactories(bool enable_pixel_output,
+                                           bool output_to_window) {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kEnablePixelOutputInTests))
     enable_pixel_output = true;
@@ -26,7 +27,8 @@
       viz::FrameSinkManagerImpl::InitParams(shared_bitmap_manager_.get()));
   host_frame_sink_manager_ = std::make_unique<viz::HostFrameSinkManager>();
   implicit_factory_ = std::make_unique<InProcessContextFactory>(
-      host_frame_sink_manager_.get(), frame_sink_manager_.get());
+      host_frame_sink_manager_.get(), frame_sink_manager_.get(),
+      output_to_window);
   implicit_factory_->SetUseFastRefreshRateForTests();
 
   // Directly connect without using Mojo.
diff --git a/ui/compositor/test/test_context_factories.h b/ui/compositor/test/test_context_factories.h
index 2709f36..805e42f3 100644
--- a/ui/compositor/test/test_context_factories.h
+++ b/ui/compositor/test/test_context_factories.h
@@ -28,7 +28,10 @@
 // |enable_pixel_output|. Most unit tests should pass false.
 class TestContextFactories {
  public:
-  explicit TestContextFactories(bool enable_pixel_output);
+  // The default for |output_to_window| will create an OutputSurface that does
+  // not display anything. Set to true if you want to see results on the screen.
+  explicit TestContextFactories(bool enable_pixel_output,
+                                bool output_to_window = false);
   ~TestContextFactories();
 
   TestContextFactories(const TestContextFactories&) = delete;
diff --git a/ui/gfx/linux/gbm_buffer.h b/ui/gfx/linux/gbm_buffer.h
index a9805d9..99e28e30 100644
--- a/ui/gfx/linux/gbm_buffer.h
+++ b/ui/gfx/linux/gbm_buffer.h
@@ -29,6 +29,7 @@
   virtual gfx::BufferFormat GetBufferFormat() const = 0;
   virtual bool AreFdsValid() const = 0;
   virtual size_t GetNumPlanes() const = 0;
+  virtual bool SupportsZeroCopyWebGPUImport() const = 0;
   virtual int GetPlaneFd(size_t plane) const = 0;
   virtual uint32_t GetPlaneHandle(size_t plane) const = 0;
   virtual uint32_t GetPlaneStride(size_t plane) const = 0;
diff --git a/ui/gfx/linux/gbm_wrapper.cc b/ui/gfx/linux/gbm_wrapper.cc
index 04417ff..4be43d55 100644
--- a/ui/gfx/linux/gbm_wrapper.cc
+++ b/ui/gfx/linux/gbm_wrapper.cc
@@ -153,7 +153,9 @@
         format_modifier_(modifier),
         flags_(flags),
         size_(size),
-        handle_(std::move(handle)) {}
+        handle_(std::move(handle)) {
+    handle_.supports_zero_copy_webgpu_import = SupportsZeroCopyWebGPUImport();
+  }
 
   Buffer(const Buffer&) = delete;
   Buffer& operator=(const Buffer&) = delete;
@@ -187,6 +189,21 @@
     DCHECK_LT(plane, handle_.planes.size());
     return handle_.planes[plane].fd.get();
   }
+
+  bool SupportsZeroCopyWebGPUImport() const override {
+    // NOT supported if the buffer is multi-planar and its planes are disjoint.
+    size_t plane_count = GetNumPlanes();
+    if (plane_count > 1) {
+      uint32_t handle = GetPlaneHandle(0);
+      for (size_t plane = 1; plane < plane_count; ++plane) {
+        if (GetPlaneHandle(plane) != handle) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
   uint32_t GetPlaneStride(size_t plane) const override {
     DCHECK_LT(plane, handle_.planes.size());
     return handle_.planes[plane].stride;
@@ -248,7 +265,7 @@
 
   const gfx::Size size_;
 
-  const gfx::NativePixmapHandle handle_;
+  gfx::NativePixmapHandle handle_;
 };
 
 std::unique_ptr<Buffer> CreateBufferForBO(struct gbm_bo* bo,
diff --git a/ui/gfx/linux/native_pixmap_dmabuf.cc b/ui/gfx/linux/native_pixmap_dmabuf.cc
index 2cddfdde..196ae1e6 100644
--- a/ui/gfx/linux/native_pixmap_dmabuf.cc
+++ b/ui/gfx/linux/native_pixmap_dmabuf.cc
@@ -60,6 +60,12 @@
   return handle_.planes.size();
 }
 
+bool NativePixmapDmaBuf::SupportsZeroCopyWebGPUImport() const {
+  // TODO(crbug.com/1258986): Figure out how to import multi-planar pixmap into
+  // WebGPU without copy.
+  return false;
+}
+
 gfx::Size NativePixmapDmaBuf::GetBufferSize() const {
   return size_;
 }
diff --git a/ui/gfx/linux/native_pixmap_dmabuf.h b/ui/gfx/linux/native_pixmap_dmabuf.h
index 189d441..141a192 100644
--- a/ui/gfx/linux/native_pixmap_dmabuf.h
+++ b/ui/gfx/linux/native_pixmap_dmabuf.h
@@ -38,6 +38,7 @@
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   gfx::Size GetBufferSize() const override;
   uint32_t GetUniqueId() const override;
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
diff --git a/ui/gfx/linux/test/mock_gbm_device.cc b/ui/gfx/linux/test/mock_gbm_device.cc
index 9ea3659a..dd6e22e0 100644
--- a/ui/gfx/linux/test/mock_gbm_device.cc
+++ b/ui/gfx/linux/test/mock_gbm_device.cc
@@ -73,6 +73,12 @@
   int GetPlaneFd(size_t plane) const override {
     return planes_[plane].fd.get();
   }
+
+  bool SupportsZeroCopyWebGPUImport() const override {
+    NOTIMPLEMENTED();
+    return false;
+  }
+
   uint32_t GetPlaneStride(size_t plane) const override {
     DCHECK_LT(plane, planes_.size());
     return planes_[plane].stride;
diff --git a/ui/gfx/native_pixmap.h b/ui/gfx/native_pixmap.h
index a880df80..8166d61 100644
--- a/ui/gfx/native_pixmap.h
+++ b/ui/gfx/native_pixmap.h
@@ -31,6 +31,7 @@
   virtual size_t GetDmaBufPlaneSize(size_t plane) const = 0;
   // Return the number of non-interleaved "color" planes.
   virtual size_t GetNumberOfPlanes() const = 0;
+  virtual bool SupportsZeroCopyWebGPUImport() const = 0;
 
   // The following methods return format, modifier and size of the buffer,
   // respectively.
diff --git a/ui/gfx/native_pixmap_handle.cc b/ui/gfx/native_pixmap_handle.cc
index ff72779..188bb89 100644
--- a/ui/gfx/native_pixmap_handle.cc
+++ b/ui/gfx/native_pixmap_handle.cc
@@ -101,6 +101,8 @@
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   clone.modifier = handle.modifier;
+  clone.supports_zero_copy_webgpu_import =
+      handle.supports_zero_copy_webgpu_import;
 #endif
 
 #if BUILDFLAG(IS_FUCHSIA)
diff --git a/ui/gfx/native_pixmap_handle.h b/ui/gfx/native_pixmap_handle.h
index 9a450cb..a9f8fa5 100644
--- a/ui/gfx/native_pixmap_handle.h
+++ b/ui/gfx/native_pixmap_handle.h
@@ -87,6 +87,9 @@
   // Generally it's platform specific, and we don't need to modify it in
   // Chromium code. Also one per plane per entry.
   uint64_t modifier = kNoModifier;
+
+  // WebGPU can directly import the handle to create texture from it.
+  bool supports_zero_copy_webgpu_import = false;
 #endif
 
 #if BUILDFLAG(IS_FUCHSIA)
diff --git a/ui/ozone/platform/cast/surface_factory_cast.cc b/ui/ozone/platform/cast/surface_factory_cast.cc
index 52d2859..073671f 100644
--- a/ui/ozone/platform/cast/surface_factory_cast.cc
+++ b/ui/ozone/platform/cast/surface_factory_cast.cc
@@ -63,6 +63,11 @@
     return gfx::BufferFormat::BGRA_8888;
   }
   size_t GetNumberOfPlanes() const override { return 1; }
+  bool SupportsZeroCopyWebGPUImport() const override {
+    // TODO(crbug.com/1258986): Figure out how to import multi-planar pixmap
+    // into WebGPU without copy.
+    return false;
+  }
   gfx::Size GetBufferSize() const override { return gfx::Size(); }
   uint32_t GetUniqueId() const override { return 0; }
 
diff --git a/ui/ozone/platform/drm/gpu/gbm_pixmap.cc b/ui/ozone/platform/drm/gpu/gbm_pixmap.cc
index 92b9577..fdff43be 100644
--- a/ui/ozone/platform/drm/gpu/gbm_pixmap.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_pixmap.cc
@@ -52,6 +52,10 @@
   return buffer_->GetNumPlanes();
 }
 
+bool GbmPixmap::SupportsZeroCopyWebGPUImport() const {
+  return buffer_->SupportsZeroCopyWebGPUImport();
+}
+
 uint64_t GbmPixmap::GetBufferFormatModifier() const {
   return buffer_->GetFormatModifier();
 }
diff --git a/ui/ozone/platform/drm/gpu/gbm_pixmap.h b/ui/ozone/platform/drm/gpu/gbm_pixmap.h
index a2192a3..c5abbdf 100644
--- a/ui/ozone/platform/drm/gpu/gbm_pixmap.h
+++ b/ui/ozone/platform/drm/gpu/gbm_pixmap.h
@@ -34,6 +34,7 @@
   size_t GetDmaBufOffset(size_t plane) const override;
   size_t GetDmaBufPlaneSize(size_t plane) const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   gfx::Size GetBufferSize() const override;
diff --git a/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.cc b/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.cc
index 54d79fa..0423510e8 100644
--- a/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.cc
+++ b/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.cc
@@ -46,6 +46,13 @@
   return 0;
 }
 
+bool FlatlandSysmemNativePixmap::SupportsZeroCopyWebGPUImport() const {
+  NOTREACHED();
+  // TODO(crbug.com/1304490): Figure out how to import multi-planar pixmap into
+  // WebGPU without copy.
+  return false;
+}
+
 uint64_t FlatlandSysmemNativePixmap::GetBufferFormatModifier() const {
   NOTREACHED();
   return 0;
diff --git a/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.h b/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.h
index ad7d6a8f..a5d6d0d 100644
--- a/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.h
+++ b/ui/ozone/platform/flatland/flatland_sysmem_native_pixmap.h
@@ -27,6 +27,7 @@
   size_t GetDmaBufOffset(size_t plane) const override;
   size_t GetDmaBufPlaneSize(size_t plane) const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   gfx::Size GetBufferSize() const override;
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc
index cc40d98..66d995e 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -164,6 +164,7 @@
   size_t GetNumberOfPlanes() const override {
     return gfx::NumberOfPlanesForLinearBufferFormat(format_);
   }
+  bool SupportsZeroCopyWebGPUImport() const override { return false; }
   gfx::Size GetBufferSize() const override { return gfx::Size(); }
   uint32_t GetUniqueId() const override { return 0; }
   bool ScheduleOverlayPlane(
diff --git a/ui/ozone/platform/scenic/sysmem_native_pixmap.cc b/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
index 0322f07..8681209 100644
--- a/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
+++ b/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
@@ -59,6 +59,11 @@
   return 0;
 }
 
+bool SysmemNativePixmap::SupportsZeroCopyWebGPUImport() const {
+  NOTREACHED();
+  return false;
+}
+
 uint64_t SysmemNativePixmap::GetBufferFormatModifier() const {
   NOTREACHED();
   return 0;
diff --git a/ui/ozone/platform/scenic/sysmem_native_pixmap.h b/ui/ozone/platform/scenic/sysmem_native_pixmap.h
index b54eca9..80b97dd 100644
--- a/ui/ozone/platform/scenic/sysmem_native_pixmap.h
+++ b/ui/ozone/platform/scenic/sysmem_native_pixmap.h
@@ -27,6 +27,7 @@
   size_t GetDmaBufOffset(size_t plane) const override;
   size_t GetDmaBufPlaneSize(size_t plane) const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   gfx::Size GetBufferSize() const override;
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
index 2ec4e4e..3d6d59d 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -141,6 +141,12 @@
   return gbm_bo_->GetNumPlanes();
 }
 
+bool GbmPixmapWayland::SupportsZeroCopyWebGPUImport() const {
+  // TODO(crbug.com/1258986): Figure out how to import multi-planar pixmap into
+  // WebGPU without copy.
+  return false;
+}
+
 uint64_t GbmPixmapWayland::GetBufferFormatModifier() const {
   return gbm_bo_->GetFormatModifier();
 }
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
index e9b25a74528..49b038b 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
@@ -57,6 +57,7 @@
   size_t GetDmaBufOffset(size_t plane) const override;
   size_t GetDmaBufPlaneSize(size_t plane) const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   gfx::Size GetBufferSize() const override;
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 4b2cf99..43a8ae90 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -2529,6 +2529,87 @@
   }
 }
 
+// Tests that size constraints returned by the `ui::PlatformWindowDelegate` are
+// obeyed by the window when its bounds are set internally via its SetBounds()
+// implementation.
+TEST_P(WaylandWindowTest, SizeConstraintsInternal) {
+  const gfx::Rect kMinBounds{0, 0, 100, 100};
+  const gfx::Rect kMaxBounds{0, 0, 300, 300};
+
+  window_->SetBounds({0, 0, 200, 200});
+  Sync();
+
+  auto even_smaller_bounds = kMinBounds;
+  even_smaller_bounds.Inset(10);
+  even_smaller_bounds.set_origin({0, 0});
+
+  EXPECT_CALL(delegate_, GetMinimumSizeForWindow())
+      .WillOnce(Return(kMinBounds.size()));
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(kMinBounds)));
+
+  window_->SetBounds(even_smaller_bounds);
+  Sync();
+
+  VerifyAndClearExpectations();
+
+  auto even_greater_bounds = kMaxBounds;
+  even_greater_bounds.Outset(10);
+  even_greater_bounds.set_origin({0, 0});
+
+  EXPECT_CALL(delegate_, GetMaximumSizeForWindow())
+      .WillOnce(Return(kMaxBounds.size()));
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(kMaxBounds)));
+
+  window_->SetBounds(even_greater_bounds);
+  Sync();
+}
+
+// Tests that size constraints returned by the `ui::PlatformWindowDelegate` are
+// obeyed by the window when its bounds are set externally via the configure
+// event sent by the compositor.
+TEST_P(WaylandWindowTest, SizeConstraintsExternal) {
+  const gfx::Rect kMinBounds{0, 0, 100, 100};
+  const gfx::Rect kMaxBounds{0, 0, 300, 300};
+
+  EXPECT_CALL(delegate_, GetMinimumSizeForWindow())
+      .WillRepeatedly(Return(kMinBounds.size()));
+  EXPECT_CALL(delegate_, GetMaximumSizeForWindow())
+      .WillRepeatedly(Return(kMaxBounds.size()));
+
+  window_->SetBounds({0, 0, 200, 200});
+  Sync();
+
+  uint32_t serial = 0;
+  auto state = InitializeWlArrayWithActivatedState();
+
+  auto even_smaller_bounds = kMinBounds;
+  even_smaller_bounds.Inset(10);
+  even_smaller_bounds.set_origin({0, 0});
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(kMinBounds)));
+
+  SendConfigureEvent(xdg_surface_, even_smaller_bounds.width(),
+                     even_smaller_bounds.height(), ++serial, state.get());
+  Sync();
+
+  VerifyAndClearExpectations();
+
+  EXPECT_CALL(delegate_, GetMinimumSizeForWindow())
+      .WillRepeatedly(Return(kMinBounds.size()));
+  EXPECT_CALL(delegate_, GetMaximumSizeForWindow())
+      .WillRepeatedly(Return(kMaxBounds.size()));
+
+  auto even_greater_bounds = kMaxBounds;
+  even_greater_bounds.Outset(10);
+  even_greater_bounds.set_origin({0, 0});
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(kMaxBounds)));
+
+  SendConfigureEvent(xdg_surface_, even_greater_bounds.width(),
+                     even_greater_bounds.height(), ++serial, state.get());
+  Sync();
+}
+
 TEST_P(WaylandWindowTest, OnSizeConstraintsChanged) {
   const bool kBooleans[] = {false, true};
   for (bool has_min_size : kBooleans) {
diff --git a/ui/ozone/platform/wayland/test/test_data_device.cc b/ui/ozone/platform/wayland/test/test_data_device.cc
index e2a8a8b..ee3fc397 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.cc
+++ b/ui/ozone/platform/wayland/test/test_data_device.cc
@@ -11,6 +11,7 @@
 #include "base/notreached.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
 #include "ui/ozone/platform/wayland/test/test_data_offer.h"
 #include "ui/ozone/platform/wayland/test/test_data_source.h"
 #include "ui/ozone/platform/wayland/test/test_selection_device_manager.h"
@@ -75,15 +76,19 @@
     &DataDeviceStartDrag, &TestSelectionDevice::SetSelection,
     &DataDeviceRelease};
 
-TestDataDevice::TestDataDevice(wl_resource* resource, wl_client* client)
+TestDataDevice::TestDataDevice(wl_resource* resource,
+                               wl_client* client,
+                               TestDataDeviceManager* manager)
     : TestSelectionDevice(resource, new WlDataDeviceImpl(this)),
-      client_(client) {}
+      client_(client),
+      manager_(manager) {}
 
 TestDataDevice::~TestDataDevice() = default;
 
 void TestDataDevice::SetSelection(TestDataSource* data_source,
                                   uint32_t serial) {
-  NOTIMPLEMENTED();
+  CHECK(manager_);
+  manager_->set_data_source(data_source);
 }
 
 TestDataOffer* TestDataDevice::CreateAndSendDataOffer() {
@@ -95,6 +100,10 @@
                                uint32_t serial) {
   DCHECK(source);
   DCHECK(origin);
+
+  CHECK(manager_);
+  manager_->set_data_source(source);
+
   if (drag_delegate_)
     drag_delegate_->StartDrag(source, origin, serial);
   wl_client_flush(client_);
diff --git a/ui/ozone/platform/wayland/test/test_data_device.h b/ui/ozone/platform/wayland/test/test_data_device.h
index ba2b2c4d..16242fb4 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.h
+++ b/ui/ozone/platform/wayland/test/test_data_device.h
@@ -21,6 +21,7 @@
 
 class TestDataOffer;
 class TestDataSource;
+class TestDataDeviceManager;
 
 class TestDataDevice : public TestSelectionDevice {
  public:
@@ -30,7 +31,9 @@
                            uint32_t serial) = 0;
   };
 
-  TestDataDevice(wl_resource* resource, wl_client* client);
+  TestDataDevice(wl_resource* resource,
+                 wl_client* client,
+                 TestDataDeviceManager* manager);
 
   TestDataDevice(const TestDataDevice&) = delete;
   TestDataDevice& operator=(const TestDataDevice&) = delete;
@@ -59,6 +62,8 @@
  private:
   wl_client* client_ = nullptr;
   DragDelegate* drag_delegate_ = nullptr;
+
+  TestDataDeviceManager* const manager_;
 };
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_device_manager.cc b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
index e54efbf8..5e774db 100644
--- a/ui/ozone/platform/wayland/test/test_data_device_manager.cc
+++ b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
@@ -8,6 +8,7 @@
 
 #include "ui/ozone/platform/wayland/test/test_data_device.h"
 #include "ui/ozone/platform/wayland/test/test_data_source.h"
+#include "ui/ozone/platform/wayland/test/test_selection_device_manager.h"
 
 namespace wl {
 
@@ -16,23 +17,25 @@
 constexpr uint32_t kDataDeviceManagerVersion = 3;
 
 void CreateDataSource(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* data_source_resource = CreateResourceWithImpl<TestDataSource>(
-      client, &wl_data_source_interface, wl_resource_get_version(resource),
-      &kTestDataSourceImpl, id);
-  GetUserDataAs<TestDataDeviceManager>(resource)->set_data_source(
-      GetUserDataAs<TestDataSource>(data_source_resource));
+  CreateResourceWithImpl<TestDataSource>(client, &wl_data_source_interface,
+                                         wl_resource_get_version(resource),
+                                         &kTestDataSourceImpl, id);
 }
 
 void GetDataDevice(wl_client* client,
-                   wl_resource* data_device_manager_resource,
+                   wl_resource* manager_resource,
                    uint32_t id,
                    wl_resource* seat_resource) {
+  auto* manager = GetUserDataAs<TestDataDeviceManager>(manager_resource);
+  CHECK(manager);
+
   wl_resource* resource = CreateResourceWithImpl<TestDataDevice>(
       client, &wl_data_device_interface,
-      wl_resource_get_version(data_device_manager_resource),
-      &kTestDataDeviceImpl, id, client);
-  GetUserDataAs<TestDataDeviceManager>(data_device_manager_resource)
-      ->set_data_device(GetUserDataAs<TestDataDevice>(resource));
+      wl_resource_get_version(manager_resource), &kTestDataDeviceImpl, id,
+      client, manager);
+
+  CHECK(GetUserDataAs<TestDataDevice>(resource));
+  manager->set_data_device(GetUserDataAs<TestDataDevice>(resource));
 }
 
 }  // namespace
diff --git a/ui/ozone/platform/wayland/test/test_selection_device_manager.cc b/ui/ozone/platform/wayland/test/test_selection_device_manager.cc
index 8936fa48..ec2626d 100644
--- a/ui/ozone/platform/wayland/test/test_selection_device_manager.cc
+++ b/ui/ozone/platform/wayland/test/test_selection_device_manager.cc
@@ -154,6 +154,8 @@
   auto* src = source ? GetUserDataAs<TestSelectionSource>(source) : nullptr;
   self->selection_serial_ = serial;
   self->delegate_->HandleSetSelection(src, serial);
+  if (self->manager_)
+    self->manager_->set_source(src);
 }
 
 TestSelectionDeviceManager::TestSelectionDeviceManager(
@@ -171,7 +173,7 @@
                                               uint32_t id) {
   CHECK(GetUserDataAs<TestSelectionDeviceManager>(manager_resource));
   auto* manager = GetUserDataAs<TestSelectionDeviceManager>(manager_resource);
-  manager->source_ = manager->delegate_->CreateSource(client, id);
+  manager->delegate_->CreateSource(client, id);
 }
 
 void TestSelectionDeviceManager::GetDevice(wl_client* client,
@@ -180,7 +182,9 @@
                                            wl_resource* seat_resource) {
   CHECK(GetUserDataAs<TestSelectionDeviceManager>(manager_resource));
   auto* manager = GetUserDataAs<TestSelectionDeviceManager>(manager_resource);
-  manager->device_ = manager->delegate_->CreateDevice(client, id);
+  auto* new_device = manager->delegate_->CreateDevice(client, id);
+  new_device->set_manager(manager);
+  manager->device_ = new_device;
 }
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_selection_device_manager.h b/ui/ozone/platform/wayland/test/test_selection_device_manager.h
index 6fa9ea59..bf4c9578 100644
--- a/ui/ozone/platform/wayland/test/test_selection_device_manager.h
+++ b/ui/ozone/platform/wayland/test/test_selection_device_manager.h
@@ -167,6 +167,8 @@
   TestSelectionOffer* OnDataOffer();
   void OnSelection(TestSelectionOffer* offer);
 
+  void set_manager(TestSelectionDeviceManager* manager) { manager_ = manager; }
+
   // Protocol object requests:
   static void SetSelection(struct wl_client* client,
                            struct wl_resource* resource,
@@ -179,6 +181,8 @@
   Delegate* const delegate_;
 
   uint32_t selection_serial_ = 0;
+
+  TestSelectionDeviceManager* manager_ = nullptr;
 };
 
 }  // namespace wl
diff --git a/ui/views/controls/progress_ring_utils.cc b/ui/views/controls/progress_ring_utils.cc
index 0faf83d..48ee1fe 100644
--- a/ui/views/controls/progress_ring_utils.cc
+++ b/ui/views/controls/progress_ring_utils.cc
@@ -11,14 +11,15 @@
 
 namespace views {
 
-void DrawProgressRing(gfx::Canvas* canvas,
-                      const SkRect& bounds,
-                      SkColor background_color,
-                      SkColor progress_color,
-                      float stroke_width,
-                      SkScalar start_angle,
-                      SkScalar sweep_angle) {
-  // Draw the background ring that gets progressively filled.
+namespace {
+
+void DrawRing(gfx::Canvas* canvas,
+              const SkRect& bounds,
+              SkColor background_color,
+              SkColor progress_color,
+              float stroke_width,
+              SkPath path) {
+  // Draw the background ring.
   SkPath background_path;
   background_path.addArc(bounds, /*startAngle=*/-90,
                          /*SweepAngle=*/360);
@@ -30,8 +31,6 @@
   canvas->DrawPath(std::move(background_path), std::move(background_flags));
 
   // Draw the filled portion of the ring.
-  SkPath path;
-  path.addArc(bounds, start_angle, sweep_angle);
   cc::PaintFlags flags;
   flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
   flags.setAntiAlias(true);
@@ -40,4 +39,33 @@
   canvas->DrawPath(std::move(path), std::move(flags));
 }
 
+}  // namespace
+
+void DrawProgressRing(gfx::Canvas* canvas,
+                      const SkRect& bounds,
+                      SkColor background_color,
+                      SkColor progress_color,
+                      float stroke_width,
+                      SkScalar start_angle,
+                      SkScalar sweep_angle) {
+  SkPath path;
+  path.addArc(bounds, start_angle, sweep_angle);
+  DrawRing(canvas, bounds, background_color, progress_color, stroke_width,
+           std::move(path));
+}
+
+void DrawSpinningRing(gfx::Canvas* canvas,
+                      const SkRect& bounds,
+                      SkColor background_color,
+                      SkColor progress_color,
+                      float stroke_width,
+                      SkScalar start_angle) {
+  SkPath path;
+  path.addArc(bounds, start_angle, 60);
+  path.addArc(bounds, start_angle + 120, 60);
+  path.addArc(bounds, start_angle + 240, 60);
+  DrawRing(canvas, bounds, background_color, progress_color, stroke_width,
+           std::move(path));
+}
+
 }  // namespace views
diff --git a/ui/views/controls/progress_ring_utils.h b/ui/views/controls/progress_ring_utils.h
index 64336ad..cc51a5e 100644
--- a/ui/views/controls/progress_ring_utils.h
+++ b/ui/views/controls/progress_ring_utils.h
@@ -22,6 +22,16 @@
                                    SkScalar start_angle,
                                    SkScalar sweep_angle);
 
+// Helper function that draws a spinning ring on `canvas`. The spinning ring
+// consists a background full ring and three arches distributed evenly on the
+// ring. `start_angle` is used to indicate the start angle of the first arch.
+VIEWS_EXPORT void DrawSpinningRing(gfx::Canvas* canvas,
+                                   const SkRect& bounds,
+                                   SkColor background_color,
+                                   SkColor progress_color,
+                                   float stroke_width,
+                                   SkScalar start_angle);
+
 }  // namespace views
 
 #endif  // UI_VIEWS_CONTROLS_PROGRESS_RING_UTILS_H_
diff --git a/ui/views/examples/examples_main_proc.cc b/ui/views/examples/examples_main_proc.cc
index f2b77a6..13f1396 100644
--- a/ui/views/examples/examples_main_proc.cc
+++ b/ui/views/examples/examples_main_proc.cc
@@ -25,7 +25,6 @@
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "components/viz/common/features.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -91,14 +90,6 @@
   // window to not render. See http://crbug.com/936249.
   command_line->AppendSwitch(switches::kDisableDirectComposition);
 
-  // Disable skia renderer to use GL instead.
-  std::string disabled =
-      command_line->GetSwitchValueASCII(switches::kDisableFeatures);
-  if (!disabled.empty())
-    disabled += ",";
-  disabled += features::kUseSkiaRenderer.name;
-  command_line->AppendSwitchASCII(switches::kDisableFeatures, disabled);
-
   base::FeatureList::InitializeInstance(
       command_line->GetSwitchValueASCII(switches::kEnableFeatures),
       command_line->GetSwitchValueASCII(switches::kDisableFeatures));
@@ -122,7 +113,8 @@
 
   // The ContextFactory must exist before any Compositors are created.
   auto context_factories =
-      std::make_unique<ui::TestContextFactories>(under_test);
+      std::make_unique<ui::TestContextFactories>(under_test,
+                                                 /*output_to_window=*/true);
 
   base::i18n::InitializeICU();
 
@@ -143,9 +135,6 @@
   base::DiscardableMemoryAllocator::SetInstance(
       g_discardable_memory_allocator.Pointer());
 
-  base::PowerMonitor::Initialize(
-      std::make_unique<base::PowerMonitorDeviceSource>());
-
   gfx::InitializeFonts();
 
 #if defined(USE_AURA)